Redis总结学习笔记

一、Redis的基本安装使用

Redis的安装及服务端的启动

# 基本环境的安装,这里是centos7
yum install gcc-c++
# 下载,解压和编译。这里是用的最新版本6.0.9。    
wget https://download.redis.io/releases/redis-6.0.9.tar.gz
tar xzf redis-6.0.9.tar.gz
cd redis-6.0.9
make
# 现在编译的二进制文件可以在src目录中找到。并且可以找到redis.conf文件
mv redis.conf /etc/redis/redis.conf
# 然后编辑配置文件 vi /etc/redis/redis.conf
daemonize yes # 让Redis后台运行
bind 0.0.0.0 # 任何地址都可以访问,当然生产上的话根据公司的实际情况去配置。
requirepass test123; # 配置redis的密码,需要重启redis。
config set requirepass test123 # 在redis-cli中配置密码,不需要重启
# 然后以配置文件的方式启动redis服务端。
redis-server /etc/redis/redis.conf

客户端连接:

# 默认的端口是6379,默认的host是 127.0.0.1, 如果需要输入密码使用 -a 选项
redis-cli -p 6379 -a password -h 127.0.0.1
# 关闭服务端。
# 1. 可以直接在redis-cli命令行关闭(不会丢失快照数据)。然后可以使用命令 ps -ef|grep redis 查看服务端是否关闭
127.0.0.1:6379> shutdown
# 2. 可以直接在终端窗口输入命令关闭,实际上也是利用客户端的命令。
redis-cli -p xxx -a xxx -h xxx shutdown
# 3. 可以直接杀死进程, 使用 ps -ef|grep redis 查看 pid 然后使用 kill -9 xxx 杀死该进程。直接杀掉异常关机不会实现备份。

二、 Redis基本数据类型及应用场景

image-20201129123226199

