Redis学习(二):Redis 持久化
程序员文章站
2022-05-20 21:09:51
...
目录
1. Redis持久化的取舍和选择
1. 什么是持久化
- redis所有数据保持在内存中,对数据的更新将异步地保存到磁盘上。
2. 持久化方式
-
快照
- MySQL Dump
- Redis RDB
-
写日志
- MySQL Binlog
- Hbase HLog
- Redis AOF
2. RDB
1. 什么是RDB
2. 触发机制 - 主要三种方式
1. save(同步)
- 客户端向redis发送一条save命令,redis会帮我们生成RDB文件。
- 问题:由于是同步命令,执行save的时候,假如我们的save非常慢(数据量多),会造成redis的阻塞。
- 文件策略:如存在老的RDB文件,新替代老。
- 复杂度:O(N)。
2. bgsave(异步)
- 客户端执行bgsave命令,它使用了linux的fork()函数,生成了主进程的一个redis子进程,让子进程完成RDB的生成。RDB生成之后,会告诉主进程RDB生成成功。
- 如果fork执行非常慢(大多数情况下非常快),依然会阻塞redis。
- redis会正常响应客户端,因为createRDB这样重的操作是让子进程进行操作的。
- 文件策略和复杂度与save相同。
命令 | save | bgsave |
---|---|---|
IO类型 | 同步 | 异步 |
阻塞 | 是 | 是(阻塞发生在fork) |
复杂度 | O(N) | O(N) |
优点 | 不会消耗额外内存 | 不阻塞客户端命令 |
缺点 | 阻塞客户端命令 | 需要fork,消耗内存 |
3. 自动
- redis提供了save配置来进行自动持久化
配置 | seconds | changes |
---|---|---|
save | 900 | 1 |
save | 300 | 10 |
save | 60 | 10000 |
- 在60秒改变了10000条数据,在300秒改变了10条数据,在900秒改变了一条数据,进行自动持久化。RDB文件生成是内部调用bgsave。
- 具体配置可以修改。
- 无法控制生成RDB频率。
4. 配置
- 我们来看一下redis默认配置文件中给出的配置
- save:自动持久化
- dbfilename:RDB名字
- dir:RDB等日志文件存放位置
- stop-writes-on-bgsave-error:bgsave发生错误时是否停止写入
- rdbcompression:RDB文件是否采用压缩的格式
- rdbchecksum:是否对RDB文件进行校验和
save 900 1
save 300 10
save 60 10000
dbfilename dump.rdb
dir ./
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
5. 最佳配置
- 关闭自动持久化
- dbfilename:使用端口号进行文件区别
- dir:根据redis数量进行分盘
dbfilename dump-${port}.rdb
dir /bigdistpath
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
3. 触发机制 - 不容忽略方式
1. 全量复制
2. debug reload
3. shutdown
4. 试验
- 修改配置文件以便远程连接
- bind 0.0.0.0
- protected-mode no
- 使用jedis随便添加些数据
127.0.0.1:6379> dbsize
(integer) 1000000
127.0.0.1:6379> info memory
# Memory
used_memory:105221280
used_memory_human:100.35M
- 我们提前在另一个窗口准备好执行命令
127.0.0.1:6379> set hello word
OK
127.0.0.1:6379> get hello
- 在第一个窗口执行save,接着执行get hello,发现被阻塞
127.0.0.1:6379> save
OK
(4.77s)
127.0.0.1:6379> get hello
"word"
(2.22s)
- 我们还可以去data目录(配置文件指定)下看到生成的RDB文件
[aaa@qq.com redis]# cd data/
[aaa@qq.com data]# ll
总用量 52428
-rw-r--r--. 1 root root 7365 12月 21 14:29 6379.log
-rw-r--r--. 1 root root 4809 12月 20 23:40 6382.log
-rw-r--r--. 1 root root 53666783 12月 21 14:29 dump-6379.rdb
- 接下来我们验证一下bgsive
127.0.0.1:6379> bgsave
Background saving started
- 使用命令发现调用子进程,同时get hello没有阻塞
[aaa@qq.com redis]# ps -ef | grep redis-
root 4987 1 1 14:21 ? 00:00:15 redis-server 0.0.0.0:6379
root 5271 3826 0 14:33 pts/0 00:00:00 redis-cli
root 5304 4987 56 14:36 ? 00:00:01 redis-rdb-bgsave 0.0.0.0:6379
root 5306 5143 0 14:36 pts/2 00:00:00 grep --color=auto redis-
- 最后我们来看一下自动持久化策略
#save 900 1
#save 300 10
save 60 5
- 可以通过flushall清空数据
127.0.0.1:6379> flushall
OK
(0.70s)
127.0.0.1:6379> dbsize
(integer) 0
- 插入数据
127.0.0.1:6379> set a b
OK
127.0.0.1:6379> set c d
OK
127.0.0.1:6379> set e f
OK
127.0.0.1:6379> set g h
OK
127.0.0.1:6379> set i j
OK
127.0.0.1:6379> set m n
OK
- 查看日志,发现进行了自动持久化
[aaa@qq.com data]# tail -f 6379.log
5475:M 21 Dec 14:45:03.075 * 5 changes in 60 seconds. Saving...
5475:M 21 Dec 14:45:03.077 * Background saving started by pid 5494
5494:C 21 Dec 14:45:05.018 * DB saved on disk
5494:C 21 Dec 14:45:05.019 * RDB: 10 MB of memory used by copy-on-write
5475:M 21 Dec 14:45:05.073 * Background saving terminated with success
5. 总结
- RDB是Redis内存到硬盘的快照,用于持久化。
- sava通常会阻塞Redis。
- bgsava不会阻塞Redis,但是会fork新进程。
- save自动配置满足任一就会被执行。
- 有些触发机制不能忽视。
3. AOF
1. RDB有什么问题
1. 耗时,耗性能
- RDB生成过程其实就是将内存中的数据dump到硬盘当中,形成一个RDB文件。
- 首先是比较耗时的,我们要将所有数据进行一个dump,是一个O(N)的过程,本身的写也会消耗很多CPU。
- 其次是一个内存的消耗,我们知道bgsive有一个fork的过程。
- 还有就是IO性能的消耗。
2. 不可控,丢失数据
配置 | 动作 |
---|---|
T1 | 执行多个写命令 |
T2 | 满足RDB自动创建的条件 |
T3 | 再次执行多个写命令 |
T4 | 宕机 |
- T3到T4之间的数据写入就会丢失
2. AOF运行原理,创建
- 客户端写一条数据,就在日志追加一条写命令
3. AOF的三种策略
-
always
- redis在执行写命令的时候,实际上不是直接写在文件系统当中,而是写在硬盘的缓冲区当中。缓冲区会根据一些策略刷新到硬盘当中。
- always是说每条命令fsync到硬盘,这样redis的写入数据就会不丢失。
-
everysec
- everysec是说每秒把缓冲区fsync到硬盘。
- 在高写入量时会适当保护硬盘。
- 缺点是如果出现了故障,有可能会丢失一秒的数据。
- 这是一个默认值。
-
no
- OS决定fsync,由操作系统决定什么时候该写入硬盘。
命令 | always | everysec | no |
---|---|---|---|
优点 | 不丢失数据 | 每秒一次fsync | 不用管 |
缺点 | IO开销较大,一般的sata盘只有几百TPS | 丢一秒数据 | 不可控 |
4. AOF重写
- set hello world set hello java set hello hehe => set hello hehe
- incr counter incr counter => set counter 2
- rpush mylist a rpush mylist b rpush mylist c => rpush mylist a b c
- 过期数据 => 重写过程是没有用的
把一些可以优化的命令进行化简,从而达到两个目的
- 减少硬盘占用量
- 加速恢复速度
5. AOF重写实现的两种方式
1. bgrewriteaof
- 客户端向redis发送一条命令bgrewriteaof,redis会返回OK并异步执行。redis接收到这个命令后会fork出一个子进程来完成AOF的重写。
- 这里的AOF重写就是将redis内存中的数据进行一次回溯,回溯成AOF文件,是从redis内存中进行一个重写。
2. AOF重写配置
- 配置
配置名 | 含义 |
---|---|
auto-aof-rewrite-min-size | AOF文件重写需要的尺寸 |
auto-aof-rewrite-percentage | AOF文件增长率 |
- 统计
统计名 | 含义 |
---|---|
aof-current-size | AOF当前尺寸(单位:字节) |
aof-base-size | AOF上次启动和重写的尺寸(单位:字节) |
- 自动触发时机
- aof-current-size > auto-aof-rewrite-min-size
- (aof-current-size - aof-base-size) / aof-base-size > auto-aof-rewrite-percentage
6. AOF配置
- 我们修改一下redis默认配置文件中给出的配置
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
dir /home/redis/data
no-appendfsync-on-rewrite yes
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
- 进行一些简单操作
127.0.0.1:6379> set hello world
OK
127.0.0.1:6379> set hello java
OK
127.0.0.1:6379> set hello redis
OK
127.0.0.1:6379> incr counter
(integer) 1
127.0.0.1:6379> incr counter
(integer) 2
127.0.0.1:6379> rpush list a
(integer) 1
127.0.0.1:6379> rpush list b
(integer) 2
127.0.0.1:6379> rpush list c
(integer) 3
- 查看AOF文件是否生成
[aaa@qq.com redis]# cd data
[aaa@qq.com data]# ll
总用量 40
-rw-r--r--. 1 root root 21736 12月 21 16:57 6379.log
-rw-r--r--. 1 root root 4809 12月 20 23:40 6382.log
-rw-r--r--. 1 root root 277 12月 21 16:58 appendonly.aof
-rw-r--r--. 1 root root 127 12月 21 15:22 dump-6379.rdb
- 查看文件内容
[aaa@qq.com data]# more appendonly.aof
*3
$3
set
$5
hello
$5
world
-
我们稍微看一下文件格式
- *3表示下面这个命令有3个参数
- $3表示下面一个数据的字节长度
-
下面我们来看下重写
127.0.0.1:6379> bgrewriteaof
Background append only file rewriting started
- 再次打开appendonly.aof,我们发现只保留了set hello redis。
*3
$3
SET
$5
hello
$5
redis
7. RDB和AOF的抉择
1. RDB和AOF比较
命令 | RDB | AOF |
---|---|---|
启动优先级 | 低 | 高 |
体积 | 小 | 大 |
恢复速度 | 快 | 慢 |
数据安全性 | 丢数据 | 根据策略决定 |
轻重 | 重 | 轻 |
2. RDB最佳策略
- “关”
- 集中管理
- 主从,从开?
3. AOF最佳策略
- “开”:缓存和存储
- AOF重写集中管理
- everysec
4. 最佳策略
- 小分片
- 缓存或者存储
- 监控(硬盘,内存,负载,网络)
- 足够的内存
4. 开发运维常见问题
1. fork操作
1. 同步操作
2. 与内存量息息相关:内存越大,耗时越长(与机器类型有关)
3. info:latest_fork_usec
4. 改善fork
- 优先使用物理机或者高效支持fork操作的虚拟化技术。
- 控制redis实例最大可用内存:maxmemory。
- 合理配置linux内存分配策略:vm.overcommit_memory=1。
- 降低fork频率:例如放宽AOF重写自动触发时机,不必要的全量复制。
2. 子进程开销和优化
1. CPU
- 开销:RDB和AOF文件生成,属于CPU密集型。
- 优化:不做CPU绑定,不和CPU密集型部署。
2. 内存
- 开销:fork内存开销,copy-on-write。
- 优化:禁止支持大的内存页分配 : echo never > /sys/kernel/mm/transparent_hugepage/enabled。
3. 硬盘
- 开销:AOF和RDB文件写入,可以结合iostat,iotop分析。
- 优化:
- 不要和高硬盘负载服务部署在一起:存储服务,消息队列等。
- 不要进行AOF追加:no-appendfsync-on-rewrite = yes。
- 根据写入量决定磁盘类型:例如ssd。
- 单机多实例持久化文件目录可以考虑分盘。
3. AOF追加阻塞
- 如果使用了AOF,我们通常会使用每秒刷盘的这样一个策略。
- 首先主线程去写入AOF缓冲区,同时还有一个AOF同步线程去同步每秒刷盘的操作,同时还会记录最近的一次同步时间。
- 主线程还会对比和上次AOF的时间,如果距离上次同步时间超过两秒,主线程会阻塞。
- 解决方法主要参考上面关于硬盘的优化。
最后
大家可以关注我的微信公众号一起学习进步。
下一篇: Matlab实现真彩RGB图直方图计算