Redis 概述三 事务与持久化 | 青训营

109 阅读9分钟

Redis 事务


Redis 的事务的本质是一组命令的批处理。这组命令在执行过程中会被顺序的,一次性全部执行完毕,只要没有出现语法错误,这组命令在执行期间是不会被中断

Redis 事务特性

redis 的事务仅保证了数据的一致性,不具有像 DBMS 一样的 ACID 特性

  • 这组命令中的某些命令的执行失败不会影响其他命令的执行,不会引发 回滚 。即不具备原子性
  • 这组命令通过乐观锁机制实现了简单的隔离性。没有复杂的隔离级别
  • 这组命令的执行结果是被写入到内存的,是否持久取决于 Redis 的持久化策略,与事务无关

Redis 事务实现

  1. 三个命令

    • multi:开启事务
    • exec:执行事务
    • discard:取消事务

exec 与 discard 不可共存

Redis 持久化


Redis 具有持久化功能,其会按照设置以 快照操作日志 的形式将数据持久化到磁盘

根据持久化使用技术不同,分为两种:RDB 与 AOF

持久化(钝化)基本原理


RDB 与 AOF 可以共存,RDB 是默认持久化技术,而如果开启 AOF,则 AOF 的优先级更高

RDB 持久化


是指将内存中某一时刻的数据快照 全量 写入到指定的 rdb 文件的持久化技术。RDB 持久化是默认开启的。当 Redis 启动时会自动读取 RDB 快照文件,将数据从硬盘载入到内存,以恢复 Redis 关机前的数据库状态

持久化的执行

RDB 持久化的执行有三种方式:手动 save 命令,手动 bgsave 命令,与自动条件触发

  1. 手动 save 命令

    通过在 redis-cli 客户端中执行 save 命令可立即进行一次持久化保存。save 命令在执行期间会阻塞 redis-server 进程,直至持久化过程完毕。而在 redis-server 进程阻塞期间,redis 不能处理任何读写请求,无法对外提供服务

  2. 手动 bgsave 命令

    通过在 redis-cli 客户端中执行 bgsave 命令可立即进行一次持久化保存。不同于 save 的是,可以后台允许 save。bgsave 命令会使服务器进程 redis-server 生成一个子进程,由该子进程负责完成保存过程。在子进程进行保存过程中,不会阻塞 redis-server 进程对客户端读写请求的处理

  3. 自动条件触发

    本质仍是 bgsave 命令的执行。只不过是用户通过在配置文件中做相应的设置后,Redis 会根据设置信息自动调用 bgsave 命令执行。

  4. 查看上次持久化的时间

    • 格式:lastsave

RDB 优化配置

参考配置文件 redis.conf 中的 SNAPSHOTTING 字段内容

RDB 文件结构

RDB 持久化文件 dump.rdb 分为五部分

  1. SOF

    SOF 是一个常量,一个字符串 REDIS,仅包含这五个字符,其长度为 5。用于 标识 RDB 文件的开始,以便在加载 RDB 文件时可以迅速判断出文件是否是 RDB 文件

  2. rdb_version

    一个整数,长度为 4 字节,标识 RDB 文件的版本号

  3. databases

    内部包含任意多个非空数据库。而每个 database 由三部分组成

    • SODB:常量,占1字节,用于标识一个数据库的开始
    • db_number:数据库编号
    • key_value_pairs:当前数据库中的键值对数据

    每个 key_value_pairs 又由很多个用于描述键值对的数据构成

    • VALUE_TYPE:常量,占1字节,用于标识该键值对中 value 的类型
    • EXPIRETIME_UNIT:常量,占1字节,用于标识过期时间的单位是秒还是毫秒
    • time:当前 key-value 的过期时间
  4. EOF

    一个常量,长度为 1,用于标识 RDB 数据的结束,校验和的开始

  5. check_sum

    校验和 check_sum 用于判断 RDB 文件中的内容是否出现数据异常,其采用的是 CRC 校验算法

RDB 持久化过程

对于 Redis 默认的 Redis 持久化,在进行 bgsave 持久化时,redis-server 进程会 fork 出一个 bgsave 子进程,由该子进程以异步方式负责完成持久化。而在持久化过程中,redis-server 进程不会阻塞,其会继续接收并处理用户的读写请求。

bgsave 子进程的详细工作原理如下

  • 由于子进程可以继承父进程的所有资源,且父进程不能拒绝子进程的继承权。所以,bgsave 子进程有权读取到 redis-server 进程写入到内存中的用户数据,使得将内存数据持久化到 dump.rdb 成为可能
  • bgsave 子进程在持久化时首先会将内存中得全量数据 copy 到磁盘中的一个 RDB 临时文件,copy 结束后,在将该文件 rename 为 dump.rdb,替换掉原来的同名文件
  • 不过,在进行持久化过程中,如果 redis-server 进程接收到了用户写请求,则系统会将内存中发生数据修改的物理块 copy 出一个副本。等内存中的全量数据 copy 结束后,会再将副本中的数据 copy 到 RDB 临时文件。这个副本的生成是由于 Linux 系统的写时复制技术(Copy-On-Write)实现的

AOF 持久化


AOF,是指 Redis 将每一次的写操作都以日志的形式记录到一个 AOF 文件中的持久性技术。当需要恢复内存数据时,将这些写操作重新执行一次,便会恢复到之前的内存数据状态

