前言
本来最近打算学习Unix网络编程,但是项目中项目中用到了Redis Cluster,自己对Redis集群这方面并不是很熟悉,所以打算花点时间来系统的学习一下Redis,Redis主要分四个部分;
1.数据结构与对象
2.单机数据库实现
3.多机数据库实现
4.独立功能的实现
本文先简单介绍下Redis 的集群实现,后续会针对上面的在进行详解;
Redis 集群介绍
Redis因为具有丰富的数据结构和超高额性能以及简单的协议,使其能够很好的作用为数据库的上游。但是当数据量变大的时候(如数据达到千万级别时),会受限以多个地方,单机内存有限、单点问题、动态扩容问题等。
为了解决上面的问题,Redis的集群方案就显得比较重要了。使用Redis集群通常有三个途径;
- 官方提供的 Redis Cluster
- 通过Proxy分片
- 客户端分片(Smart Client)
本文主要介绍一下官方提供的 Redis Cluster,Redis Cluster 是在Redis 3.0开始支持的,3.0主要是更新了多机方面的功能,在5.0之前Redis Cluster 主要是采用 Redis提供的 redis-trib.rb(Ruby实现的脚本,需要安装相应的依赖环境)这个管理工具。5.0版本开始支持 –cluster 参数来进行管理。
Redis Cluster 数据分片
Redis Cluster 并没有使用一致性hash,而是引用了一个叫哈希槽的概念。
Redis Cluster 中有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽。集群的每个节点分配一部分的哈希槽,这样有一点很方便就是在增加和移除的时候,只需要分配相应的哈希槽就可以了。
在单命令执行下和单机Redis并无区别,客户端写入命令,由Redis Cluster计算当前key属于哪个槽点然后返回相应数据。但是多命令的情况下(如求集合的交集)有可能多个key不在同一个槽点里,可以采用{}来设置需要多命令执行的key。{key}:xxx它只对括号里面的进行hash操作,所以可以保证在同一个哈希槽中。
Redis Cluster 端口
Redis Cluster 的每个节点都需要维护两个TCP端口,用于为Redis提供服务的普通端口,例如6370,加上通过向数据端口添加10000获得的端口,如16379。
第二个端口是用于集群总线,使用二进制节点到节点的通信通道(gossip 协议)。总线端口的偏移量是固定的,始终为10000。节点使用的集群总线主要用来进行故障检测、数据更新、故障转移授权等 。为了保证集群的正常使用,客户端永不应该尝试与集群总线通讯,始终使用正常的端口通讯。防火墙要打开这两个端口,否则Redis节点将无法通讯。
Redis Cluster 故障恢复问题
为了提高可用性,Redis 采用了主从模型,其中每一个节点拥有一个master N个slave,如果其中一个节点的 master出现问题,它的一个slave会被选举为master节点。如果某个节点的master和slave全部挂掉,那么这个Redis Cluster将无法服务。其中还有一点需要注意,选举的时候需要有其它一半以上的master节点参加。如果没有一半以上的master节点存活参与选举则slave不会被选举为master。如两个master服务,其中一个挂掉,挂掉服务的slave是不会被选举成为master的,因为存活的master不到一半以上。或者是master刚好分配在一台服务器上,这个服务器挂掉,即使他的slave存在,集群也是没办法将slave节点选举为master节点做到自动故障转移的。所以我们在分配节点的时候一定要注意合理分配,最好自己分配(默认自动分配)。
Redis Cluster 强一致性
首先我们要知道Redis Cluster 并不提供强一致性。这意味着在某种条件下,Redis 会造成数据丢失。主要原因是因为Redis Cluster 采用了异步复制:
- 客户端向主机写入
- 主机向客户端回复确认
- 主机通知该节点的slave设备
基于这种情况,如果出现了上面的故障问题,主机在崩溃后没来得及通知其slave服务,并且这个slave被选为master,那么这个数据则会永久丢失。
Redis Cluster 也支持同步复制,通过WAIT命令实现,这样会大大的降低数据丢失的可能性。但即使是同步复制,也不可能保证强一致性。在更复杂的情况下总是可以实现失败场景,无法接收写入的slave被选为master。
还有另一个值得注意的情况是,Redis Cluster将丢失写入,这种情况发生在网络分区中 。客户端向节点请求是有最大时间限制(cluster-node-timeout)
不推荐用redis事务机制,因为采用了分片处理。如果一个事物中涉及到多个key的操作的话,这么多个key不一定都存储在同一个节点上。
Redis 分布式锁
如果确实需要强一致性的话可以考虑采用分布式锁: