# 6.2 AOF持久化机制实战

## 6.2.1 AOF配置文件说明

在默认情况下,Redis服务器是不会开启AOF持久化机制的,如果需要开启,可以在`redis.conf`等配置文件里通过修改`appendonly`参数值为`yes`来开启.如果要关闭基于AOF的持久化功能,将该参数值设置为`no`即可.

```
appendonly yes
```

用`appendonly`参数开启AOF持久化后,通过`appendfsync`参数可以设置持久化策略,该参数有3个取值:`always`、`everysec`、`no`

* `appendfsync`参数:设置持久化策略
  * `always`:每次发生Redis的写命令时都会触发持久化动作,这样可能会影响到Redis甚至是Redis所在服务器的性能
  * `everysec`:以一秒的频率触发持久化动作,在这种方式下能很好地平衡持久化需求和性能间的关系,一般情况下取这个值
  * `no`:会由操作系统来决定持久化的频率,这种方式对其他另外两种而言性能最好,但可能每次持久化操作间的间隔有些长,这样当故障发生时可能会丢失较多的数据
* `dir`参数:设置持久化文件所在路径
* `appendfilename`:设置持久化文件的文件名.默认为`appendonly.aof`
* `aof-load-truncated`:定义AOF文件的加载策略.具体表现为:在AOF持久化文件损坏的前提下,启动Redis时是否会加载,默认取值`yes`

随着持久化数据的增多,对应的AOF文件会越来越大,这可能会影响到性能.对此,Redis提供了AOF文件重写功能.具体而言,Redis能创建新的AOF文件来替代现有的AOF文件,在数据恢复时,这两个文件的效果是相同的,但新文件不会包含冗余命令,所以文件大小会比原来的小

可以通过如下三个参数来定义重写时的策略:

* `no-appendfsync-on-rewrite`:该参数用于平衡性能和安全性.如果该参数取值为`yes`,那么在重写AOF文件时能提升性能,但可能在重写AOF文件时丢失数据;如果取值为`no`,则不会丢失数据,但相比于取值为`yes`时的性能可能会降低.这个参数的默认取值是`no`
* `auto-aof-rewrite-percentage`:该选项指定了AOF文件增长的百分比.当当前AOF文件的大小相对于上一次AOF重写后的大小增长了该参数指定指定的百分比时,Redis将触发一个自动重写操作.前提是AOF文件的大小也超过了`auto-aof-rewrite-min-size`指定的值.默认该选项的值为`100`,这意味着当当前AOF文件的大小是上次重写后大小的两倍(即增长了100%)时,Redis 会考虑进行自动重写.但还需要满足AOF文件大小超过`auto-aof-rewrite-min-size`指定的阈值
* `auto-aof-rewrite-min-size`:该选项指定了AOF文件的最小大小.只有当AOF文件的大小超过这个值时,Redis才会考虑进行AOF重写.这是为了防止在AOF文件还很小的时候频繁地进行重写.该选项默认值为`64mb`,即只有当AOF文件的大小超过64MB时,Redis才会考虑进行自动重写.需要注意的是,仅当AOF文件大小超过这个值时,Redis才会考虑进行重写.但是否真的执行重写还取决于其他条件,例如`auto-aof-rewrite-percentage`选项

注意:`auto-aof-rewrite-percentage`和`auto-aof-rewrite-min-size`这两个参数是逻辑且关系,即只有同时满足这2个条件时才会触发重写操作.

此外,可以通过`BGREWRITEAOF`命令来手动触发针对AOF持久化文件的重写操作

## 6.2.2 实践AOF持久化

* step1. 编写配置文件

```
(base) root@yuanhong chpater6 % cat redis.conf
# 启用AOF持久化机制
appendonly yes

# 持久化频率为每秒持久化1次
appendfsync everysec

# 持久化文件的存储路径
dir /StudyRedisBaseOnDocker/conf/chpater6

# 持久化文件名称
appendfilename "my_appendonly.aof"
```

* step2. 根据配置文件启动redis-server

