一. Redis集群cluster
Redis cluster主要是为了保证Redis的高可用而设计的,仅仅使用主从复制来说。只能保证master不可用后,通过哨兵自动将salve切换为主机。但是在切换的过程中,Redis是不可工作的。但是不能保证写入操作的高可用,或者说通过负载均衡完成写入操作。不能够支持海量数据的存储。
此时就需要搭建Redis的集群,能够实现海量数据的存储,并且达到写入操作的负载均衡。达到完善的高可用Redis
1. Redis cluster模式图以及工作原理
工作原理:将多个主从复制的哨兵模式组合成一起,形成Redis集群模式,将Redis集群分为多个内存槽块(将数据进行分片存储),存储数据时,根据CRC16算法,得到key的hash值。对Redis集群的所有槽位取模后,按照槽位区间进行存储。
单个哨兵组的槽位区间可以不是连续的,但是必须要保证所有的槽位加起来等于总槽位,否则会导致数据丢失无法存储的问题。
2. 搭建Redis Cluster
cluster帮助文档:./redis-cli --cluster help
- 修改配置文件,将Redis的集群模式开启 REDIS CLUSTER
cluster-enabled yes :表示开启集群,否则会以单实例启动
cluster-config-file nodes-6379.conf 集群配置文件:该文件列出了群集中其他节点,它们的状态,持久变量
cluster-node-timeout 15000 集群超时时间(毫秒)
cluster-require-full-coverage yes 默认为yes,表示只有所有哈希槽有主节点管理的时候,集群才可以接受查询
cluster-migration-barrier 1
cluster-replica-validity-factor 10
- 配置9台Redis实例,无需设置主从复制模式(三个master,每个master对一个两个slave),启动实例
- 创建集群(将这9台实例创建成为一个集群),为cluster分配槽位,并且设置主从模式:
#create host1:port1 ... hostN:portN
--cluster-replicas <arg>
./redis-cli --cluster create 192.168.101.1:7000 192.168.101.1:7000 192.168.101.1:7000 192.168.101.1:7000 --cluster-replicas 1
这条指令的意思是:将这些IP地址的Redis实例组成一个集群。并且设置主从复制。复制因子是1(代表每个master有1个slave),例如一共6台服务器,则每个master有一个salve,就一共有三组哨兵。
- 创建集群时,要保证Redis服务的端口是开启的,并且16379总线端口也要开启
- 此时Redis的启动需要添加 -c
- 查看集群节点信息cluster nodes
此时,Redis的集群就搭建好了。
3. Redis集群的扩容
当Redis需要横向扩容时,就需要对Redis集群进行扩容
add-node new_host:new_port existing_host:existing_port
--cluster-slave
--cluster-master-id <arg>
- 创建两个Redis实例,配置文件开启cluster,并且启动。
- 通过cluster的命令,在任意一台的Redis-cli执行均可:
./redis-cli --cluster add-node 192.168.101.10:7000 192.168.101.1:7000
该指令的意思是:将192.168.101.10:7000的Redis实例添加到.1(集群中的任意一个节点)的集群中,默认是添加了master节点。
如果要为该master节点,添加slave集群节点的话:需要指定该master节点的ID
./redis-cli --cluster add-node 192.168.101.10:7000 192.168.101.1:7000 --cluster-slave --cluster-master-id asdf4asf2345dfg3t3
此时,集群中这两个节点就被加入了。但是还没有分配槽位,不能使用
- 分配槽位
reshard host:port
为我们新添加的master分配槽位:
./redis-cli --cluster reshard 192.168.101.10:7000
执行后,按照脚本指令指定分配多少槽位、给哪个节点ID分配槽位、从哪些节点拿出槽位进行分配(all:将其他节点平均分配给新节点,或者输入需要拿出槽位的多个master节点ID,DONE执行),即可完成分配。同时,迁移过去的槽位的数据同样也会被迁移过去。
4. Redis的缩容
当Redis不需要这么多集群时,可以将某一组哨兵下线。先释放槽位,然后下线Redis实例。
- 释放槽位:
reshard host:port 集群环境中的任意一个节点地址
--cluster-from <arg> 需要释放的master节点ID
--cluster-to <arg> 需要迁移到那个master节点ID
--cluster-slots <arg> 需要释放多少槽位
./redis-cli --cluster reshard 192.168.101.10:7000 --cluster-from c12f3345565ac345dfq34qasdfq3 --cluster-to 2834anf345kasdf34nifasd --cluster-slots 2000
- 删除Redis实例节点
del-node host:port node_id
./redis-cli --cluster del-node 192.168.101.10:7000 asdfaw34aifqi34inrmas
指向集群环境中的任意一个节点地址,指定删除的node——id即可完成删除。
此时,Redis集群完成缩容
二. Redis的分布式锁
1. 通过SETNX实现分布式锁
通过setNX实现分布式锁是可以的, 但是需要考虑的东西是比较多的,例如:
- 锁是否是可重入的(controller获取了一次锁,调用service时,还需要再次获取一次锁)
- 锁是否不被释放,导致死锁
- 在锁可重入的情况下,是否存在提前释放锁的问题(service释放了锁,但controller还有一些业务逻辑要处理)
- 设置的锁失效时间,是否一定会保证大于业务逻辑的执行时间
- 当某个线程占有锁时,其他线程在进行自旋获取锁(死循环时)的时候,如何将CPU资源释放出来实现阻塞还要保证时效性呢
2. redisson分布式锁解决方案
Redisson的实现思路:
某个线程获取锁成功后,会执行后台线程一直检查锁是否还持有,持有的话会把超时时间延长三分之一。
其他线程在获取锁失败时,会while循环自旋一直等待获取锁。