Redis学习笔记(三)Redis原理及高级特性

1、发布订阅模式

使用Redis的List数据结构可以实现消息队列(例如:rpush 和 lpop),但是需要消费者循环调用 lpop 查看 List 中是否有等待处理的消息,为了减少通信的消耗,可以 sleep()一段时间再消费。但是会出现两个问题:

  1. 如果生产者生产消息的速度远大于消费者消费消息的速度,List会占用大量的内存。
  2. 消息的实时性降低。
  3. 不支持一对多的消息分发

list 还提供了一个阻塞的命令:blpop,没有任何元素可以弹出的时候,连接会被阻塞。

Redis提供了一组命令实现发布/订阅模式。这种方式,发送者和接收者没有直接关联(实现了解耦),接收者也不需要持续尝试获取消息。

(1)订阅频道(channel)

我们可以把消息按照类型分成不同的频道,例如:新闻可以分为体育新闻、娱乐新闻、时政新闻等不同的类型。订阅者(消费者)可以订阅一个或者多个频道。消息的发布者(生产者)可以给指定的频道发布消息。只要有消息到达了频道,所有订阅了这个频道的订阅者都会收到这条消息。

需要注意的注意是,发出去的消息不会被持久化,因为它已经从队列里面移除了,所以消费者只能收到它开始订阅这个频道之后发布的消息。

发布订阅模式命令:

#订阅频道,可以一次订阅多个
subscribe channel-1 channel-2 channel-3
#向指定频道发布消息,一次只能向一个频道发送信息
publish channel-1 abcd
#取消订阅,不写频道名则退订全部。redis-cli客户端在订阅状态不能使用unsubscribe
unsubscribe channel-1 channel-2

(2)按规则(Pattern)订阅频道

Redis支持使用?和*占位符订阅频道。?代表一个字符,*代表 0 个或者多个字符。使用Pattern订阅频道的命令是psubscribe。

#可以输入一个活多个规则
psubscribe *sport news*
#也可以使用没有占位符的频道名称
psubscribe news-weather

2、Redis 事务

Redis的单个命令是原子性的,但如果涉及到多个命令的时候,需要把多个命令作为一个不可分割的处理序列,就需要用到事务。例如:使用setnx实现分布式锁,我们先set,然后设置对key设置 expire,防止 del发生异常的时候锁不会被释放,业务处理完了以后再del,这三个动作我们就希望它们作为一组命令执行。
Redis 的事务有两个特点:

  1. 按进入队列的顺序执行。
  2. 不会受到其他客户端的请求的影响。

Redis 的事务涉及到四个命令:multi(开启事务),exec(执行事务),discard(取消事务),watch(监视)

(1)事务的使用

以简单的转账为例:zhangsan和lisi各有1000元,zhangsan向lisi转账100元。zhangsan账户减少100元,lisi账户增加100元。

127.0.0.1:6379>set zhangsan 1000
OK
127.0.0.1:6379>set lisi 1000
OK
#开启事务
127.0.0.1:6379>multi
OK
127.0.0.1:6379>decrby zhangsan 100
QUEUED
127.0.0.1:6379>incrby lisi 100
QUEUED
#执行事务
127.0.0.1:6379> exec
1) (integer) 900
2) (integer) 1100
127.0.0.1:6379>get zhangsan 
"900"
127.0.0.1:6379>get lisi
"1100"

通过multi的命令开启事务。事务不能嵌套,多个multi命令效果一样。multi执行后,客户端可以继续向服务器发送任意多条命令, 这些命令不会立即被执行, 而是被放到一个队列中, 当exec命令被调用时, 所有队列中的命令才会被执行。

通过exec的命令执行事务。如果没有执行exec,所有的命令都不会被执行。如果中途不想执行事务了,可以调用discard可以清空事务队列,放弃执行。

(2)watch命令

在Redis中还提供了一个watch命令,它可以为Redis事务提供CAS乐观锁行为(Check and Set / Compare and Swap),也就是多个线程更新变量的时候,会跟原值做比较,只有它没有被其他线程修
改的情况下,才更新成新的值。

我们可以用watch监视一个或者多个 key,如果开启事务之后,至少有一个被监视key键在 exec 执行之前被修改了, 那么整个事务都会被取消(key 提前过期除外)。可以用 unwatch 取消。
注:watch命令不能在multi之后使用

