阅读 58

Redis学习(二):Redis 持久化

1. Redis持久化的取舍和选择

1. 什么是持久化

  • redis所有数据保持在内存中,对数据的更新将异步地保存到磁盘上。

2. 持久化方式

  • 快照
    • MySQL Dump
    • Redis RDB
  • 写日志
    • MySQL Binlog
    • Hbase HLog
    • Redis AOF

2. RDB

1. 什么是RDB

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文件
[root@localhost redis]# cd data/
[root@localhost 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没有阻塞
[root@localhost 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
复制代码
  • 查看日志,发现进行了自动持久化
[root@localhost 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运行原理,创建

  • 客户端写一条数据,就在日志追加一条写命令

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文件是否生成
[root@localhost redis]# cd data
[root@localhost 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
复制代码
  • 查看文件内容
[root@localhost 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的时间,如果距离上次同步时间超过两秒,主线程会阻塞。
  • 解决方法主要参考上面关于硬盘的优化。

最后

大家可以关注我的微信公众号一起学习进步。

关注下面的标签,发现更多相似文章
评论