2.1 String类型

  1. 设置 key 的值 set key value [EX seconds] [PX milliseconds] [NX|XX]

    • EX seconds : 将键的过期时间设置为 seconds 秒。 执行 set key value EX seconds 的效果等同于执行 setex key seconds value
    • PX milliseconds : 将键的过期时间设置为 milliseconds 毫秒。 执行 set key value PX milliseconds 的效果等同于执行 psetex key milliseconds value
    • NX : 只在键不存在时, 才对键进行设置操作。 执行 set key value NX 的效果等同于执行 setnx key value
    • XX : 只在键已经存在时, 才对键进行设置操作。
     set name "wangyun" EX 10 #=> Ok  对键 name 设置值 "wangyun",并且于10秒后过期。
     tll name #=> 3 查看到期剩余时间
     pttl name #=> 3212 查看到期的剩余时间(毫秒显示)
     set name "wangyun" PX 10 # 对键 name 设置值 "wangyun",并且于10毫秒后过期。
     set not-exists-key "value" NX #=> Ok 键不存在的时候,设置成功。
     set name "value" NX #=> nil 键已存在的时候,设置失败,返回 nil 。
     exists exists-key #=> 0 该键不存在
     set exists-key "value" XX #=> nil 因为键不存在设置失败。
    
  2. 在 key 不存在的情况下才设置其值 setnx key value

    只在键 key 不存在的情况下, 将键 key 的值设置为 value 。若键 key 已经存在, 则 SETNX 命令不做任何动作。

    exists job #=> 0
    setnx job "coding" #=> 1 因为键不存在,所以设置成功。
    setnx job "move brick" #=> 0 因为该键已经存在了,所以设置失败。
    
  3. 设置 key 的值并设置其过期时间 setex key seconds value

    将键 key 的值设置为 value , 并将键 key 的生存时间设置为 seconds 秒钟。如果键 key 已经存在, 那么 SETEX 命令将覆盖已有的值。

    setex name wangyun 10 #=> OK
    # 上面的那条命令等同于下面的两个命令。
    SET name wangyun #=> OK
    EXPIRE name seconds  #=>1 设置生存时间
    
  4. 设置 key 的值并设置其过期时间(毫秒级时间)psetex key milliseconds value

    这个命令和 SETEX 命令相似, 但它以毫秒为单位设置 key 的生存时间, 而不是像 SETEX 命令那样以秒为单位进行设置。

    psetex mykey 1000 "Hello" #=> OK
    pttl mykey #=> 800
    
  5. 获取 key 的值 get key

    如果键 key 不存在, 那么返回特殊值 nil ; 否则, 返回键 key 的值。

    如果键 key 的值并非字符串类型, 那么返回一个错误, 因为 GET 命令只能用于字符串值。

    get db #=> nil
    set db redis #=> OK
    get db #=> "redis"
    
  6. 设置 key 的值,并返回key的旧值 getset key value

    返回给定键 key 的旧值。如果键 key 没有旧值, 也即是说, 键 key 在被设置之前并不存在, 那么命令返回 nil 。当键 key 存在但不是字符串类型时, 命令返回一个错误。

    getset name wangyun #=> nil
    getset name wy #=> "wangyun"
    
  7. 返回 key 储存的字符串值的长度 strlen key

    strlen 命令返回字符串的长度。当键 key 不存在时,返回 0。 当key不是字符串是返回一个错误。

    set mykey "Hello world" #=> OK
    strlen mykey #=> 11
    
  8. 追加值到 key 的末尾 append key value

    如果键 key 已经存在并且它的值是一个字符串, APPEND 命令将把 value 追加到键 key 现有值的末尾。如果 key 不存在, APPEND 就简单地将键 key 的值设为 value , 就像执行 SET key value 一样。

    exists myphone #=> 0
    append myphone apple #=>5
    append myphone " xiaomi" #=> 12
    
  9. 修改key的值 setrange key offset value

    从偏移量 offset 开始, 用 value 参数覆写(overwrite)键 key 储存的字符串值。

    SETRANGE 命令会确保字符串足够长以便将 value 设置到指定的偏移量上, 如果键 key 原来储存的字符串长度比偏移量小(比如字符串只有 5 个字符长,但你设置的 offset10 ), 那么原字符和偏移量之间的空白将用零字节(zerobytes, "\x00" )进行填充。

    set email "906971957@qq.com" #=> OK
    setrange email 5 "@qq.com" #=> "90697@qq.com.com" 注意这是替换value值
    
  10. 返回 key 的指定部分值 getrange key start end

    返回键 key 储存的字符串值的指定部分, 字符串的截取范围由 startend 两个偏移量决定 (包括 startend 在内)。负数偏移量表示从字符串的末尾开始计数, -1 表示最后一个字符, -2 表示倒数第二个字符, 以此类推。

    SET greeting "hello, my friend" #=> OK
    getrange greeting 0 4 #=> "hello" 返回索引0-4的字符,包括4。
    getrange greeting 0 -1 #=> "hello, my friend" 从第一个到最后一个
    getrange greeting 0 100 #=> "hello, my friend" 值域范围不超过实际字符串,超过部分自动被符略
    
  11. 为 key 的值加1 incr key

    如果键 key 不存在, 那么它的值会先被初始化为 0 , 然后再执行 INCR 命令。

    如果键 key 储存的值不能被解释为数字, 那么 INCR 命令将返回一个错误。

    INCR 命令是一个针对字符串的操作。 因为 Redis 并没有专用的整数类型, 所以键 key 储存的值在执行 INCR 命令时会被解释为十进制 64 位有符号整数。

    set page_view 20 #=> OK
    incr page_view #=> 21
    # 命令: incrby key increment 为key的值加上增量 increment 
    incrby page_view 23 #=> 44
    # 该命令是为 key 的值增加一个浮点数,并返回一个字符串类型。
    incrbyfloat page_view 2.3 #=> "46.3"
    
  12. 为key的值减1 decr key

    如果键 key 不存在, 那么键 key 的值会先被初始化为 0 , 然后再执行 DECR 操作。

    如果键 key 储存的值不能被解释为数字, 那么 DECR 命令将返回一个错误。

    exists nums #=> 0
    decr nums #=> -1
    set num "1" #=> OK
    decr num #=> 0
    decrby num 23 #=> -23
    decrby num -20 #=> -3
    
  13. 为多个key设置值 mset key value [key value]

    同时为多个键设置值。如果某个给定键已经存在, 那么 MSET 将使用新值去覆盖旧值。

    MSET 是一个原子性(atomic)操作, 所有给定键都会在同一时间内被设置, 不会出现某些键被设置了但是另一些键没有被设置的情况。

    mset date "2012.3.30" time "11:00 a.m." weather "sunny" #=> OK
    mget date time weather
    
  14. 为所有给定键设置值,当且仅当所有给定的键都不存在时 msetnx key value [key value]

    当且仅当所有给定键都不存在时, 为所有给定键设置值。

    即使只有一个给定键已经存在, MSETNX 命令也会拒绝执行对所有键的设置操作。

    MSETNX 是一个原子性(atomic)操作, 所有给定键要么就全部都被设置, 要么就全部都不设置, 不可能出现第三种状态。

    MSETNX rmdbs "MySQL" nosql "MongoDB" key-value-store "redis" #=> 1
    