示例:

client1client2
127.0.0.1:6379> set balance 1000
OK
127.0.0.1:6379> watch balance
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incrby balance 100
QUEUED
127.0.0.1:6379> decrby balance 100
(integer) 900
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> get balance
"900"

(3)执行事务遇到错误

事务执行遇到的错误分成两种,一种是在执行exec之前发生错误,一种是在执行 exec 之后发生错误。

在执行 exec 之前发生错误
例如:入队的命令存在语法错误,包括参数数量,参数名等等(编译器错误)。

127.0.0.1:6379>multi
127.0.0.1:6379>set jack 123
127.0.0.1:6379>hset hkey 456
(error) ERR wrong number of arguments for 'hset' command
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because 

在这种情况下事务会被拒绝执行,也就是队列中所有的命令都不会得到执行。

在执行 exec 之后发生错误
比如,类型错误,比如对 String 使用了 Hash 的命令,这是一种运行时错误。

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set skey 123
QUEUED
127.0.0.1:6379> hset skey a b
QUEUED
127.0.0.1:6379> exec
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> get skey 
"123"

最后我们发现set skey 123的命令是成功的,也就是在这种发生了运行时异常的情况下,只有错误的命令没有被执行,但是其他命令没有受到影响。这个显然不符合我们对原子性的定义,也就是我们没办法用 Redis 的这种事务机制来实现原子性,保证数据的一致。
Redis之所以不支持回滚,是因为只有在执行的Redis命令有语法错误(在将命令放入事务队列时,Redis能够检查出问题),或者对某个键执行不符合其数据结构的操作时才会执行失败,这种问题一般出现在程序开发阶段,在生产上不会产生。Redis已经在系统内部进行功能简化,确保更快的运行速度,因此Redis不需要事务回滚的能力。

3、Lua 脚本

Lua是一种轻量级脚本语言,它是用 C 语言编写的,跟数据的存储过程有点类似。 使用 Lua 脚本来执行 Redis 命令的好处:

  1. 一次发送多个命令,减少网络开销。
  2. Redis会将整个脚本作为一个整体执行,不会被其他请求打断,保持原子性。
  3. 对于复杂的组合命令,我们可以放在文件中,可以实现程序之间的命令集复用。

本文只说明Redis怎么调用lua脚本,具体的lua详细语法参考以下链接。

Lua详细语法教程:https://www.runoob.com/lua/lua-tutorial.html

(1)在Redis中调用Lua脚本

#命令
eval lua-script key-num [key1 key2 key3 ....] [value1 value2 value3 ....]
#示例 ,返回一个字符串,0个参数
eval "return 'Hello World'" 0
  • eval 代表执行 Lua 语言的命令。
  • lua-script 代表 Lua 语言脚本内容。
  • key-num 表示参数中有多少个 key,需要注意的是 Redis 中 key 是从 1 开始的,如果没有 key 的参数,那么写 0。
  • [key1 key2 key3…]是 key 作为参数传递给 Lua 语言,也可以不填,但是需要和 key-num 的个数对应起来。
  • [value1 value2 value3 ….]这些参数传递给 Lua 语言,它们是可填可不填的。

(2)在Lua脚本中调用Redis命令

127.0.0.1:6379>eval "return redis.call('set',KEYS[1],ARGV[1])" 1 k1 1234
OK
127.0.0.1:6379> get k1
"1234"

上述命令等价于set k1 1234
在 redis-cli 中直接写Lua脚本不够方便,也不能实现编辑和复用,通常我们会把脚本放在文件里面,然后执行这个文件。

调用Lua脚本文件
文件内容:

redis.call('set','test','123456')
return redis.call('get','test')

在Redis客户端中调用 Lua 脚本

#Redis有密码 则加上 -a password
#./redis-cli -a password --eval luatest.lua 0
./redis-cli --eval luatest.lua 0

缓存 Lua 脚本
在脚本比较长的情况下,如果每次调用脚本都需要把整个脚本传给Redis服务端,会产生比较大的网络开销。为了解决这个问题,Redis 提供了EVALSHA命令,允许开发者通过脚本内容的SHA1摘要来执行脚本。
Redis在执行script load命令时会计算脚本的 SHA1 摘要并记录在脚本缓存中,执行EVALSHA命令时Redis会根据提供的摘要从脚本缓存中查找对应的脚本内容,如果找到了则执行脚本,否则会返回错误:“NOSCRIPT No matching script. Please use EVAL.”

