2021-03-12
Redis总结
1. 最近主要版本和变更内容
1.1 Redis 4.0
Redis 4.x 只有 4.0.x 版本,没有 4.1 及其以上版本,变更主要功能:
- 缓存淘汰策略 新加 LFU 算法,相比之前算法,提高了性能和精确度 http://antirez.com/news/109
- 新加惰性删除特性,可以在后台使用不同的线程处理删除,而不阻塞服务。同时新加
unlink
非阻塞的方式删除(del 是阻塞的),flushdb
、flushall
新加async
参数,以异步的方式清空数据 - AOF 日志新加 RDB-AOF 混合格式,当开启 AOF 持久化当情况,默认使用混合格式
1.2 Redis 5.0
Redis 5.x 只有 5.0.x 版本,没有 5.1 及其以上版本,变更主要功能:
- 新加数据类型 Stream
1.3 Redis 6.0
- Acl 支持,可以让不同用户使用不同命令组,访问不同模式匹配的 keys
- 可以支持多线程、提高性能
2. 使用场景
- 计数器- redis 可以对 string 实现自增自减运算,前提是数字类型的 string 如 "1", "230" 等;
- 分布式ID生成,利用自增特性,且是原子操作,不同客户端不可能获取一样的值,也可以一次请求一个大的步长
incrby key 100
, 缓存在本地使用; - 数据统计,使用位图,存储是否参加过什么活动,答过哪些题,是否是vip等等;
- 分布式session缓存;
- 热点数据缓存;
- 排行榜;
- 分布式锁;
- 延迟队列;
3. 持久化
Redis 持久化有 RDB 快照 和 AOF 两者模式,两种可以同时开启。RDB 优点是速度快,缺点是可能丢失部分数据;AOF 优点是更安全、文件不像 rdb 是二进制文件,更易读和修改,缺点是性能比 RDB 低,文件比 RDB 大,恢复数据比 RDB 慢。
3.1 RDB 快照
RDB 是通过保存某一时刻的快照的方式,默认文件名称是 dump.rdb ,可以通过手动执行 bgsave
或者 save
触发,也可以通过如下配置自动触发,等同于执行 bgsave
命令
save 900 1
save 300 10
save 60 10000 // 60s 更新操作大于1万次,生成快照
save 是同步阻塞的,发送 save 之后,redis 服务会阻塞其他客户端的命令,直到 save 执行完成。
bgsave 是异步的方式,通过 fork 子进程用于持久化,不影响主进程的操作,fork 进程的动作是同步的,如果 fork 耗时太久也会阻塞其他客户端的执行,一般情况不会。fork 之后其他客户端可以正常读取数据,因为 fork 的子进程和 master 进程是有共享内存空间的,fork 之前的数据全部在共享内存中。
快照生成过程中是否停止服务?不会,继续提供服务。
写数据如何处理?此时写数据会触发 COW (Copy On Write),会复制一块新的内存空间,用于写操作
RDB 缺点: 自动触发 RDB 快照的间隔,如果设置太小,会频繁触发 fork 子进程的操作,降低服务性能,如果设置大,发生宕机的时候可能会丢失更多的数据
3.2 AOF 日志
AOF: Append Only File,默认不开启,appendonly no
和生成快照不同,AOF 是把所有的写命令以追加的方式记录到日志文件中,当机器重启后读取 AOF 日志文件,依次执行写命令来恢复数据。AOF 根据配置不同,按不同的频率写入每次会变更数据的命令
# 可选 always 、no,默认每秒一次
appendfsync everysec
但写入的日志会有一些重复的,比如连续修改同一个 key ,每次修改都会记录到 AOF 日志,所以 AOF 日志会比较大,可以通过重写 AOF 日志,去除对同一个 key 的多次操作,只保留最新的,触发重写事件可以显式调用 bgrewriteaof
命令,也可以依赖以下两个配置隐式触发。
# 1. 重写百分比
# 每次重写 redis 纪录 AOF 日志大小,如果自启动以来没有重写过,纪录当前 AOF 日志大小,
# 当 AOF 日志比上次纪录的日志大小,大于指定比百分比,就会触发重写
# 设置为 0 ,不会重写
auto-aof-rewrite-percentage 100
# 2. 触发重写的最小大小
# 防止文件不大,但也大于上次的百分比,设置最小重写大小
auto-aof-rewrite-min-size 64mb
Redis 4.0 后的 AOF 日志文件,支持 RDB-AOF 混合格式。重写时以 [RDB文件]+[AOF后缀]
的形式,可以显著减少 AOF 日志文件的大小,也可以加快数据恢复的速度,默认是开启的, aof-use-rdb-preamble yes
,但开启时,AOF 日志就会变成二进制格式。
🕹️ tips: RDB 快照和 AOF 重写都会通过 fork 子进程来完成,fork 进程会把主进程的分页表复制到子进程,所以 redis 占用内存越高,需要复制分页表数据越多,fork 越耗时,例如 redis 内存达到 16GB,需要复制的分页表大小为 16GB/4k = 4MB
4. 主从复制
如果机器的读压力大,可以通过主从复制的方式,部署 1 主 N 从的架构。
配置主从:
# 5.0 之后是 replicaof,之前是 slaveof
replicaof master_host master_port
masterauth pwd
4.1 重连后的全量复制问题
复制有 全量复制
和 增量复制
两种,第一次连接会全量复制,其他情况默认增量复制。但有个特殊情况,当主库发现从库断开连接后,断开连接的时间段内需要同步的数据,会保存在同步累计缓存日志 backlog
中,它是一个 buffer 缓存
,当从库重新连接后,只需要将 backlog
中的内容先发送给从库即可,无需全量同步,但如果 repl-backlog-size
设置过小,默认 1 m,断开连接后,写操作又很频繁,很快就会写满 1m 的累计缓存日志,再次重新连接后就导致重新全量同步
;
还有种情况是,客户端输出缓冲区大小设置,如默认的client-output-buffer-limit replica 256mb 64mb 60
,如果写流量很大,但是从库机器出现比如网络抖动等问题,导致同步太慢,缓存积累的数据超过限制,被主库主动断开了连接,再次连接后会再次触发全量同步
。
client-output-buffer-limit
配置表示如果设置限制,redis 服务端发送给客户端的数据,首先需要写入 buffer 缓存中,如果缓存达到限制条件,会主动断开与客户端的连接,有三条默认的配置如下
client-output-buffer-limit normal 0 0 0
# 注意 5.0 之后 replica 替换了之前的 slave
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
normal
表示正常客户端没有限制; replica
表示主从模式下,从库作为客户端,256m
表示缓存大小只要大于 256mb 就会断开,64mb 60
表示缓存大于 64mb 且持续 60s,就会断开连接,两个条件满足一个就会断开。
4.2 主从数据不一致问题
有种情况,当主从都设置了内存淘汰策略,且是作用于所有 keys 的策略,从库内存优先达到最大内存,从库会优先开始淘汰数据,这个时候就出现数据不一致现象。解决办法,从库最大可用内存设置的比主库稍微大写,淘汰策略优先选择作用于设置过期时间的 keys 上。
5. 高可用
5.1 哨兵
哨兵机制
是在主从复制的基础上,实现自动化的故障恢复。不是以哨兵模式
开启 redis 实例时,主库发生故障的时候,只能依靠手动选择一个可用的从库服务,修改其配置成为新的主库,而且需要手动修改代码中的连接配置信息。哨兵模式
就是在主库发生故障时,自动选举一个从库成为新的主库,代码中的 redis 配置信息,也需要修改成哨兵模式,具体流程是:把配置的 redis ip + port 改为哨兵实例的端口
→ 通过哨兵的接口获取当前的 master 地址
→ 连接 redis
,常用的客户端比如 phpredis
, jredis
都支持哨兵模式。
哨兵模式
的原理简单描述:通过哨兵
不断地给主库发送心跳包 (PING),检查是否存活,如果主库在指定时间内无法响应(默认 30s,可修改),且指定时间无法重启,就会选举新的从库成为主库,然后修改每个机器的配置。
# 30s 后主机仍然不能连接,认为主机下线
sentinel down-after-milliseconds mymaster 30000
# 3分钟内 主机仍然没有重启成功,重新选举
sentinel failover-timeout mymaster 180000
默认的 30s 和 3min 可以调低,减少主机宕机后不可用时间。线上可以改成 5s 和 10s。
5.2 哨兵模式搭建
最小三台 redis 机器: 1 master → 2 slave
master 192.168.66.110 6379
slave_1 192.168.66.111 6379
slave_2 192.168.66.201 6379
sentinel 192.168.66.110 26379
sentinel 192.168.66.111 26379
sentinel 192.168.66.201 26379
首先主从关系要配置好:
/etc/redis.conf
初始化修改 2 台从库机器的配置
replicaof 192.168.66.110 6379
如果有密码
masterauth pwd
然后修改哨兵模式的配置:
/etc/redis-sentinel.conf
初始化修改 3 台机器的配置 (监控主库的地址)
sentinel monitor mymaster 192.168.66.110 6379 2
如果有密码
requirepass pwd
分别开启三台机器的 sentinel 服务
systemctl start redis-sentinel
查看每台机器的配置 /etc/redis-sentinel.conf,再配置文件的最后几行会自动添加几行信息,如下:
sentinel known-replica mymaster 192.168.66.111 6379
sentinel known-replica mymaster 192.168.66.201 6379
sentinel known-sentinel mymaster 192.168.66.111 26379 4cfcc7ea407412760c5c59a3012edca2a9aeb169
sentinel known-sentinel mymaster 192.168.66.110 26379 780b8b84b7d49fb3f416b3449ac8feab6b29adb5
sentinel current-epoch 1
5.3 优缺点
优点:配置简单,基于主从同步,可以实现自动故障转移
缺点:不容易横向扩展,存储容量达到单机上线,不能扩展成分布式的集群服务。
5.4 其他解决方案
TODO
codis 等
6. 内存淘汰算法
redis 是内存KV数据库,默认不限制内存,依赖机器内存大小,可以通过 maxmemory
设置最大可用内存,当可使用内存达到 maxmemory
时,根据内存策略, maxmemory-policy
配置来决定如何淘汰内存,默认不淘汰 maxmemory-policy noeviction
,内存不可用时报错。
redis 包含多种淘汰策略,4.0 之前 6 种
(2个LRU+2个随机+ttl+不淘汰),4.0 之后 8 种
(2个LFU+2个LRU+2个随机+ttl+不淘汰)
- Volatile-lru,所有设置过期时间的,最近最少使用的先淘汰(Least Recently Used)是一种常用的页面置换算法,
- volatile-ttl,所有设置过期时间的,先淘汰将要过期的数据
- volatile-random,所有设置过期时间的,任意选择数据
- all_keys-lru,所有数据中,最近最少使用
- all_keys-random, 所有数据中,随机淘汰
- no-eviction, 不淘汰
- 4.0 之后新加 all_keys-LFU 和 volatile-LFU , LFU(最不经常使用)、LRU(最近最少使用)
如果所有的 key 访问频率大概相等,使用 all_keys-random 的淘汰策略;如果有的 key 访问频率高,有的访问频率低,使用 all_keys-LRU 或者 LFU 都可以
Redis 内存达到上限会发生什么?
写报错,读可以正常返回,或者使用淘汰策略,内存达到限制就会淘汰一定的数据
Redis 提高删除性能
在需要删除时,可以开启 lazyfree
特性(4.0之后支持,默认开启),开启后台线程删除,不影响前前台线程
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
lazyfree-lazy-user-del no
7. Redis 分布式锁
首先 setnx 抢锁,抢到之后,自用 expire 给锁加上过期时间,防止忘记锁无法释放,
8. Redis 优化维度
TODO
- 性能
- 可靠性
- 资源
- 运维
- 监控
- 安全
9. Redis 集群
TODO