Redis知识点

1.Redis的过期策略有哪些?大量key集中过期导致卡顿如何解决?

如果我们对key设置了失效时间1分钟,1分钟后,Redis 是如何对这个 key 进行删除的呢?

Redis过期策略采用的是惰性删除+定期删除策略。

惰性删除
当某个key被设置了过期时间之后,客户端每次对该key的访问(读写)都会事先检测该key是否过期,如果过期就直接删除。

定期删除
当某个key被设置了过期时间之后,客户端每次对该key的访问(读写)都会事先检测该key是否过期,如果过期就直接删除(这种是被动删除);但有一些键只访问一次或者是冷数据,因此需要主动删除,默认情况下Redis每秒检测10次,检测的对象是所有设置了过期时间的键集合,每次从这个集合中随机检测20个键查看他们是否过期,如果过期就直接删除,如果过期的key比例超过1/4,那就把上述操作重复一次(贪心算法)。同时为了保证过期扫描不会出现循环过度,导致线程卡死现象,算法还增加了扫描时间的上限,默认不会超过25ms。

如果一个大型的 Redis 实例中所有的 key 在同一时间过期了,会引发什么问题?

这个主动过期 key 的定时任务,是在 Redis 主线程中执行的,也就是说如果在执行主动过期的过程中,出现了需要大量删除过期 key 的情况,那么此时应用程序在访问 Redis 时,必须要等待这个过期任务执行结束,Redis 才可以服务这个客户端请求。此时就会出现,应用访问 Redis 延时变大。

有小伙伴问,扫描不是有 25ms 的时间上限了么,怎么会导致卡顿呢?

假如有 1001 个客户端同时将请求发过来了,然后前 1000 个请求的执行时间都是 25ms,那么第 1001 个指令需要等待多久才能执行?25000ms,25秒,这个就是客户端的卡顿时间,是由服务器不间断的小卡顿积少成多导致的。

大量key集中过期导致卡顿如何解决?

方案一:在设置 key 的过期时间时,增加一个随机时间

redis.expireat(key, expire_time + random(300))
1
这样一来,Redis 在处理过期时,不会因为集中删除过多的 key 导致压力过大,从而避免阻塞主线程。

方案二:Redis 4.0 以上版本,开启 lazy-free 机制

lazyfree-lazy-expire yes
1
另外,除了业务层面的优化和修改配置之外,你还可以通过运维手段及时发现这种情况。

运维层面,你需要把 Redis 的各项运行状态数据监控起来,在 Redis 上执行 INFO 命令就可以拿到这个实例所有的运行状态数据。

在这里我们需要重点关注 expired_keys 这一项,它代表整个实例到目前为止,累计删除过期 key 的数量。

你需要把这个指标监控起来,当这个指标在很短时间内出现了突增,需要及时报警出来,然后与业务应用报慢的时间点进行对比分析,确认时间是否一致,如果一致,则可以确认确实是因为集中过期 key 导致的延迟变大。

从库的过期策略:从库不会进行过期扫描,从库对过期的处理是被动的。主库在 key 到期时,会在 AOF 文件里增加一条 del 指令,同步到所有的从库,从库通过执行这条 del 指令来删除过期的 key。

2.Redis内存淘汰策略有哪些?他们有什么区别?

当实际内存超出 maxmemory 时,Redis 提供了6种可选策略 (maxmemory-policy) 来让用户自己决定该如何腾出新的空间以继续提供读写服务。

noeviction:不会继续服务写请求 (DEL 请求可以继续服务),读请求可以继续进行。这样可以保证不会丢失数据,但是会让线上的业务不能持续进行。这是默认的淘汰策略。
volatile-lru:尝试淘汰设置了过期时间的 key,通过LRU算法驱逐最近最少使用的key。没有设置过期时间的 key 不会被淘汰,这样可以保证需要持久化的数据不会突然丢失。
volatile-random:尝试淘汰设置了过期时间的 key,在设置了过期时间的key集合中随机选择数据淘汰。
volatile-ttl:尝试淘汰设置了过期时间的 key,在设置了过期时间的key集合中优先淘汰ttl小的。
allkeys-lru:和volatile-lru的区别在于要淘汰的key对象是全体key集合而不只是设置了过期时间的key,其他都一样。
allkeys-random:和volatile-random的区别在于要淘汰的key对象是全体key集合而不只是设置了过期时间的key,其他都一样。
Redis4.0后新增了两个策略:

volatile-lfu:尝试淘汰设置了过期时间的 key,通过LFU算法驱逐使用频率最少的key。没有设置过期时间的 key 不会被淘汰。