127.0.0.1:6379> script load "return 'Hello World'"
"470877a599ac74fbfda41caa908de682c5fc7d4b" 
127.0.0.1:6379> evalsha "470877a599ac74fbfda41caa908de682c5fc7d4b" 0
"Hello World"

对于比较复杂的脚本,把脚本变成单行,语句之间使用分号隔开。然后使用script load即可。

(3)脚本超时

为了防止某个脚本执行时间过长导致Redis无 法提 供 服务 , Redis提 供 了lua-time-limit 参数限制脚本的最长运行时间,默认为 5 秒钟。lua-time-limit 5000(redis.conf 配置文件中)

当脚本运行时间超过这一限制后,Redis 将开始接受其他命令但不会执行(以确保脚本的原子性,因为此时脚本并没有被终止),而是会返回“BUSY”错误。

Redis提供了一个script kill 的命令来中止脚本的执行。新开一个客户端:script kill

如果当前执行的Lua 脚本对Redis的数据进行了修改(SET、DEL 等),那么通过script kill 命令是不能终止脚本运行的。因为要保证脚本运行的原子性,如果脚本执行了一部分终止,那就违背了脚本原子性的要求。最终要保证脚本要么都执行,要么都不执行。

遇到这种情况,只能通过 shutdown nosave 命令来强行终止 redis。shutdown nosave 和 shutdown 的区别在于 shutdown nosave 不会进行持久化操作,意味着发生在上一次快照后的数据库修改都会丢失。

4、Redis为什么这么快

Redis提供了redis-benchmark工具,用来对redis做压力测试,使用如下命令:

./redis-benchmark -t set,lpush -n 100000 -q

结果:
SET: 51546.39 requests per second – 每秒钟处理 5 万多次set请求
LPUSH: 61124.69 requests per second --每秒钟处理 6 万多次lpush请求

根据官方的数据,Redis的QPS最高可以达到 10 万左右(每秒请求数)。

Redis速度快的原因
1、存内存结构。时间复杂度 O(1)
2、单线程。没有创建线程、销毁线程带来的消耗、避免了上线文切换导致的 CPU 消耗、避免了线程之间带来的竞争问题,例如加锁释放锁死锁等等。
3、多路复用。异步非阻塞 I/O,多路复用处理并发连接。

Redis使用单线程的原因
因为单线程已经够用了,CPU 不是 redis 的瓶颈。Redis 的瓶颈最有可能是机器内存或者网络带宽。既然单线程容易实现,而且 CPU 不会成为瓶颈,那就顺理成章地采用单线程的方案了。

5、内存回收

Reids所有的数据都是存储在内存中的,在某些情况下需要对占用的内存空间进行回收。内存回收主要分为两类,一类是key过期,一类是内存使用达到上限(max_memory)触发内存淘汰。

(1)过期策略

对设置了过期时间的key,需要进行key过期检查,以便回收内存空间。有3中设计思路。
定时过期(主动淘汰)
每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的 CPU 资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。

惰性过期(被动淘汰)
只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。

定期过期
每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key ,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。

注:Redis中同时使用了惰性过期和定期过期两种过期策略。

(2)淘汰策略

当Redis里的key过多,导致内存使用达到最大内存极限时,需要使用淘汰算法来决定清理掉哪些数据,以保证新数据的存入。
最大内存设置
redis.conf 参数配置:

#maxmemory <bytes>

如果不设置 maxmemory 或者设置为 0,64 位系统不限制内存,32 位系统最多使用 3GB 内存。
动态修改:

redis> config set maxmemory 2GB

淘汰策略
redis.conf配置文件:
#maxmemory-policy noeviction

具体策略有如下8种:

# volatile-lru -> Evict using approximated LRU among the keys with an expire set. 
# allkeys-lru -> Evict any key using approximated LRU. 
# volatile-lfu -> Evict using approximated LFU among the keys with an expire set. 
# allkeys-lfu -> Evict any key using approximated LFU. 
# volatile-random -> Remove a random key among the ones with an expire set. 
# allkeys-random -> Remove a random key, any key. 
# volatile-ttl -> Remove the key with the nearest expire time (minor TTL)
# noeviction -> Don't evict anything, just return an error on write operations.