AOF 基础配置

  1. AOF 的开启

    在 redis.conf 配置文件内将 appendonly 字段设置为 yes

  2. 文件名配置

    包括三类多个文件

    • 基本文件:可以是 RDF 格式也可以是 AOF 格式。其存放的内容是由 RDB 转为 AOF 当时内存的快照数据。该文件可以有多个
    • 增量文件:以操作日志形式记录转为 AOF 后的写入操作。该文件可以有多个
    • 清单文件:用于维护 AOF 文件的创建顺序,保障激活时的应用顺序。该文件只有一个
  3. 混合式持久化开启

    对于基本文件可以是 RDB 格式也可以是 AOF 格式。通过 aof-use-rdb-preamble 属性可以选择。其默认值是 yes,即默认 AOF 持久化的基本文件为 rdb 格式文件,也就是默认采用混合式持久化

AOF 文件格式

  1. Redis 协议

    增量文件扩展名为.aof,采用 AOF 格式。AOF 格式其实就是 Redis 通讯协议格式,AOF 持久化文件的本质就是基于 Redis 通讯协议的文本,将命令以纯文本的方式写入到文件中

    Redis 协议规定,Redis 文本是以行来划分,每行以\r\n 行结束。每一行都有一个我消息头,以表示消息类型。消息头由六种不同的符号表示

    • (+):表示一个正确的状态信息
    • (-):表示一个错误信息
    • (*):表示消息体总共有多少行,不包括当前行
    • ($):表示下一行消息数据的长度,不包括换行符长度\r\n
    • (空):表示一个消息数据
    • (:):表示返回一个数值

Rewrite 机制

为了防止 AOF 文件由于太大而占用大量的磁盘空间,降低性能,Redis 引入了 Rewrite 机制来对 AOF 文件进行压缩

  1. 何为 rewrite

    所谓 Rewrite 其实就是对 AOF 文件进行重写整理。当 Rewrite 开启后,主进程 redis-server 创建出一个子进程 bgrewriteaof,由该子进程完成 rewrite 过程。其首先对现有 aof 文件进行 rewrite 计算,将计算结果写入到一个临时文件,写入完毕后,再 rename 该临时文件为原 aof 文件名,覆盖原有文件

  2. rewrite 计算

    其遵循以下策略

    • 读操作命令不写入文件
    • 无效命令不写入文件
    • 过期数据不写入文件
    • 多条命令合并写入文件
  3. 手动开启 rewrite

    • 格式 bgrewriteaof

AOF 优化配置

  1. appendfsync

    当客户端提交写操作命令后,该命令就会写入到 aof_buf 中,而 aof_buf 中的数据持久化到磁盘 AOF 文件的过程称为数据同步。

    可以采用以下三种不同的数据同步策略

    • always:写操作命令写入 aof_buf 后会立即调用 fsync() 系统函数,将其追加到 AOF 文件。该策略效率较低,但相对比较安全,不会丢失太多的数据。(生产情况不要使用)
    • no:写操作命令写入 aof_buf 后什么都不做,不会调用 fsync() 函数。而将 aof_buf 中的数据同步磁盘的操作由操作系统负责。Linux 系统默认同步周期为 30 秒,效率较高。
    • everysec:默认策略。写操作命令写入 aof_buf 后并不直接调用 fsync(),而是每秒调用一次 fsync() 系统函数来完成同步。该策略兼顾了性能与安全,是一种折中方案
  2. no-appendfsync-on-rewrite

    前提是 appendfsync 设置为 always 或 everysec

    该属性用于指定,当主进程创建了子进程正在执行 bgsave 或 bgrewriteaof 时,主进程是否不调用 fsync() 来做数据同步

    如果调用 fsync(),在需要同步的数据量非常大时,会阻塞主进程对外提供服务,可以将此命令设置为 yes,关闭 rewrite 操作,但这样也就和 appendfsync no 一样了

  3. aof-rewrite-incremental-fsync

    当 bgrewriteaof 在执行过程也是先将 rewrite 计算的结果写入到了 aof_rewrite_buf 缓存中,然后当缓存中数据达到一定量后就会调用 fsync() 进行刷盘操作,即数据同步,将数据写入到临时文件。该属性用于控制 fsync() 每次刷盘的数据量最大不超过 4MB。这样可以避免由于单次刷盘量过大而引发长时间堵塞

  4. aof-timestamp-enabled

    该属性设置为 yes 则会开启在 AOF 文件中增加时间戳的显示功能,可方便按照时间对数据进行恢复。但该方法可能会与 AOF 解析器不兼容,所以默认值为 no,不开启

  5. aof-load-truncated

    AOF 文件可能会在加载到内存时被截断(当操作系统崩溃时,极有可能发生。但是当 Redis 崩溃,但系统没有崩溃时,不会发生这个情况),如果此命令设置为 yes,会重新加载 AOF 文件

AOF 持久化过程

RDB 与 AOF 对比


RDB 优势与不足

优势
  • RDB 文件较小(快照)
  • 数据恢复快
不足
  • 数据安全性较差
  • 写时复制会降低性能
  • RDB 文件可读性较差

AOF 优势与不足

优势
  • 数据安全性高
  • AOF 文件可读性高
不足
  • AOF 文件比较大(日志)
  • 写操作会影响性能
  • 数据恢复较慢(重新执行命令)

持久化技术的选型

官方推荐使用 RDB 与 AOF 混合式持久化。

若对数据安全性要求不高,则推荐使用纯 RDB 持久化方式

不推荐使用纯 AOF 持久化方式。

若 Redis 仅用于缓存,无需使用任何持久化技术