Varobj

2021-03-12

Redis总结



1. 最近主要版本和变更内容

1.1 Redis 4.0

Redis 4.x 只有 4.0.x 版本,没有 4.1 及其以上版本,变更主要功能:

1.2 Redis 5.0

Redis 5.x 只有 5.0.x 版本,没有 5.1 及其以上版本,变更主要功能:

1.3 Redis 6.0

2. 使用场景


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+不淘汰)

  1. Volatile-lru,所有设置过期时间的,最近最少使用的先淘汰(Least Recently Used)是一种常用的页面置换算法,
  2. volatile-ttl,所有设置过期时间的,先淘汰将要过期的数据
  3. volatile-random,所有设置过期时间的,任意选择数据
  4. all_keys-lru,所有数据中,最近最少使用
  5. all_keys-random, 所有数据中,随机淘汰
  6. no-eviction, 不淘汰
  7. 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