Redis 事务、内存回收、持久化机制

目录

1. 内存回收(默认 惰性过期+定期过期)

1.1 三种key过期策略

1.1.1 定时过期(主动 淘汰 )

1.1.2 惰性过期(被动 淘汰 )

1.1.3 定期过期

1.2 内存淘汰策略(默认 noeviction策略)

1.2.1 redis.conf 最大内存参数设置

1.2.2 淘汰策略分类策略 及含义

1.2.3 LRU 淘汰原理

 2.Redis 事务

2.1 redis 事务特点

2.1.2 事务命令(multi、exec、discard、watch、unwatch)

2.1.3 事物的原子性不可靠

3.Lua 脚本

3.1 lua 命令格式

3.2 Lua 脚本缓存  SHA1 摘要

3.3 Lua 死循环关闭命令 script kill

4. Reids 为什么这么快

4.1 基于内存的KV 结构

4.2 单线程

4.3 异步非阻塞 多路复用IO

5.持久化机制(RDB 、AOF)

5.1 RDB (按量持久)

5.1.1 redis.conf RDB配置

5.1.2 RDB优势和劣势

5.2 AOF 存储

5.2.1 redis.conf AOF配置

5.2.2 AOF 持久化时机

5.2.3 AOF 文件重写

5.2.4 AOF 优势与劣势


1. 内存回收(默认 惰性过期+定期过期)

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

1.1 三种key过期策略

1.1.1 定时过期(主动 淘汰 )

每个设置过期时间的 key 都需要创建一个定时器,到过期时间就会立即清除。该策
略可以立即清除过期的数据,对内存很友好;但是会占用大量的 CPU 资源去处理过期的
数据,从而影响缓存的响应时间和吞吐量。

1.1.2 惰性过期(被动 淘汰 )

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

1.1.3 定期过期

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

1.2 内存淘汰策略(默认 noeviction策略)

Redis 的内存淘汰策略,是指当内存使用达到最大内存极限时,需要使用淘汰算法来
决定清理掉哪些数据,以保证新数据的存入。当达到最大内存是使用策略

1.2.1 redis.conf 最大内存参数设置

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

redis> config set maxmemory 2GB

1.2.2 淘汰策略分类策略 及含义

策略含义
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

1.2.3 LRU 淘汰原理

Redis LRU 对传统的 LRU 算法进行了改良,通过随机采样来调整算法的精度。
如果淘汰策略是 LRU,则根据配置的采样值 maxmemory_samples(默认是 5 个),
随机从数据库中选择 m 个 key, 淘汰其中热度最低的 key 对应的缓存数据。所以采样参
数m配置的数值越大, 就越能精确的查找到待淘汰的缓存数据,但是也消耗更多的CPU计
算,执行效率降低。
Redis 内部数据data 维护了一个时间戳和全局时间戳,采用更新data时间抽对比全局时间戳进行淘汰,每100ms更新一次全局时间戳,而不是实时更新,用来提高执行效率。

 2.Redis 事务

2.1 redis 事务特点

1、按进入队列的顺序执行。
2、不会受到其他客户端的请求的影响。
3、事务不能嵌套,多个 multi 命令效果一样。