2.2 哈希型

  1. 为哈希表 hash 中的 field 设置为 value hset hash field value

    如果给定的哈希表并不存在, 那么一个新的哈希表将被创建并执行 HSET 操作。

    如果域 field 已经存在于哈希表中, 那么它的旧值将被新值 value 覆盖。

    # 当 HSET 命令在哈希表中新创建 field 域并成功为它设置值时, 命令返回 1 ; 如果域 field 已经存在于哈希表, 并且 HSET 命令成功使用新值覆盖了它的旧值, 那么命令返回 0 。
    hset website google "www.g.cn" #=> 1
    hset webside google "google.com" #=> 0
    
  2. 当哈希中的field字段不存在的情况下,设置其值。hsetnx hash field value

    当且仅当域 field 尚未存在于哈希表的情况下,将它的值设置为 value

    如果给定域已经存在于哈希表当中, 那么命令将放弃执行设置操作。

    如果哈希表 hash 不存在, 那么一个新的哈希表将被创建并执行 HSETNX 命令。

    # HSETNX 命令在设置成功时返回 1 , 在给定域已经存在而放弃执行设置操作时返回 0 。
    hsetnx database key-value-store Redis #=> 1
    hsetnx database key-value-store Riak #=> 0
    hget database key-value-store #=> "Redis" 所以给定域已经存在哈希表中的话,那么命令将不执行。
    
  3. 返回哈希表中给定域的值。 hget hash field

    如果给定域不存在于哈希表中, 又或者给定的哈希表并不存在, 那么命令返回 nil

    hset homepage redis redis.com #=> 1
    hget homepage redis #=> "redis.com"
    
  4. 检查给定域 field 是否存在于哈希表 hash 中。hexists hash field

    HEXISTS 命令在给定域存在时返回 1 , 在给定域不存在时返回 0

    hexists phone myphone #=> 0
    hset phone myphone iphone #=> 1
    hexists phone myphone #=> 1
    
  5. 一些其他的用于哈希中的方法

    # 删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略。 hdel hash field [field …]
    hdel website google baidu #=> 2
    hdel website not-exists-field #=> 0
    
    # 返回哈希表 hash 中域的数量。 hlen hash
    hlen website #=> 1
    
    # 返回哈希表 hash 中,与给定域 field 相关联的值的字符串长度。hstrlen hash field 
    hmset myhash f1 "HelloWorld" f2 "99" f3 "-256" #=> 0
    hstrlen myhash f1 #=> 10
    
    # 为哈希表hash中的域field的值加上增量increment其值可以为负。hincrby hash field increment
    hexists counter page_view #=> 0
    hincrby counter page_view 200 #=> 200
    hget count page_view #=> "200"
    
    # 为哈希表hash中的域field加上浮点数增量increment。 hincrbyfloat hash field increment
    hset mykey field 10.5 #=> 1
    hincrbyfloat mykey field 0.1 #=> 10.6
    
    # 同时将多个field-value(域-值)对设置到哈希表hash中。hmset key field value [field value]
    # 如果 key 不存在,一个空哈希表被创建并执行 HMSET 操作。
    # hash 不是哈希类型时返回错误。
    hmset website google www.google.com yahoo www.yahoo.com #=> OK
    
    # 返回哈希表 hash 中,一个或多个给定域的值。hmget hash field [field ...]
    # 如果给定的域不存在于哈希表,那么返回一个 nil 值。
    hmset pet dog "doudou" cat "nounou" #=> OK 
    hmget pet dog cat lion #=> "doudou" "nounou" nil
    
    # 返回哈希表 hash 中的所有域。 hkeys hash
    hmset website google www.google.com yahoo www.yahoo.com #=> OK
    hkeys website #=> "google" "yahoo"
    
    # 返回哈希表 hash 中所有域的值。 hvals hash
    hvals website #=> "www.google.com" "www.yahoo.com"
    
    # 返回哈希表 hash 中,所有的域和值。hgetall hash
    hset people jack "Jack Sparrow" #=> OK
    hset people gump "Forrest Gump" #=> OK
    hgetall people 
    1) "jack"          # 域
    2) "Jack Sparrow"  # 值
    3) "gump"
    4) "Forrest Gump"
    

