背景
团队内每周延安大学分享中,redis中讨论了一个AOF会阻塞主线程的问题,这个问题之前没考虑到过,一直以为fork了子进程一定不会阻塞,没有深度考虑。
因为大家也没有深入研究,这里整理一下,因为这块确实实践较少,可能有描述不准确的,大家多多提意见指正,我多多完善。
AOF
redis中文官网:http://www.redis.cn/topics/persistence.html
什么是AOF
以下定义均来自中文官网
只追加操作的文件(Append-only file,AOF)
快照功能(RDB)并不是非常耐久(dura ble): 如果 Redis 因为某些原因而造成故障停机, 那么服务器将丢失最近写入、且仍未保存到快照中的那些数据。 从 1.1 版本开始, Redis 增加了一种完全耐久的持久化方式: AOF 持久化。
你可以在配置文件中打开AOF方式:
appendonly yes
从现在开始, 每当 Redis 执行一个改变数据集的命令时(比如 SET), 这个命令就会被追加到 AOF 文件的末尾。这样的话, 当 Redis 重新启时, 程序就可以通过重新执行 AOF 文件中的命令来达到重建数据集的目的。
日志重写
因为 AOF 的运作方式是不断地将命令追加到文件的末尾, 所以随着写入命令的不断增加, AOF 文件的体积也会变得越来越大。举个例子, 如果你对一个计数器调用了 100 次 INCR , 那么仅仅是为了保存这个计数器的当前值, AOF 文件就需要使用 100 条记录(entry)。然而在实际上, 只使用一条 SET 命令已经足以保存计数器的当前值了, 其余 99 条记录实际上都是多余的。
为了处理这种情况, Redis 支持一种有趣的特性: 可以在不打断服务客户端的情况下, 对 AOF 文件进行重建(rebuild)。执行 BGREWRITEAOF 命令, Redis 将生成一个新的 AOF 文件, 这个文件包含重建当前数据集所需的最少命令。
Redis 2.2 需要自己手动执行 BGREWRITEAOF 命令; Redis 2.4 则可以自动触发 AOF 重写, 具体信息请查看 2.4 的示例配置文件。
通过官网基本可以了解了什么是AOF和重写,如果可以想了解AOF和RDB的区别,官网也有描述可以通过上面链接去阅读。
rdb:默认开启,文件小,格式紧凑,数据恢复快,比较适合用来做灾备,服务宕机丢数据更多一些
aof:默认关闭,文件大,数据恢复慢,但是数据更加完整,支持多种同步策略。
官方推荐使用混合模式。
AOF写入策略
由appendfsync参数控制:
可配置的值 | 说明 |
always | 命令写入buf后调用系统调用fsync同步AOF文件,fsync完成后线程返回。 |
no | 命令写入buf后调用系统调用write操作,后续fsync同步操作由操作系统来完成,一般为30秒一次。 |
everysec | 命令写入buf后调用系统调用write操作,后续fsync同步操作专门线程每一秒调用一次。 |
everysec是always和no的折中,是性能和安全性的这种,是redis默认的配置,也是比较推荐的配置。
重写流程图
重写流程:
- bgrewriteaof 触发重写,判断是否当前有bgsave或bgrewriteaof在运行,如果有,则等待该命令结束后再继续执行
- 主进程fork出子进程执行重写操作,保证主进程不会阻塞
- 子进程遍历redis内存中数据到临时文件,客户端的写请求同时写入aof_buf缓冲区和aof_rewrite_buf重写缓冲区 保证原AOF文件完整以及新AOF文件生成期间的新的数据修改动作不会丢失
- 1).子进程写完新的AOF文件后,向主进程发信号,父进程更新统计信息。2).主进程把aof_rewrite_buf中的数据写入到新的AOF文件
- 使用新的AOF文件覆盖旧的AOF文件,完成AOF重写
流程图来源:https://blog.csdn.net/wsdc0521/article/details/106765809
阻塞
虽然在everysec配置下aof的fsync是由子线程进行操作的,但是主线程会监控fsync的执行进度。
主线程在执行时候如果发现上一次的fsync操作还没有返回(也有一种对比上一次的fsync操作时间,大于2秒阻塞的),那么主线程就会阻塞。
解决阻塞
- 修改配置为yes,减少IO竞争:
no-appendfsync-on-rewrite yes/no
- 设置为yes,重写期间会停止
appendfsync
操作,避免io竞争((表示在日志重写时,不进行命令追加操作,而只是将命令放在重写缓冲区里,避免与命令的追加造成磁盘IO
上的冲突))。但是这样的风险:如果在aof重写期间redis宕机了,那么aof的数据便会丢失,可靠性下降。 - 配置就是设置为no时候,aof重写期间还是会执行
fsync
,这个时候就会产生IO竞争,有可能阻塞主线程。如果当前AOF文件很大,那么相应的rewrite时间会变长,appendfsync
被阻塞的时间也会更长
- 为了保证可靠性,进行集群化
- 给当前Redis实例添加slave节点,当前节点设置为master, 然后master节点关闭AOF,slave节点开启AOF。
- 这样的方式的风险是如果master挂掉,尚没有同步到salve的数据会丢失。而且 集群选举时,aof配置怎么根据选举结果动态修改修改,可能需要手动上去改一遍。感觉运维成本比较大。
- 闲的时间定时重写、提升服务器io性能
- 在凌晨低峰期定时手动执行
bgrewriteaof
命令完成每日一次的AOF
重写 - 比如使用ssd
- 将配置设为no
- 硬盘采用高速固态硬盘SSD
- 如果是三方服务器,购买私有或者用户隔离的混合云,不要购买共有云,不要让其他用户影响该服务器磁盘稳定性
- 上监控人工介入,监控所有系统必不可少的一部分。
- 在重写时为了避免硬盘空间不足或者
IO
使用率高影响重写功能,我们还添加了硬盘空间报警和IO
使用率报警保