2.1.2 事务命令(multi、exec、discard、watch、unwatch

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

事务操作示例1 :

127.0.0.1:6379> set tom 1000
OK
127.0.0.1:6379> set mic 1000
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby tom 100
QUEUED
127.0.0.1:6379> incrby mic 100
QUEUED
#127.0.0.1:6379> discard 取消事务
127.0.0.1:6379> exec 

事务操作示例2 watch

127.0.0.1:6379> set tom 1000
OK
127.0.0.1:6379> watch balance
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby tom 100
QUEUED

#另一个客户端执行了 修改操作
#127.0.0.1:6379> decrby tom 500
#(integer) 900

#因为监听到了这个key被修改 所以事务执行失败
127.0.0.1:6379> exec 
(nil)
127.0.0.1:6379> get balance
"500"

2.1.3 事物的原子性不可靠

1.在执行 exec 之前 发生错误 
所有操作都会回滚

2.在执行 exec 之后 发生错误
操作正确的命令执行成功,有误的命令执行失败,违反了事物的原子性。事务没有回滚,所以采用Lua脚本进行改良。

3.Lua 脚本

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

3.1 lua 命令格式

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

例子1:设置 key =  lua-key , value = lua-value

redis> eval "redis.call('set',KEYS[1],ARGV[1])" 1 lua-key lua-value

 例子2:通过lua 文件执行脚本
在reids src下次 创建lua 脚本 
 

redis> vim test.lua
#设置命令
redis.call('set',KEYS[1],ARGV[1])
return redis.call('get',KEYS[1])


#执行
redis> redis-cli --eval testLua.lua 1 abc 123
redis> 123

3.2 Lua 脚本缓存  SHA1 摘要

因为每次调用脚本都需要把整个脚本传给 Redis 服务端会产生比较大的网络开销。为了解决这个问题,Redis 提供了 EVALSHA 命令,允许开发者通过脚本内容的 SHA1 摘要来执行脚本。

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

127.0.0.1:6379> evalsha "470877a599ac74fbfda41caa908de682c5fc7d4b" 0
"Hello World"

3.3 Lua 死循环关闭命令 script kill

 为了防止某个脚本执行时间过长导致 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. Reids 为什么这么快

4.1 基于内存的KV 结构

KV 结构的内存数据库,时间复杂度 O(1)。

4.2 单线程

1、没有创建线程、销毁线程带来的消耗
2、避免了上线文切换导致的 CPU 消耗(程序计数器和CPU寄存器相关)

在每个任务运行前,CPU 都需要知道任务从哪里加载、又从哪里开始运行,也就是
说,需要系统事先帮它设置好 CPU 寄存器和程序计数器(Program Counter),这个叫做
CPU 的上下文。
而这些保存下来的上下文,会存储在系统内核中,并在任务重新调度执行时再次加
载进来。这样就能保证任务原来的状态不受影响,让任务看起来还是连续运行。
在切换上下文的时候,需要完成一系列的工作,这是一个很消耗资源的操作。


3、避免了线程之间带来的竞争问题,例如加锁释放锁死锁等等

4.3 异步非阻塞 多路复用IO

异步非阻塞 I/O,多路复用处理并发连接。

/O 指的是网络 I/O。
多路指的是多个 TCP 连接(Socket 或 Channel)。
复用指的是复用一个或多个线程。
它的基本原理就是不再由应用程序自己监视连接,而是由内核替应用程序监视文件
描述符。

5.持久化机制(RDB 、AOF)

5.1 RDB (按量持久)

RDB 是 Redis 默认的持久化方案。当满足一定条件的时候,会把当前内存中的数
据写入磁盘,生成一个快照文件 dump.rdb。Redis 重启会通过加载 dump.rdb 文件恢
复数据。
触发时机
1.执行shutdown命令也会触发RDB存储
2.根据配置文件save配置进行存储

5.1.1 redis.conf RDB配置

save 900 1 # 900 秒内至少有一个 key 被修改(包括添加)
save 300 10 # 400 秒内至少有 10 个 key 被修改
save 60 10000 # 60 秒内至少有 10000 个 key 被修改
# 文件路径,
dir ./
# 文件名称
dbfilename dump.rdb
# 是否是 LZF 压缩 rdb 文件
rdbcompression yes
# 开启数据校验 使用 CRC64 算法来进行数据校验,但是这样做会增加大约 10%的性能消耗,如果希望获取到最
大的性能提升,可以关闭此功能。
rdbchecksum yes

5.1.2 RDB优势和劣势

一、优势
1.RDB 是一个非常紧凑(compact)的文件,它保存了 redis 在某个时间点上的数据
集。这种文件非常适合用于进行备份和灾难恢复。
2.生成 RDB 文件的时候,redis 主进程会 fork()一个子进程来处理所有保存工作,主
进程不需要进行任何磁盘 IO 操作。
3.RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。
二、劣势
1、RDB 方式数据没办法做到实时持久化/秒级持久化。因为 bgsave 每次运行都要
执行 fork 操作创建子进程,频繁执行成本过高。
2、在一定间隔时间做一次备份,所以如果 redis 意外 down 掉的话,就会丢失最后
一次快照之后的所有修改(数据有丢失)。
如果数据相对来说比较重要,希望将损失降到最小,则可以使用 AOF 方式进行持久
化。

5.2 AOF 存储

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

5.2.1 redis.conf AOF配置

# 开关 Redis 默认只开启 RDB 持久化,开启 AOF 需要修改为 yes
appendonly yes
# 文件名 #路径也是通过 dir 参数配置 config get dir
appendfilename "appendonly.aof"

5.2.2 AOF 持久化时机

配置文件 appendfsync everysec

AOF 持久化策略(硬盘缓存到磁盘),默认 everysec
no 表示不执行 fsync,由操作系统保证数据同步到磁盘,速度最快,但是不太安全;
always 表示每次写入都执行 fsync,以保证数据同步到磁盘,效率很低;
everysec 表示每秒执行一次 fsync,可能会导致丢失这 1s 数据。通常选择 everysec ,
兼顾安全性和效率。

5.2.3 AOF 文件重写

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

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

#默认值为 100。aof 自动重写配置,当目前 aof 文件大小超过上一次重写的 aof 文件大小的
百分之多少进行重写,即当 aof 文件增长到一定大小的时候,Redis 能够调用 bgrewriteaof
对日志文件进行重写。当前 AOF 文件大小是上次日志重写得到 AOF 文件大小的二倍(设
置为 100)时,自动启动新的日志重写过程。

#默认 64M。设置允许重写的最小 aof 文件大小,避免了达到约定百分比但尺寸仍然很小的
情况还要重写。

5.2.4 AOF 优势与劣势

优点
1、AOF 持久化的方法提供了多种的同步频率,即使使用默认的同步频率每秒同步
一次,Redis 最多也就丢失 1 秒的数据而已。
缺点:
1、对于具有相同数据的的 Redis,AOF 文件通常会比 RDF 文件体积更大(RDB
存的是数据快照)。
2、虽然 AOF 提供了多种同步的频率,默认情况下,每秒同步一次的频率也具有较
高的性能。在高并发的情况下,RDB 比 AOF 具好更好的性能保证。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值