allkeys-lfu:和volatile-lfu的区别在于要淘汰的key对象是全体key集合而不只是设置了过期时间的key,其他都一样。
 

3.持久化方式有哪些?有什么区别?


Redis 的数据全部在内存里,如果突然宕机,数据就会全部丢失,因此必须有一种机制来保证 Redis 的数据不会因为故障而丢失,这种机制就是 Redis 的持久化机制。

什么是持久化?
就是把内存里的数据保存到硬盘上。

必须使用数据持久化吗?
Redis的数据持久化机制是可以关闭的。如果你只把Redis作为缓存服务使用,Redis中存储的所有数据都不是该数据的主体而仅仅是同步过来的备份,那么可以关闭Redis的数据持久化机制。

但通常来说,仍然建议至少开启RDB方式的数据持久化,因为:

①数据量不是非常大时,RDB方式的持久化几乎不损耗Redis本身的性能,因为Redis父进程持久化时只需要fork一个子进程,这个子进程可以共享主进程的所有内存数据,子进程会去读取主进程的内存数据,并把它们写入RDB文件。

②Redis无论因为什么原因发送故障,重启时能够自动恢复到上一次RDB快照中记录的数据(自动加载RDB文件)。这省去了手工从其他数据源(如数据库)同步数据的过程,而且要比其他任何的数据恢复方式都要快

③服务器的硬盘都是T级别的,几个G的数据影响忽略不计

Redis 不同于 Memcached 的很重要一点就是,Redis 支持持久化,而且支持三种不同的持久化策略。

RDB
Redis提供了两个命令来生成 RDB 文件:

save:在主进程中执行,会导致写请求阻塞。
bgsave:fork一个子进程,专门用于写入 RDB 文件,避免了主进程的阻塞。
为了快照而阻塞写请求,这是系统无法接受的,因此Redis借助操作系统提供的写时复制技术(Copy-On-Write, COW),在执行快照的同时,正常处理写操作。

Redis在执行持久化时,会fork出一个bgsave子进程,这个子进程可以共享主进程的所有内存数据,bgsave子进程运行后,会去读取主进程的内存数据,并把它们写入RDB文件。

有小伙伴问,为什么要fork一个子线程?

redis是单线程程序,若单线程同时在服务线上的请求还需要进行文件IO操作,这不仅影响性能而且还会阻塞线上业务,因此这里主进程fork出一个进程,fork出的这个进程去完成快照操作。

快照持久化是 Redis 默认采用的持久化方式,我们可以根据业务需求配置下面参数:

save 900 1    #每900秒(15分钟)至少有1个key发生变化,Redis就会自动触发BGSAVE命令创建快照。

save 300 10   #每300秒(5分钟)至少有10个key发生变化,Redis就会自动触发BGSAVE命令创建快照。

save 60 10000 #每60秒(1分钟)至少有10000个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
1
2
3
4
5
key发生变化(key数据添加、修改、删除)

触发快照的几种方式:

①服务器正常关闭时,会照一次快照 ./bin/redis-cli shutdown

②key满足一定条件,会照一次快照(通过上述Redis.conf配置)

③通过BGSAVE命令(在redis中执行)手动触发RDB快照保存

优点:

①RDB文件紧凑,体积小,网络传输快,适合全量复制

②与AOF方式相比,通过RDB文件恢复数据比较快更快

③RDB最大化了Redis的性能,因为Redis父进程持久化时只需要fork一个子进程,这个子进程可以共享主进程的所有内存数据,子进程会去读取主进程的内存数据,并把它们写入RDB文件。

缺点:

①快照是定期生成的,所有在 Redis 故障时或多或少会丢失一部分数据

②当数据量比较大时,fork 的过程是非常耗时的,fork 子进程时是会阻塞的,在这期间 Redis 是不能响应客户端的请求的。

AOF
Redis会把每一个写请求都记录在一个日志文件里,在Redis重启时,会把AOF文件中记录的所有写操作顺序执行一遍,确保数据恢复到最新。

Redis 会在收到客户端修改指令后,先进行参数校验,如果没问题,就立即将该指令文本存储到 AOF 日志中,也就是先存到磁盘,然后再执行指令。这样即使遇到突发宕机,已经存储到 AOF 日志的指令进行重放一下就可以恢复到宕机前的状态。

日志文件太大怎么办?

AOF 日志在长期的运行过程中会变的很大,Redis重启时需要加载 AOF 日志进行指令重放,此时这个过程就会非常耗时。 所以需要定期进行AOF 重写,给 AOF 日志进行瘦身。

