Redis 集群
Redis 集群将数据分散存储在多个节点上,每个节点存储一部分数据,从而实现数据的分布式存储和处理,突破了单个节点内存容量的限制,此时就需要考虑如何将数据分布在这些片区中
1. 数据分片算法
分片算法 |
描述 |
特点 |
哈希分片 |
通过对数据的某个键值进行哈希运算,将得到的哈希值映射到特定的分片上。通常使用取模运算将哈希值映射到固定数量的分片中 |
当需要增加或减少分片数量时,会导致大量数据的迁移 |
一致性哈希 |
将哈希值空间组织成一个虚拟的圆环,即哈希环。节点和数据都通过哈希函数映射到这个环上。当有数据需要存储时,从数据的哈希值位置开始,沿着环顺时针查找,找到的第一个节点就是该数据的存储节点。当增加或减少节点时,只会影响到该节点在环上的相邻节点,数据迁移量相对较小 |
数据分布的均匀性可能不如哈希分片 |
哈希槽 |
Redis 集群采用的一种数据分片方式,Redis 集群有固定的 16384 个哈希槽。对数据的键进行哈希计算(如使用 CRC16 算法),得到的结果对 16384 取模,从而确定该键对应的哈希槽编号。每个节点负责一部分哈希槽,集群中的节点记录着哈希槽与节点的映射关系 |
方便集群的扩展和收缩,增加或减少节点时,只需迁移部分哈希槽的数据,能保证数据在集群中的均匀分布;节点之间通过交换哈希槽信息来维护集群状态,协议相对简单高效 |
2. 分布式锁的简单实现
什么是分布式锁?
在分布式系统中,也会涉及到多个节点访问同一个公共资源的情况,和 Java 中多线程的锁不一样,由于分布式系统中涉及到多个进程多个主机,所以说 Java 中 synchronized 就不合适了。
思路:使用一个公共的服务器来记录加锁的状态
可以用过 Redis 来记录一个键值来表示锁的状态,当加锁的时候就设置该键值对,释放锁时就删除,不过此时就有了一个问题,如果说服务器 1 加锁之后宕机了,那么释放锁的操作就无法执行,其他服务器就不能获取锁,所以说可以通过set ex nx
的方式再设置一个过期时间,键值对的设置也是有讲究的,如果设置为一个简单的 “111”,那么其他服务器也能释放锁,此时可以引入一个校验 id,比如设置为服务器编号,通过判断是否是加锁的那台服务器来释放锁。
此时还有一个问题:校验 id 时需要通过 get 操作获取值,然后通过 del 操作删除锁,这样的操作并不是原子的,那么就会导致一台服务器加锁之后,其中的两个线程进行解锁操作,当线程 A 解锁完成后,线程 B 的 del 操作前的这段时间,服务器 2 加锁之后就被释放了
可以把释放锁的逻辑写在 Lua 脚本中执行,Lua 脚本在 Redis 中的执行是原子的
上述的方案中还存在一个问题:由于把 key 的过期时间设置为了一个定值,如果设置的过小,那么任务还没执行完,锁自动释放了,这就需要引入看门狗机制,watch dog 本质上是加锁的服务器上的一个单独的线程(不是 Redis 服务器的),通过这个线程来对锁过期时间进行续约,这样就不用担心提前失效的问题了,即使服务器宕机了,watch dog 线程也就没了,无法续约,到了一定时间自然就释放锁了
在实际中,Redis 一般是集群部署的,就可能出现下面这种情况:
服务器 1 向 master 节点进行加锁操作,设置 key 的过程刚刚完成,master 节点就宕机了,slave 节点升级为了新的 master 节点,但是原来主节点设置的锁并没有来得及同步过来,此时就相当于服务器 1 的锁白加了。
可以引入多组 Redis 节点,每组 Redis 节点都包含一个主节点和若干从节点,并且组和组之间的数据都是一致的,加锁的时候,按照一定的顺序,写多个 master 节点,并且设定操作的超时时间,如果超过这个时间还没有加锁成功,就认为该节点加锁失败了,根据少数服从多数的原则,如果超过一半的主节点都加锁失败,才认为本次加锁失败,解锁也是这样的操作。
- 点赞
- 收藏
- 关注作者
评论(0)