淘汰算法:
LRU:Least Recently Used:最近最少使用。判断最近被使用的时间,目前最远的数据优先被淘汰。
LFU:Least Frequently Used,最不常用,4.0 版本新增。
random:随机删除。

淘汰策略含义

策略含义
volatile-lru根据 LRU 算法删除设置了超时属性(expire)的键,直到腾出足够内存为止。如果没有 可删除的键对象,回退到 noeviction 策略。
allkeys-lru根据 LRU 算法删除键,不管数据有没有设置超时属性,直到腾出足够内存为止。
volatile-lfu在带有过期时间的键中选择最不常用的。
allkeys-lfu在所有的键中选择最不常用的,不管数据有没有设置超时属性。
volatile-random在带有过期时间的键中随机选择。
allkeys-random随机删除所有键,直到腾出足够内存为止。
volatile-ttl根据键值对象的 ttl 属性,删除最近将要过期数据。如果没有,回退到 noeviction 策略。
noeviction默认策略,不会删除任何数据,拒绝所有写入操作并返回客户端错误信息(error)OOM command not allowed when used memory,此时 Redis 只响应读操作。
如果没有符合前提条件的key被淘汰,那么 volatile-lru、volatile-random 、 volatile-ttl相当于noeviction(不做内存回收)。 动态修改淘汰策略:
redis> config set maxmemory-policy volatile-lru

建议使用 volatile-lru,在保证正常服务的情况下,优先删除最近最少使用的 key。

(3)LRU淘汰算法原理

LRU(Least Recently Used),最近最少使用。判断最近被使用的时间,目前最远的数据优先被淘汰。

传统LRU算法文章参考:

https://blog.csdn.net/belongtocode/article/details/102989685

Redis LRU
Redis LRU对传统的LRU算法进行了改良,通过随机采样来调整算法的精度。

如果淘汰策略是LRU,则根据配置的采样值 maxmemory_samples(默认是 5 个), 随机从数据库中选择m个key, 淘汰其中热度最低的key对应的缓存数据。所以采样参数m配置的数值越大, 就越能精确的查找到待淘汰的缓存数据,但是也消耗更多的CPU计算,执行效率降低。

Redis 中所有对象结构都有一个lru字段, 且使用了unsigned 的低 24 位,这个字段用来记录对象的热度。对象被创建时会记录lru值。在被访问的时候也会更新lru的值。但是不是获取系统当前的时间戳,而是设置为全局变量 server.lruclock 的值。

源码server.h

typedef struct redisObject {
	unsigned type:4;
	unsigned encoding:4;
	unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or
	* LFU data (least significant 8 bits frequency
	* and most significant 16 bits access time). */
	int refcount;
	void *ptr;
} robj;

Redis中有个定时处理的函数serverCron, 默认每100毫秒调用函数
updateCachedTime更新一次全局变量的server.lruclock的值,它记录的是当前unix时间戳。

源码:server.c

void updateCachedTime(void) {
	time_t unixtime = time(NULL);
	atomicSet(server.unixtime,unixtime);
	server.mstime = mstime();
	struct tm tm;
	localtime_r(&server.unixtime,&tm);
	server.daylight_active = tm.tm_isdst;
}

之所以不获取精确的时间而是放在全局变量中,是因为更新数据的lru热度值时,就不用每次调用系统函数time,可以提高执行效率。

当对象里面已经有了 LRU 字段的值,就可以评估对象的热度了。函数 estimateObjectIdleTime评估指定对象的lru热度,思想就是对象的lru值和全局的server.lruclock的差值越大(越久没有得到更新), 该对象热度越低。

源码 evict.c

/* Given an object returns the min number of milliseconds the object was never
* requested, using an approximated LRU algorithm. */
unsigned long long estimateObjectIdleTime(robj *o) {
	unsigned long long lruclock = LRU_CLOCK();
	if (lruclock >= o->lru) {
		return (lruclock - o->lru) * LRU_CLOCK_RESOLUTION;
	} else {
		return (lruclock + (LRU_CLOCK_MAX - o->lru)) * LRU_CLOCK_RESOLUTION;
	}
}

server.lruclock只有24位,按秒为单位来表示才能存储194天。当超过24bit 能表示的最大时间的时候,它会从头开始计算。在这种情况下,可能会出现对象的lru大于server.lruclock的情况,如果这种情况出现那么就两个相加而不是相减来求最久的key。