AOF如何重写?

Redis 提供了 bgrewriteaof 指令用于对 AOF 日志进行瘦身。每次执行重写时,主进程 fork 出一个bgrewriteaof 子进程,会把主进程的内存拷贝一份给 bgrewriteaof 子进程,对内存进行遍历转换成一系列 Redis 的操作指令,序列化到一个新的 AOF 日志文件中。序列化完毕后再将操作期间发生的增量 AOF 日志追加到这个新的 AOF 日志文件中,追加完毕后就立即替代旧的 AOF 日志文件了,瘦身工作就完成了。

Redis提供了AOF rewrite功能,可以重写AOF文件,只保留能够把数据恢复到最新状态的最小写操作集。

AOF 重写可以通过bgrewriteaof命令(在redis里执行)触发,也可以配置Redis定期自动进行:

## Redis在每次AOF rewrite时,会记录完成rewrite后的AOF日志大小,当AOF日志大小在该基础上增长了100%后,自动进行AOF rewrite。同时如果增长的大小没有达到64mb,则不会进行rewrite。
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
1
2
3
AOF默认是关闭的,如果需要开启,需要在redis.conf配置文件中配置

appendonly  yes
1
AOF提供三种fsync配置,always/everysec/no,通过配置项appendfsync指定,默认是everysec。

appendfsync always    # 每写入一条日志就进行一次fsync操作,数据安全性最高,但速度最慢(每次有数据修改发生时都会写入AOF文)
appendfsync everysec  # 折中的做法,交由后台线程每秒fsync一次(每秒钟同步一次,该策略为AOF的缺省策略)
appendfsync no        # 不进行fsync,将flush文件的时机交给OS决定,速度最快(从不同步。高效但是数据不会被持久化)
1
2
3
优点:

①数据安全性高,可以根据业务需求配置fsync策略

②AOF文件易读,可修改,在进行了某些错误的数据清除操作后,只要AOF文件没有rewrite,就可以把AOF文件备份出来,把错误命令删除,然后恢复数据

缺点:

①AOF方式生成的日志文件太大,即使通过AFO重写,文件体积仍然很大

②数据恢复速度比RDB慢

混合持久化
如果我们采用 RDB 持久化会丢失一段时间数据。如果我们采用 AOF 持久化,AOF日志较大,重放比较慢。

Redis 4.0 为了解决这个问题,支持混合持久化。将 RDB 文件的内容和增量的 AOF 日志文件存在一起。

混合持久化同样也是通过 bgrewriteaof 完成的,不同的是当开启混合持久化时,fork出的子进程先将共享的内存副本全量的以 RDB 方式写入 AOF 文件,然后在将重写缓冲区的增量命令以 AOF 方式写入到文件,写入完成后通知主进程更新统计信息,并将新的含有RDB格式和 AOF 格式的 AOF 文件替换旧的的 AOF 文件。简单的说:新的AOF文件前半段是RDB格式的全量数据后半段是AOF格式的增量数据。

于是在 Redis 重启的时候,可以先加载 rdb 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重启效率因此大幅得到提升。

实战经验
快照需要fork子进程的方式进行的,它是一个比较耗资源的操作。(当数据量非常大时,fork会很耗时,需要大概几百毫秒甚至1秒,fork时父进程是阻塞的,不能正常服务redis读写请求)

AOF 的 fsync 是一个耗时的 IO 操作,它会降低 Redis 性能,同时也会增加系统 IO 负担

所以通常 Redis 的主节点是不会进行持久化操作,持久化操作主要在从节点进行。从节点是备份节点,没有来自客户端请求的压力,它的操作系统资源往往比较充沛。

但是如果出现网络分区,从节点长期连不上主节点,就会出现数据不一致的问题,特别是在网络分区出现的情况下又不小心主节点宕机了,那么数据就会丢失,所以在生产环境要做好实时监控工作,保证网络畅通或者能快速修复。另外还应该再增加一个从节点以降低网络分区的概率,只要有一个从节点数据同步正常,数据也就不会轻易丢失。
 

3.redis主从不一致?

1.如果能接受,可以忽略。

2.强制读主   

(1)使用一个高可用主库提供数据库服务

(2)读和写都落到主库上

(3)采用缓存来提升系统读性能

这是很常见的微服务架构,可以避免数据库主从一致性问题。

总结

数据库主库和从库不一致,常见有这么几种优化方案:

(1)业务可以接受,系统不优化

(2)强制读主,高可用主库,用缓存提高读性能

(3)在cache里记录哪些记录发生过写请求,来路由读主还是读从

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值