2.3 列表

  1. 将一个或多个值 value 插入到列表 list 的表头 Lpush list value [value ...]

    如果有多个 value 值,那么各个 value 值按从左到右的顺序依次插入到表头: 比如说,对空列表 mylist 执行命令 LPUSH mylist a b c ,列表的值将是 c b a ,这等同于原子性地执行 LPUSH mylist aLPUSH mylist bLPUSH mylist c 三个命令。

    如果 key 不存在,一个空列表会被创建并执行 LPUSH操作。

     lpush languages python
    

三、Redis的持久化

3.1 持久化方式RDB和AOF的区别:

RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘。

具体过程:

  1. Redis使用 fork 函数复制一份当前进程(父进程)的副本(子进程);
  2. 父进程继续接收并处理客户端发来的命令,而子进程开始将内存中的数据写入硬盘中的临时文件;
  3. 当子进程写入完所有数据后会用该临时文件替换旧的 RDB 文件,至此一次快照操作完成。

image-20201129123312275

AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。 Redis 还可以在后台对 AOF 文件进行重写(rewrite),使得 AOF 文件的体积不会超出保存数据集状态所需的实际大小。

image-20201129123348248

3.2 RDB和AOF持久化的详细过程

3.2.1 RDB的持久化

触发RDB持久化过程分为手动触发和自动触发

手动触发:

  1. save命令:阻塞当前Redis服务器,直到RDB过程完成为止,对于内存 比较大的实例会造成长时间阻塞,线上环境不建议使用
  2. bgsave命令:Redis进程执行fork操作创建子进程,RDB持久化过程由子 进程负责,完成后自动结束。阻塞只发生在fork阶段,一般时间很短

自动触发:

  1. 使用配置文件中的save相关配置,如 save m n。表示m秒内数据集存在n次修改 时,自动触发 bgsave
  2. 如果从节点执行全量复制操作,主节点自动执行 bgsave 生成 RDB 文件并发送给从节点。
  3. 执行 debug reload 命令重新加载 Redis 时,也会自动触发 save 操作。
  4. 默认情况下执行 shutdown 命令时,如果没有开启 AOF 持久化功能则 自动执行 bgsave

bgsave是主流的触发RDB持久化方式,流程图如下 :

img

bgsave 执行的具体过程:

  1. 执行 bgsave 命令,Redis父进程判断当前是否存在正在执行的子进 程,如 RDB/AOF 子进程,如果存在 bgsave 命令直接返回。
  2. 父进程执行 fork 操作创建子进程,fork 操作过程中父进程会阻塞,通 过 info stats 命令查看 latest_fork_usec 选项,可以获取最近一个 fork 操作的耗时,单位为微秒
  3. 父进程 fork 完成后,bgsave 命令返回“Background saving started”信息并不再阻塞父进程,可以继续响应其他命令。
  4. 子进程创建 RDB 文件,根据父进程内存生成临时快照文件,完成后 对原有文件进行原子替换。执行 lastsave 命令可以获取最后一次生成 RDB 的时间,对应 info 统计的 rdb_last_save_time 选项。
  5. 进程发送信号给父进程表示完成,父进程更新统计信息。
3.2.2 AOF 持久化

AOF的主要作用 是解决了数据持久化的实时性,目前已经是Redis持久化的主流方式。

AOF的工作流程操作:命令写入 (append)、文件同步(sync)、文件重写(rewrite)、重启加载 (load)

img

具体过程说明:

  1. 所有的写入命令会追加到 aof_buf(缓冲区)中。
  2. AOF 缓冲区根据对应的策略向硬盘做同步操作。
  3. 随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩的目的。
  4. 当 Redis 服务器重启时,可以加载 AOF 文件进行数据恢复。

