Redis部署方式
Redis有下面四种部署方式模式优点缺点单机版架构简单,部署方便机器故障、容量瓶颈、QPS瓶颈主从复制高可靠性,读写分离故障恢复复杂,主库的写跟存受单机限制Sentinel 哨兵集群部署简单,HA原理繁琐,slave存在资源浪费,不能解决读写分离问题Redis Cluster数据动态存储solt,可扩展,高可用客户端动态感知后端变更,批量操作支持查redis主从复制该模式下 具有高可用性且读写分离
Redis有下面四种部署方式
模式 | 优点 | 缺点 |
---|---|---|
单机版 | 架构简单,部署方便 | 机器故障、容量瓶颈、QPS瓶颈 |
主从复制 | 高可靠性,读写分离 | 故障恢复复杂,主库的写跟存受单机限制 |
Sentinel 哨兵 | 集群部署简单,HA | 原理繁琐,slave存在资源浪费,不能解决读写分离问题 |
Redis Cluster | 数据动态存储solt,可扩展,高可用 | 客户端动态感知后端变更,批量操作支持查 |
redis主从复制
该模式下 具有高可用性且读写分离, 会采用 增量同步
跟 全量同步
两种机制。
Redis全量复制一般发生在Slave初始化阶段,这时Slave需要将Master上的所有数据都复制一份
1、slave连接master,发送
psync
命令。2、master接收到
psync
命名后,开始执行bgsave命令生成RDB文件并使用缓冲区记录此后执行的所有写命令。3、master发送快照文件到slave,并在发送期间继续记录被执行的写命令。4、slave收到快照文件后丢弃所有旧数据,载入收到的快照。
5、master快照发送完毕后开始向slave发送缓冲区中的写命令。
6、slave完成对快照的载入,开始接收命令请求,并执行来自master缓冲区的写命令。
增量同步也叫指令同步,。Redis会把指令存放在一个环形队列当中,因为内存容量有限,如果备机一直起不来,不可能把所有的内存都去存指令,也就是说,如果备机一直未同步,指令可能会被覆盖掉。
Redis增量复制是指Slave初始化后开始正常工作时master发生的写操作同步到slave的过程。增量复制的过程主要是master每执行一个写命令就会向slave发送相同的写命令。
1、主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步
。当然,如果有需要,slave 在任何时候都可以发起全量同步。redis 策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步。2、slave在同步master数据时候如果slave丢失连接不用怕,slave在重新连接之后丢失重补
。
3、一般通过主从来实现读写分离,但是如果master挂掉后如何保证Redis的 HA呢?引入Sentinel
进行master的选
哨兵模式
Redis-sentinel 本身是一个独立运行的进程,一般sentinel集群 节点数至少三个且奇数个,它能监控多个master-slave集群,sentinel节点发现master宕机后能进行自动切换。Sentinel可以监视任意多个主服务器以及主服务器属下的从服务器,并在被监视的主服务器下线时,自动执行故障转移操作。这里需注意sentinel
也有single-point-of-failure
问题。大致罗列下哨兵用途:
集群监控:循环监控master跟slave节点。
消息通知:当它发现有redis实例有故障的话,就会发送消息给管理员
故障转移:这里分为主观下线(单独一个哨兵发现master故障了)。客观下线(多个哨兵进行抉择发现达到quorum数时候开始进行切换)。
配置中心:如果发生了故障转移,它会通知将master的新地址写在配置中心告诉客户端。
存在的问题
就是会有脑裂的情况,就是当一台主节点的机子因为某些原因无法进行连接了,哨兵以为它挂了,就重新选举出一台新的机子作为主节点,但后续之前那台机子又恢复过来了,这样整个集群就有了两个主节点了,就好像大脑分裂了一样。
解决方案也很简单:就是将之前的主节点降级为从节点就行了
Redis Cluster
RedisCluster是Redis的分布式解决方案,在3.0版本后推出的方案,有效地解决了Redis分布式的需求。说白了就是把数据分布在不同的节点上,采用去中心化的思想
分区规则如下
有下面这几个分区规则
节点取余
:hash(key) % N一致性哈希
:一致性哈希环虚拟槽哈希
:CRC16[key] & 16383
总结
- RedisCluster采用了
虚拟槽分区
方式 - 采用去中心化的思想,它使用虚拟槽solt分区覆盖到所有节点上,取数据一样的流程,节点之间使用轻量协议通信Gossip来减少带宽占用所以性能很高
- 自动实现负载均衡与高可用,自动实现failover并且支持动态扩展,官方已经玩到可以1000个节点 实现的复杂度低。
- 每个Master也需要配置主从,并且内部也是采用哨兵模式,如果有半数节点发现某个异常节点会共同决定更改异常节点的状态。
- 如果集群中的master没有slave节点,则master挂掉后整个集群就会进入fail状态,因为集群的slot映射不完整。如果集群超过半数以上的master挂掉,集群都会进入fail状态。
- 官方推荐 集群部署至少要3台以上的master节点。
Redis限流
在开发高并发系统时,有三把利器用来保护系统:缓存
、降级
和限流
。目前主要有下面这几种限流的算法
基于Redis的setnx、zset
setnx
比如我们需要在10秒内限定20个请求,那么我们在setnx的时候可以设置过期时间10,当请求的setnx数量达到20时候即达到了限流效果。
缺点:比如当统计1-10秒的时候,无法统计2-11秒之内,如果需要统计N秒内的M个请求,那么我们的Redis中需要保持N个key等等问题。
zset
其实限流涉及的最主要的就是滑动窗口,上面也提到1-10怎么变成2-11。其实也就是起始值和末端值都各+1即可。我们可以将请求打造成一个zset数组,当每一次请求进来的时候,value保持唯一,可以用UUID生成,而score可以用当前时间戳表示,因为score我们可以用来计算当前时间戳之内有多少的请求数量。而zset数据结构也提供了range方法让我们可以很轻易的获取到2个时间戳内有多少请求,
缺点:就是zset的数据结构会越来越大。
漏桶算法
把水比作是请求,漏桶比作是系统处理能力极限,水先进入到漏桶里,漏桶里的水按一定速率流出,当流出的速率小于流入的速率时,由于漏桶容量有限,后续进入的水直接溢出(拒绝请求),以此实现限流。
令牌桶算法
令牌桶算法的原理:可以理解成医院的挂号看病,只有拿到号以后才可以进行诊病。
一些细节
- 所有的请求在处理之前都需要拿到一个可用的令牌才会被处理。
- 根据限流大小,设置按照一定的速率往桶里添加令牌。
- 设置桶最大可容纳值,当桶满时新添加的令牌就被丢弃或者拒绝。
- 请求达到后首先要获取令牌桶中的令牌,拿着令牌才可以进行其他的业务逻辑,处理完业务逻辑之后,将令牌直接删除。
- 令牌桶有最低限额,当桶中的令牌达到最低限额的时候,请求处理完之后将不会删除令牌,以此保证足够的限流
分布式锁
在日常情况下在进程或线程中我们可以使用 synchronized 、Lock 实现并发编程,但如何在分布式集群下使用呢?可以使用 Redisson , Zookeeper ,Redis本身
Zookeeper实现分布式锁
1、持久节点:客户端断开连接zk不删除persistent类型节点
2、临时节点:客户端断开连接zk删除ephemeral类型节点
3、顺序节点:节点后面会自动生成类似0000001的数字表示顺序
4、节点变化的通知:客户端注册了监听节点变化的时候,会调用回调方法
缺点
频繁的创建删除节点,加上注册watch事件,对于zookeeper集群的压力比较大,性能也比不上Redis实现的分布式锁。
Redis实现分布式锁
本身原理也比较简单,Redis 自身就是一个单线程处理器,具备互斥的特性,通过setNX,exist等命令就可以完成简单的分布式锁,处理好超时释放锁的逻辑即可。
-
SETNX
SETNX 是SET if Not eXists的简写,日常指令是
SETNX key value
,如果 key 不存在则set成功返回 1,如果这个key已经存在了返回0。 -
SETEX
SETEX key seconds value 表达的意思是 将值 value 关联到 key ,并将 key 的生存时间设为多少秒。如果 key 已经存在,setex命令将覆写旧值。并且 setex是一个
原子性
(atomic)操作。 -
加锁
一般就是用一个标识唯一性的字符串比如UUID 配合 SETNX 实现加锁。
-
解锁
这里用到了LUA脚本,LUA可以保证是原子性的,思路就是判断一下Key和入参是否相等,是的话就删除,返回成功1,0就是失败。
缺点
这个锁是无法重入的,且自己实心的话各种边边角角都要考虑到,所以了解个大致思路流程即可,工程化还是用开源工具包就行。
存在的问题
普通利用Redis实现分布式锁的时候,我们可能会为某个锁指定某个key,当线程获取锁并执行完业务逻辑代码的时候,将该锁对应的key删除掉来释放锁。lock->set(key),成功->执行业务,业务执行完毕->unlock->del(key)。
第一个问题,因为我们的业务不知道要执行多久才能结束,所以这个key我们一般不会设置过期时间。这样如果在执行业务的过程中,业务机器宕机,unlock操作不会执行,所以这个锁不会被释放,其他机器拿不到锁,从而形成了死锁。
第二个问题,如果Redis宕机,三种情况:
①Redis是单点模式
②Redis是集群模式,master在获取到一把锁之后(写操作成功后),在没来得及把该锁同步到slave之前就宕掉,这个时候slave没有锁,这把锁失效了……
③Redis是集群模式,而整个集群都宕机,那么就没救了……
Redisson(1)分布式锁——如何解决死锁问题_xxcupid的博客-CSDN博客_redisson分布式锁缺陷解决(opens new window)
Redisson实现分布式锁
Redisson 是在Redis基础上的一个服务,采用了基于NIO的Netty框架,不仅能作为Redis底层驱动客户端,还能将原生的RedisHash,List,Set,String,Geo,HyperLogLog等数据结构封装为Java里大家最熟悉的映射(Map),列表(List),集(Set),通用对象桶(Object Bucket),地理空间对象桶(Geospatial Bucket),基数估计算法(HyperLogLog)等结构。
加锁解锁的流程如下
redis 分布式锁的 5个坑,真是又大又深 - 程序员内点事 - 博客园 (cnblogs.com)(opens new window)
这里应该会问你Redisson解决了啥问题
- 可以实现可重入加锁机制
- 在一个分布式环境下,假如一个线程获得锁后,突然服务器宕机了,那么这个时候在一定时间后这个锁会自动释放,你也可以设置锁的有效时间(不设置默认30秒),这样的目的主要是防止死锁的发生。
更多推荐
所有评论(0)