注:Redis之所以不用常规的哈希表+双向链表的方式实现,一个是因为需要额外的数据结构,消耗资源;而Redis LRU算法在sample为10的情况下,已经能接近传统LRU算法了。

(4)LFU淘汰算法原理

LFU(Least Frequently Used),最不常用。判断最近被访问的次数(频率),最小的被淘汰。

源码server.h

typedef struct redisObject {
	unsigned type:4;
	unsigned encoding:4;
	unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or
	* LFU data (least significant 8 bits frequency
	* and most significant 16 bits access time). */
	int refcount;
	void *ptr;
} robj;

当lru字段被用作LFU时,其被分为两部分

  • 高16位用来记录访问时间(单位为分钟,ldt,last decrement time)
  • 低8位用来记录访问频率,简称counter(logc,logistic counter)

counter是用基于概率的对数计数器实现的,8位可以表示百万次的访问频率。对象被读写的时候,lfu的值会被更新。
源码:db.c

void updateLFU(robj *val) {
	unsigned long counter = LFUDecrAndReturn(val);
	counter = LFULogIncr(counter);
	val->lru = (LFUGetTimeInMinutes()<<8) | counter;
}

redis.conf 配置文件

#lfu-log-factor 越大,counter 增长的越慢
# lfu-log-factor 10

#控制counter减少,如果值是 1 的话,N 分钟没有访问就要减少 N。
# lfu-decay-time 1

6、持久化机制

Redis速度快,很大一部分原因是因为它所有的数据都存储在内存中。如果断电或者宕机,都会导致内存中的数据丢失。为了实现重启后数据不丢失,Redis 提供了两种持久化的方案,一种是RDB快照(Redis DataBase),一种是AOF(Append Only File)。

(1)RDB

RDB是Redis默认的持久化方案。当满足一定条件的时候,会把当前内存中的数据写入磁盘,生成一个快照文件dump.rdb。Redis 重启会通过加载 dump.rdb文件恢复数据。

RDB 触发
a)自动触发
配置redis.conf文件

save 900 1 # 900 秒内至少有一个 key 被修改(包括添加)
save 300 10 # 400 秒内至少有 10 个 key 被修改
save 60 10000 # 60 秒内至少有 10000 个 key 被修改

可以配置多个save,不会冲突,只要满足一个就会触发RDB持久化。如果不需要RDB方案,注释save或者配置成空字符串""。

RDB文件位置和目录redis.conf配置:

# 文件路径,文件默认在启动目录下(相对路径)config get dir 获取
dir ./
# 文件名称
dbfilename dump.rdb
# 是否是 LZF 压缩 rdb 文件,开启压缩可以节省存储空间,但是会消耗一些 CPU 的计算时间,默认开启
rdbcompression yes
# 开启数据校验,使用 CRC64 算法来进行数据校验,但是这样做会增加大约 10%的性能消耗,如果希望获取到最大的性能提升,可以关闭此功能。
rdbchecksum yes

RDB持久化自动触发还有:

  • shutdown 触发,保证服务器正常关闭。
  • flushall,RDB 文件是空的,没什么意义

b)手动触发

Redis提供了两条命令用来手动保存RDB快照。

  • save。save在生成快照的时候会阻塞当前Redis服务器, Redis不能处理其他命令。如果内存中的数据比较多,会造成Redis长时间的阻塞。生产环境不建议使用这个命令。
  • bgsave。执行bgsave时,Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求。具体操作是Redis进程执行fork操作创建子进程(copy-on-write),RDB 持久化过程由子进程负责,完成后自动结束。它不会记录 fork 之后后续的命令。阻塞只发生在fork 阶段,一般时间很短。

用astsave命令可以查看最近一次成功生成快照的时间。
注:Redis在启动时会自动加载dir下的dump.rdb文件进行数据恢复。

(2)AOF

AOF(Append Only File),Redis默认不开启。AOF采用日志的形式来记录每个写操作,并追加到文件中。开启后,执行更改Redis数据的命令时,就会把命令写入到AOF文件中。Redis重启时会根据日志文件的内容把写指令从前到后执行一次以完成数据的恢复工作。

AOF配置
redis.conf配置