问答:

  1. AOF为什么把命令追加到aof_buf中?

    Redis使用单线程响应命令,如 果每次写AOF文件命令都直接追加到硬盘,那么性能完全取决于当前硬盘负 载。先写入缓冲区aof_buf中,还有另一个好处,Redis可以提供多种缓冲区同步硬盘的策略,在性能和安全性方面做出平衡。

  2. 重写后的AOF文件为什么可以变小?

    • 进程内已经超时的数据不再写入文件。
    • 旧的AOF文件含有无效命令,如del key1、hdel key2、srem keys、set a111、set a222等。重写使用进程内数据直接生成,这样新的AOF文件只保留最终数据的写入命令。
    • 多条写命令可以合并为一个,如:lpush list alpush list blpush list c 可以转化为:lpush list a b c 。为了防止单条命令过大造成客户端缓冲区溢出,对于listsethashzset等类型操作,以64个元素为界拆分为多条。
  3. AOF重写过程是如何手动触发和自动触发的?

    • 手动触发:直接调用bgrewriteaof命令。
    • 自动触发:根据auto-aof-rewrite-min-size和auto-aof-rewrite-percentage参数确定自动触发时机。
    • auto-aof-rewrite-min-size:表示运行AOF重写时文件最小体积,默认 为64MB。
    • auto-aof-rewrite-percentage:代表当前AOF文件空间 (aof_current_size)和上一次重写后AOF文件空间(aof_base_size)的比值。
  4. 如果 AOF 文件出错了,怎么办?

    服务器可能在程序正在对 AOF 文件进行写入时停机, 如果停机造成了 AOF 文件出错, 那么 Redis 在重启时会拒绝载入这个 AOF 文件, 从而确保数据的一致性不会被破坏。

    当发生这种情况时, 可以用以下方法来修复出错的 AOF 文件:

    • 为现有的 AOF 文件创建一个备份。

    • 使用 Redis 附带的 redis-check-aof 程序,对原来的 AOF 文件进行修复。

    • 重启 Redis 服务器,等待服务器载入修复后的 AOF 文件,并进行数据恢复。

重启后恢复流程图:

img

具体过程说明:

  1. AOF持久化开启且存在AOF文件时,优先加载AOF文件,打印如下日志:

    DB loaded from append only file: 5.841 seconds
    
  2. AOF关闭或者AOF文件不存在时,加载RDB文件,打印如下日志:

DB loaded from disk: 5.586 seconds
  1. 加载AOF/RDB文件成功后,Redis启动成功。
  2. AOF/RDB文件存在错误时,Redis启动失败并打印错误信息。

3.3 RDB(Redis DataBase的缩写)的优缺点

优点:

  1. 一旦采用该方式,那么你的整个Redis数据库将只包含一个文件,这对于文件备份而言是非常完美的。比如,你可能打算每个小时归档一次最近24小时的数据,同时还要每天归档一次最近30天的数据。通过这样的备份策略,一旦系统出现灾难性故障,我们可以非常容易的进行恢复。
  2. 对于灾难恢复而言,RDB是非常不错的选择。因为我们可以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上。
  3. 性能最大化。对于Redis的服务进程而言,在开始持久化时,它唯一需要做的只是fork出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行IO操作了。
  4. 相比于AOF机制,如果数据集很大,RDB的启动效率会更高。

缺点:

  1. 如果你需要尽量避免在服务器故障时丢失数据,那么 RDB 不适合你。 虽然 Redis 允许你设置不同的保存点(save point)来控制保存 RDB 文件的频率, 但是, 因为RDB 文件需要保存整个数据集的状态, 所以它并不是一个轻松的操作。 因此你可能会至少 5 分钟才保存一次 RDB 文件。 在这种情况下, 一旦发生故障停机, 你就可能会丢失好几分钟的数据。
  2. 每次保存 RDB 的时候,Redis 都要 fork() 出一个子进程,并由子进程来进行实际的持久化工作。 在数据集比较庞大时, fork() 可能会非常耗时,造成服务器在某某毫秒内停止处理客户端; 如果数据集非常巨大,并且 CPU 时间非常紧张的话,那么这种停止时间甚至可能会长达整整一秒。 虽然 AOF 重写也需要进行 fork() ,但无论 AOF 重写的执行间隔有多长,数据的耐久性都不会有任何损失。

3.4 AOF(AppendOlny File的缩写)的优缺点

优点:

  1. 该机制可以带来更高的数据安全性,即数据持久性。Redis中提供了3中同步策略,即每秒同步(默认)、每修改同步和不同步。事实上,每秒同步也是异步完成的,其效率也是非常高的,所差的是一旦系统出现宕机现象,那么这一秒钟之内修改的数据将会丢失。而每修改同步,我们可以将其视为同步持久化,即每次发生的数据变化都会被立即记录到磁盘中。可以预见,这种方式在效率上是最低的。
  2. AOF 文件是一个只进行追加操作的日志文件(append only log), 因此对 AOF 文件的写入不需要进行 seek , 即使日志因为某些原因而包含了未写入完整的命令(比如写入时磁盘已满,写入中途停机,等等), redis-check-aof 工具也可以轻易地修复这种问题。
  3. Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。 整个重写操作是绝对安全的,因为 Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 而一旦新 AOF 文件创建完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操作。
  4. AOF 文件有序地保存了对数据库执行的所有写入操作, 这些写入操作以 Redis 协议的格式保存, 因此 AOF 文件的内容非常容易被人读懂, 对文件进行分析(parse)也很轻松。 导出(export) AOF 文件也非常简单: 举个例子, 如果你不小心执行了 FLUSHALL 命令, 但只要 AOF 文件未被重写, 那么只要停止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就可以将数据集恢复到 FLUSHALL 执行之前的状态。

