Redis 持久化机制,主从复制,集群,缓存击穿,缓存穿透,缓存雪崩,分布式锁等内容整理。
目录
问题:setnx刚好获取到锁,业务逻辑出现异常,导致锁无法释放
RDB AOF
官方推荐两个持久化机制都启用。
如果对数据不敏感,可以选单独用RDB。
不建议单独用AOF,因为可能会出现Bug。
如果只是做纯内存缓存,可以都不用。
AOF和RDB同时开启,Redis听谁的?
AOF和RDB同时开启,系统默认取AOF的数据(数据不会存在丢失)
RDB ( Redis database )
在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里
备份是如何执行的
Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能 如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。
AOF( Append Only File )
以日志的形式来记录每个写操作(增量保存),将Redis执行过的所有写指令记录下来(读操作不记录)(类似于mysql中的redo log), 只许追加文件但不可以改写文件,Redis启动之初会读取该文件重新构建数据,换言之,Redis 重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作
AOF默认不开启
可以在Redis.conf中配置文件名称,默认为 appendonly.aof
AOF文件的保存路径,同RDB的路径一致。
Redis主从复制
主机数据更新后根据配置和策略,自动同步到备机的master/slaver机制,Master以写为主,Slave以读为主
主从复制原理
l Slave启动成功连接到master后会发送一个sync命令
l Master接到命令启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令, 在后台进程执行完毕之后,master将传送整个数据文件到slave,以完成一次全量复制
l 全量复制:而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。
l 增量复制:Master继续将新的所有收集到的修改命令依次传给slave,完成同步
l 但是只要是重新连接master,一次完全同步(全量复制)将被自动执行
主从复制优缺点
摘自:Redis集群的 3 种方式,各自优缺点分析! – 知乎
优点
- 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离
- Slave同样可以接受其它Slaves的连接和同步请求,这样可以有效的分载Master的同步压力。
- Master Server是以非阻塞的方式为Slaves提供服务。所以在Master-Slave同步期间,客户端仍然可以提交查询或修改请求。
- Slave Server同样是以非阻塞的方式完成数据同步。在同步期间,如果有客户端提交查询请求,Redis则返回同步之前的数据
缺点
- Redis不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。
- 主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。
- Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。
哨兵模式
主从复制 设置哨兵,如果主机down掉那么从机上位。
Redis-server.exe sentinel.conf –sentinel
能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库
哨兵模式的优缺点
优点
- 哨兵模式是基于主从模式的,所有主从的优点,哨兵模式都具有。
- 主从可以自动切换,系统更健壮,可用性更高。
缺点
- Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。
Redis集群
redis的哨兵模式基本已经可以实现高可用,读写分离 ,但是在这种模式下每台redis服务器都存储相同的数据,很浪费内存,所以在redis3.0上加入了cluster模式,实现的redis的分布式存储,也就是说每台redis节点上存储不同的内容。
Redis 集群实现了对Redis的水平扩容,即启动N个Redis节点,将整个数据库分布存储在这N个节点中,每个节点存储总数据的1/N。
Redis 集群通过分区(partition)来提供一定程度的可用性(availability):即使集群中有一部分节点失效或者无法进行通讯,集群也可以继续处理命令请求。
优点
扩容,容灾,分摊压力
缺点
方案没有被目前的公司广泛部署,因为大都采用 代理模式
缓存穿透
我们使用Redis大部分情况都是通过Key查询对应的值,假如发送的请求传进来的key是不存在Redis中的,那么就查不到缓存,查不到缓存就会去数据库查询。
穿过Redis 直接访问数据库
原因
1.黑客攻击
2.访问不存在的url
如何防止
空值缓存
如果一个查询返回的数据为空(不管是数据是否不存在),我们仍然把这个空结果(null)进行缓存,设置空结果的过期时间会很短,最长不超过五分钟。
假如传进来的这个不存在的Key值每次都是随机的,那存进Redis空值也没有意义。
布隆过滤器
把有效的key经过k次hash的bitmap置位1
来了一个url 先进行 k次hash 如果有一次是0 说明它不是有效的 直接返回
这样的话,如果全都是1,就访问redis ,当然也会有一定的误判率。
缓存击穿
原因
一个热门key过期导致穿过Redis 直接访问数据库
key对应的数据存在,但在Redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
和缓存穿透的区别是 这个key在数据库中有 Redis中也有 但是过期了
解决方法
(1)预先设置热门数据:在Redis高峰访问之前,把一些热门数据提前存入到Redis里面,加大这些热门数据key的时长
(2)实时调整:现场监控哪些数据热门,实时调整key的过期时长
(3)使用锁:
1、上面说过了,如果业务允许的话,对于热点的key可以设置永不过期的key。
2、使用互斥锁。如果缓存失效的情况,只有拿到锁才可以查询数据库,降低了在同一时刻打在数据库上的请求,防止数据库打死。当然这样会导致系统的性能变差。
缓存雪崩
原因
大量key同时过期,造成数据库瘫痪。
为什么会出现这个问题呢,有几种可能,第一种可能是Redis宕机,第二种可能是采用了相同的过期时间。
解决措施
(1) 构建多级缓存架构:nginx缓存 + Redis缓存 +其他缓存(ehcache等)
(2) 使用锁或队列:
用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。不适用高并发情况
(3) 设置过期标志更新缓存:
记录缓存数据是否过期(设置提前量),如果过期会触发通知另外的线程在后台去更新实际key的缓存。
(4) 将缓存失效时间分散开:
比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。
分布式锁
跨JVM进行锁的相关操作,适用于数据库集群
好文:一文搞懂分布式锁及其业务场景_chunqiu3351的博客-CSDN博客
为啥用
因为单机上锁其他java api也能识别,但是集群就不行了,
所以需要实现分布式锁 让一台服务器上的锁 在别的服务器上也能识别到。
具体有几种方法
zookeeper
Redis实现
数据库实现
性能比较高的是Redis
1. 性能:Redis最高
2. 可靠性:zookeeper最高
如何实现和优化
Redis如何实现呢?
set lock uuid nx ex 12
设置lock 的值为uuid nx 表示为不允许再设置
ex 表示过期时间 12s
在java中也能实现
比如在一台机子上 运行时 一个线程 在Redis中设置了 lock
其他机子如果想要执行程序 但是无法拿到这个锁
set 成功就会返回1 不成功就会返回0
问题:setnx刚好获取到锁,业务逻辑出现异常,导致锁无法释放
解决:设置过期时间,自动释放锁。
所以必须设置过期时间 否则别的线程无法拿到锁了
问题:可能会释放其他服务器的锁。
一台服务器做完需要同步的操作后就可以 del 锁了 让别的服务器去用
所须在删除锁之前先进行判断
通过 uuid进行锁的标识,看是不是自己的锁
如果是自己的锁 那么删除 别人在set lock 为自己的uuid
不是自己的 说明 已经过了过期时间 自动释放
别的线程已经拿到了,那么就不用管了
问题:删除操作缺乏原子性。
在判断是自己的uuid 后准备删除 这时候正好过期,被另一个线程拿到了这把锁。
也会误删 所以需要使得判断和删除时原子性的
使用lua脚本保证删除操作的原子性。
为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:
– 互斥性。在任意时刻,只有一个客户端能持有锁。
– 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。
– 解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。
– 加锁和解锁必须具有原子性。
最后这块很可能存在一种异常场景:
实例 a 抢占到分布式锁,设置 value 及过期时长
此时该 key-value 保存在 Redis master 节点,突然 master 节点挂了
Redis 集群从 slave 节点中推选出新的 master
由于异步同步的原因,新的 master 此时不包含该分布式锁 key-value
实例 b 抢占到分布式锁,设置 value 及过期时长
此时实例 a 和 实例 b 都抢占到 Redis 锁,就有可能造成同步问题
实际该问题的本质是 Redis 异步同步可能丢数据的问题,只是用到分布式锁这个模块比较重要,影响范围更大。此时一般有两种解决方式:
RedLock 获取分布式锁
Zookeeper 实现分布式锁
这里简单介绍下 RedLock 的逻辑:
获取当前时间,以毫秒为单位
客户端依次从5个毫无关系的 Redis 实例请求分布式锁
通过当前时间减去步骤1的时间计算获取锁的时长,当且仅当 (N / 2 + 1)个 Redis 实例获取锁成功并且获取锁的时间小于锁失效的时间才算获取锁成功
此时如果获取锁成功,锁真正有效时间等于初始有效时间(根据步骤1时间 + 过期时长算出的时间)减去真正获取到锁的时间。如果获取锁失败执行解锁逻辑
总的来说 RedLock 实施难度太大,成本高是一方面,各个 Redis 实例时间还得对其,还得防止不出现重启等情况。一般很少使用,了解即可。
如果Redis内存满了的话会发生什么?
Redis所在服务器内存满了可能导致访问变慢,更严重的会直接宕机。
如果不设置最大内存大小或者设置最大内存大小为0,在64位操作系统下不限制内存大小,在32位操作系统下最多使用3GB内存。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/92867.html