# 开关,Redis 默认只开启 RDB 持久化,开启 AOF 需要修改为 yes
appendonly no
# 文件名,路径也是通过 dir 参数配置
appendfilename "appendonly.aof"
#AOF 持久化策略(硬盘缓存到磁盘),默认 everysec
appendfsync everysec

appendfsync参数用来配置AOF持久化策略,由于操作系统的缓存机制,AOF数据并没有真正地写入硬盘,而是进入了系统的硬盘缓存。

参数说明
appendfsyncno 表示不执行 fsync,由操作系统保证数据同步到磁盘,速度最快,但是不太安全;
always 表示每次写入都执行 fsync,以保证数据同步到磁盘,效率很低;
everysec 表示每秒执行一次 fsync,可能会导致丢失这 1s 数据。通常选择 everysec ,兼顾安全性和效率。

AOF文件整理
由于AOF持久化是Redis不断将写命令记录到AOF文件中,随着Redis不断的运行,AOF的文件会越来越大,文件越大,占用服务器内存越大以及AOF恢复要求时间越长。

例如:同一个key的多个set命令,只有最后一个set生效就可以了,之前的set可以不用记录。

为了解决这个问题,Redis新增了重写机制,当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集。

使用命令bgrewriteaof来重写。AOF文件重写并不是对原文件进行重新整理,而是直接读取服务器现有的键值对,然后用一条命令去代替之前记录这个键值对的多条命令,生成一个新的文件后去替换原来的AOF文件。

redis.conf配置

# 重写触发机制
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb

no-appendfsync-on-rewrite no
aof-load-truncated yes
参数说明
auto-aof-rewrite-percentage默认值为 100。aof自动重写配置,当目前aof文件大小超过上一次重写的aof文件大小的百分之多少进行重写,即当aof文件增长到一定大小的时候,Redis能够调用bgrewriteaof对日志文件进行重写。当前AOF文件大小是上次日志重写得到AOF文件大小的二倍(设置为100)时,自动启动新的日志重写过程。
auto-aof-rewrite-min-size默认 64M。设置允许重写的最小aof文件大小,避免了达到约定百分比但尺寸仍然很小的情况还要重写。
no-appendfsync-on-rewrite在 aof 重写或者写入 rdb 文件的时候,会执行大量 IO,此时对于 everysec 和 always 的 aof模式来说,执行 fsync 会造成阻塞过长时间,no-appendfsync-on-rewrite 字段设置为默认设置为 no。如果对延迟要求很高的应用,这个字段可以设置为 yes,否则还是设置为 no,这样对持久化特性来说这是更安全的选择。设置为 yes 表示 rewrite 期间对新写操作不 fsync, 暂时存在内存中,等 rewrite 完成后再写入,默认为 no,建议修改为 yes。Linux 的默认 fsync策略是 30 秒。可能丢失 30 秒数据。
aof-load-truncatedaof 文件可能在尾部是不完整的,当 redis 启动的时候,aof 文件的数据被载入内存。重启可能发生在 redis 所在的主机操作系统宕机后,尤其在 ext4 文件系统没有加上 data=ordered选项,出现这种现象。redis 宕机或者异常终止不会造成尾部不完整现象,可以选择让 redis退出,或者导入尽可能多的数据。如果选择的是 yes,当截断的 aof 文件被导入的时候,会自动发布一个 log 给客户端然后 load。如果是 no,用户必须手动 redis-check-aof 修复 AOF文件才可以。默认值为 yes。

=注:重启 Redis 之后就会进行 AOF 文件的恢复。

(3)两种持久化方案选择

RDB与AOF对比:
1、对于具有相同数据的的Redis,RDB通常比AOF文件更小,恢复速度更快。
2、AOF持久化的方法提供了多种的同步频率,即使使用默认的同步频率每秒同步一次,Redis最多也就丢失1秒的数据而已。
3、在高并发的情况下,RDB比AOF具好更好的性能保证。

如果可以忍受一小段时间内数据的丢失,毫无疑问使用RDB是最好的,否则就选择AOF。但是一般情况下建议不要单独使用某一种持久化机制,而是应该两种一起用,在这种情况下,当 redis 重启的时候会优先载入 AOF 文件来恢复原始的数据,因为在通常情况下 AOF 文件保存的数据集要比 RDB 文件保存的数据集要完整。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值