```
(base) root@yuanhong chpater6 % redis-server redis.conf 
```

注:此处是直接在宿主机上启动的redis-server,而非在容器中

* step3. 使用客户端连接后执行一些读写命令

```
(base) root@yuanhong ~ % redis-cli 
127.0.0.1:6379> GET name
(nil)
127.0.0.1:6379> SET name "Peter"
OK
127.0.0.1:6379> SET age 18
OK
```

* step4. 查看AOF持久化文件的内容

```
(base) root@yuanhong chpater6 % cat my_appendonly.aof
*2
$6
SELECT
$1
0
*3
$3
SET
$4
name
$5
Peter
*3
$3
SET
$3
age
$2
18
```

逐条命令分析含义:

```
*2
$6
SELECT
$1
0
```

这部分内容对应命令`SELECT 0`.其中:

* `*2`:表示接下来的命令有2个参数
* `$6`:表示接下来的参数长度为6字节
* `SELECT`:表示第一个参数,即命令名`SELECT`
* `$1`:表示接下来的参数长度为1字节
* `0`:表示第二个参数.该参数的含义为要选择的数据库编号,即`0`

```
*3
$3
SET
$4
name
$5
Peter
```

这部分内容对应命令`SET name "Peter"`

* `*3`:表示接下来的命令有3个参数
* `$3`:表示接下来的参数长度为3字节
* `SET`:表示第1个参数,即命令`SET`
* `$4`:表示接下来的参数长度为4字节
* `name`:表示第2个参数,即键名`name`
* `$5`:表示接下来的参数长度为5字节
* `Peter`:表示第3个参数,即键`name`对应的值`Peter`

```
*3
$3
SET
$3
age
$2
18
```

这部分内容对应命令:`SET age 18`

* `*3`:表示接下来的命令有3个参数
* `$3`:表示接下来的参数长度为3字节
* `SET`:表示第1个参数,即命令`SET`
* `$3`:表示接下来的参数长度为3字节
* `age`:表示第2个参数,即键名`age`
* `$2`:表示接下来的参数长度为2字节
* `18`:表示第3个参数,即键`age`对应的值`18`

## 6.2.3 观察重写AOF文件的效果

* step1. 向键为`nameList`的list中添加若干数据

```
127.0.0.1:6379> LPUSH nameList "Peter"
(integer) 1
127.0.0.1:6379> LPUSH nameList "Mary"
(integer) 2
127.0.0.1:6379> LPUSH nameList "Mike"
(integer) 3
127.0.0.1:6379> LPUSH nameList "Tom"
(integer) 4
```

* step2. 查看AOF文件

```
(base) root@yuanhong chpater6 % cat my_appendonly.aof
*2
$6
SELECT
$1
0
*3
$3
SET
$4
name
$5
Peter
*3
$3
SET
$3
age
$2
18
*3
$5
LPUSH
$8
nameList
$5
Peter
*3
$5
LPUSH
$8
nameList
$4
Mary
*3
$5
LPUSH
$8
nameList
$4
Mike
*3
$5
LPUSH
$8
nameList
$3
Tom
```

* step3. 运行`BGREWRITEAOF`命令手动触发AOF文件的重写动作

```
127.0.0.1:6379> BGREWRITEAOF
Background append only file rewriting started
```

查看被重写后的AOF文件

```
 cat my_appendonly.aof
REDIS0009?	redis-ver6.2.13?
redis-bits?@?ctime?dused-mem??q?
                                 aof-preamble???agenameList##omMikeMaryPeter?namePeter???%?%                                                                    
```

虽然文件内容被压缩了,但从一些人类可读的字符中仍然可以看到之前AOF文件里记录的多条`LPUSH`命令被合并为1条

在实际项目里,如果没有特殊情况,一般不会主动运行`BGREWRITEAOF`命令手动触发AOF文件的重写动作,而是会通过autoaof-rewrite-percentage和auto-aof-rewrite-min-size这两个参数来定义触发重写AOF文件的条件

## 6.2.4 模拟数据恢复的流程