缺点:

  1. 对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
  2. 根据所使用的同步策略,AOF 的速度可能会慢于 RDB 。 在一般情况下, 每秒同步的性能依然非常高, 而关闭同步可以让 AOF 的速度和 RDB 一样快, 即使在高负荷之下也是如此。 不过在处理巨大的写入载入时,RDB 可以提供更有保证的最大延迟时间(latency)。
  3. AOF 在过去曾经发生过这样的 bug : 因为个别命令的原因,导致 AOF 文件在重新载入时,无法将数据集恢复成保存时的原样。测试套件里为这种情况添加了测试: 它们会自动生成随机的、复杂的数据集, 并通过重新载入这些数据来确保一切正常。 虽然这种 bug 在 AOF 文件中并不常见, 但是对比来说, RDB 几乎是不可能出现这种 bug 的。

3.5 RDB和AOF该如何选择

RDB和AOF的总结:

image-20201129150812125

如何选择:

  1. 二者选择的标准,就是看系统是愿意牺牲一些性能,换取更高的缓存一致性(aof),还是愿意写操作频繁的时候,不启用备份来换取更高的性能,待手动运行save的时候,再做备份(rdb)。rdb这个就更有些 eventually consistent的意思了。不过生产环境其实更多都是二者结合使用的。
  2. Redis 还可以同时使用 AOF 持久化和 RDB 持久化。 在这种情况下, 当 Redis 重启时, 它会优先使用 AOF 文件来还原数据集, 因为 AOF 文件保存的数据集通常比 RDB 文件所保存的数据集更完整。
  3. 如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。

3.6 如何配置

3.6.1 RDB持久化配置:

Redis会将数据集的快照dump到dump.rdb文件中。此外,我们也可以通过配置文件来修改Redis服务器dump快照的频率,在打开6379.conf文件之后,我们搜索save,可以看到下面的配置信息:

  1. save 900 1 #在900秒(15分钟)之后,如果至少有1个key发生变化,则dump内存快照。
  2. save 300 10 #在300秒(5分钟)之后,如果至少有10个key发生变化,则dump内存快照。
  3. save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生变化,则dump内存快照。

条件之间是“或”的关系,只要满足其中一个条件,就会进行快照。

Redis默认会将快照文件存储在当前目录(可在 redis-cli 的命令行中输入 config get dir 来查看)的 dump.rdb 文件中,可以通过配置 dirdbfilename 两个参数分别指定快照文件的存储路径和文件名。

3.6.2 AOF持久化配置:

在配置文件中开启 AOF 持久化:

appendonly yes

AOF 文件的保存位置和 RDB 文件的位置相同,目录都是通过 dir 参数设置的,默认的文件名是appendonly.aof ,以通过 appendfilename 参数修改:

appendfilename appendonly.aof

AOF 方式默认提供三种持久化方案:

  1. appendfsync always # 每次有数据修改发生时都会写入AOF文件, 所以其性能也会受到影响。
  2. appendfsync everysec # 当设置 appendfsynceverysec 的时候,Redis会默认每隔一秒进行一次 fsync调用,将缓冲区中的数据写到磁盘。但是当这一次的 fsync 调用时长超过1秒时。Redis会采取延迟 fsync 的策略,再等一秒钟。也就是在两秒后再进行 fsync ,这一次的 fsync 就不管会执行多 长时间都会进行。这时候由于在 fsync 时文件描述符会被阻塞,所以当前的写操作就会阻塞。 结论就是,在绝大多数情况下,Redis会每隔一秒进行一 次 fsync 。在最坏的情况下,两秒钟会进行一次fsync 操作。这一操作在大多数数据库系统中被称为group commit ,就是组合多次写操作的数据,一次性将日志写到磁盘。
  3. appendfsync no # Redis不会主动调用fsync去将AOF日志内容同步到磁盘,所以这一切就完全依赖于操作系统的调试了。对大多数Linux操作系统,是每30秒进行一次fsync,将缓冲区中的数据写到磁盘上。