redis灵魂拷问:聊一聊AOF日志重写
“ AOF日志重写到底会不会阻塞主线程?”
01
—
AOF介绍
redis的AOF日志,是redis持久化的一种方式,它是一种write after log,即先执行命令后记录日志。这样的好处是日志不会记录执行失败的命令,同时记录日志不会阻塞当前命令执行。
记录AOF是在主线程中执行的,所以也会阻塞主线程。这个跟AOF的写回策略有关,这个配置项参数叫appendfsync,在redis.conf文件中,默认值是everysec。下面是3种写回策略的比较:
写回策略 |
策略说明 |
优点 |
缺点 |
---|---|---|---|
always |
执行命令同步写盘 |
基本不丢失命令 |
性能损耗大 |
everysec |
每秒写一次盘 |
比always性能损耗小 |
可能丢失1秒内命令 |
no |
操作系统控制写盘 |
性能损耗最新 |
可能会丢失很多命令 |
这样,我们就需要在性能和可靠性之间做一些取舍了。
当redis上执行的命令越来越多,AOF日志文件会变得很大,这样AOF文件追加命令会很慢,而且操作系统对文件大小也有一定的限制,再者如果使用AOF做主从同步或数据恢复的话,因为命令记录太多会导致耗时很长。redis解决这个问题的手段就是AOF日志重写。
02
—
AOF重写介绍
在redis.conf文件中,有下面2个参数来控制AOF重写:
auto-aof-rewrite-percentage 100 #AOF文件大小较上次重写超过100%时进行重写
auto-aof-rewrite-min-size 64mb #aof文件大小超过64m时重写
下面我们执行6条命令:
192.168.59.146:6379> set name jinjnzhu
OK
192.168.59.146:6379> set password 123456
OK
192.168.59.146:6379> set name jinjunzhu1
OK
192.168.59.146:6379> set password 1234567
OK
192.168.59.146:6379> set name jinjunzhu2
OK
192.168.59.146:6379> set password 12345678
OK
之后我们查看name和password的值:
192.168.59.146:6379> get name
"jinjunzhu2"
192.168.59.146:6379> get password
"12345678"
这2个key的值被赋予了最新的一次赋值,虽然我们执行了6条命令,但是AOF重写后日志文件就剩了最后2条命令。
03
—
AOF重写对性能的影响
在上小节的介绍中,如果AOF文件较上次重写超过了100%,就要进行重写。但是如果日志特别大,AOF重写后把日志写回磁盘也是一个非常耗时的操作,那么AOF重写是否会阻塞主线程呢?
- AOF重写并不是在主线程中,而是redis会fork一个bgrewriteaof子进程,这样就不会阻塞主线程执行了。
- fork子进程的过程是要在主线程中执行的,这时候主线程需要拷贝内存页表,这个页表记录了虚拟内存和物理内存的映射关系,如果内存很大,拷贝过程花费的时间就会很大,而这个拷贝过程中主线程是阻塞的。
- fork子进程完成后,主线程和bgrewriteaof子进程使用的是同一块儿内存空间,这时如果有新的写请求到来,并且写命令是已经存在的key,主线程会使用CopyOnWrite的方式,为这个key申请新的内存空间,进行写操作。如果这个key是一个bigkey,那也会耗时很多。
下面我画了一个简单的图,主线程fork出bgrewriteaof子进程时,复制了一个页表给子进程用,跟自己指向相同的内存空间。但是AOF重写过程中收到了foo这个key的写命令,这时主线程需要拷贝一份数据到新的内存空间进行修改。
在AOF重写的过程中,如果有新的写命令到来了,会影响AOF重写吗?
当然不会,新的写命令不仅会记录到AOF日志的缓存区,还会记录到重写的新AOF日志缓存区,这样当AOF重写结束后,把重新缓存区数据写到新AOF文件,就不会丢失了。
- Caliburn.Micro学习笔记(五)----协同IResult
- 一个Pythoner的自我修养系列(一)
- 众里寻她千百度,蓦然回首,那bug却在灯火阑珊处
- Github|Python开源项目漫游指南(一)
- Caliburn.Micro学习笔记(四)----IHandle<T>实现多语言功能
- .NET 4 System.Threading.CountdownEvent
- java与ruby的直观比较
- 每天一个Linux命令:chmod
- SparkSql 中外连接查询中的谓词下推规则
- Caliburn.Micro学习笔记(三)----事件聚合IEventAggregator和 Ihandle<T>
- Python进行数据可视化分析快速教程实例
- 一个抓取豆瓣图书的开源爬虫的详细步骤
- Java 8 Stream 教程 (三)
- silverlight ListBox 多列图片效果
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Mycat 整合 MySQL 8.x 踩坑实践
- Python 技术篇-xlwt库不新建,直接读取已存在的excel并写入
- Python3 列表
- Mycat 核心配置详解
- Python字符串
- 初识 HBase
- Number对象
- Windows 技术篇-设置计划任务,每天自动关机
- Mycat 快速入门
- Python 技术篇-连接qq邮箱服务器,调用qq邮箱发送邮件实战演示,qq邮箱授权码开通方法
- 浅谈数据库集群方案
- SkyWalking - 实现微服务监控告警
- Actuator + Prometheus + Grafana搭建微服务监控平台
- Python 用smtplib库发邮件报错:[WinError 10061] 由于目标计算机积极拒绝,无法连接。解决办法
- python运算符