此处我们基于刚刚的配置文件和AOF文件启动一个容器,看容器中是否存在刚刚写入的键值对

* step1. 启动容器

```
(base) root@yuanhong chpater6 % docker run -itd --name redis-server -v /StudyRedisBaseOnDocker/conf/chpater6/redis.conf:/redisConf/redis.conf:rw -v /StudyRedisBaseOnDocker/conf/chpater6/my_appendonly.aof:/StudyRedisBaseOnDocker/conf/chpater6/my_appendonly.aof:rw -p 6379:6379 redis:latest redis-server /redisConf/redis.conf
d46b73c5a69e947baf5f3a461a467438165997357ccc1c365dd20fdbf0754571
```

* step2. 确认容器状态

```
(base) root@yuanhong chpater6 % docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS        PORTS                    NAMES
d46b73c5a69e   redis:latest   "docker-entrypoint.s…"   2 seconds ago   Up 1 second   0.0.0.0:6379->6379/tcp   redis-server
```

* step3. 连接redis服务器并验证数据是否存在

```
(base) root@yuanhong chpater6 % docker exec -it redis-server /bin/bash
root@d46b73c5a69e:/data# redis-cli
127.0.0.1:6379> GET name
"Peter"
127.0.0.1:6379> LRANGE nameList 0 -1
1) "Tom"
2) "Mike"
3) "Mary"
4) "Peter"
127.0.0.1:6379> GET age
"18"
```

若AOF文件损坏,可以通过`redis-check-aof`命令进行修复

## 6.2.5 修复AOF文件

例:

```
(base) root@yuanhong chpater6 % redis-check-aof --fix /StudyRedisBaseOnDocker/conf/chpater6/my_appendonly.aof
The AOF appears to start with an RDB preamble.
Checking the RDB preamble to start:
[offset 0] Checking RDB file --fix
[offset 27] AUX FIELD redis-ver = '6.2.13'
[offset 41] AUX FIELD redis-bits = '64'
[offset 53] AUX FIELD ctime = '1692079763'
[offset 68] AUX FIELD used-mem = '1077712'
[offset 84] AUX FIELD aof-preamble = '1'
[offset 86] Selecting DB ID 0
[offset 164] Checksum OK
[offset 164] \o/ RDB looks OK! \o/
[info] 3 keys read
[info] 0 expires
[info] 0 already expired
RDB preamble is OK, proceeding with AOF tail...
AOF analyzed: size=164, ok_up_to=164, ok_up_to_line=1, diff=0
AOF is valid
```

注意:`redis-check-aof`是一个二进制,不是Redis中的一个命令.且修复后会生成一个RDB文件.

当你使用`redis-check-aof --fix`命令修复AOF文件时,工具可能会发现AOF中的某些命令序列是不完整或损坏的.为了修复这些问题并确保数据的完整性,工具会进行以下操作:

1. 它启动一个Redis实例并重播AOF文件中的所有命令,就像Redis在启动时重播AOF文件一样
2. 之后,它会将这个Redis实例的当前数据集导出为一个RDB文件
3. 最后,它会将这个新的RDB文件追加到原始的AOF文件中

这个新生成的RDB文件是AOF文件修复过程的一部分.当Redis下次启动并加载这个修复后的AOF文件时,它首先会加载RDB文件中的数据集,然后再继续重播RDB之后的所有AOF命令.这确保了数据的完整性和连续性.

使用这种方法的好处是:

* RDB是一种紧凑的二进制格式,加载速度非常快
* 通过将当前数据集导出为RDB格式并追加到AOF文件的方式,可以确保数据的完整性,即使原始AOF文件中的某些部分是损坏的

总之,生成RDB文件是`redis-check-aof --fix`命令修复AOF文件的一部分,目的是为了确保数据的完整性和提高加载速度


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://redis.sai.show/di-6-zhang-redis-shu-ju-chi-jiu-hua-cao-zuo/6.2-aof-chi-jiu-hua-ji-zhi-shi-zhan.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
