Redis系列-第四篇持久化与事务
一、持久化
Redis是一个内存数据库,为了保证数据的持久性,它提供了两种持久化方案:
-
RDB方式(默认)
-
AOF方式
持久化功能有效地避免因进程退出造成的数据丢失问题, 当下次重启时利用之前持久化的文件即可实现数
据恢复 。
1.RDB
1.1介绍
-
RDB是Redis默认采用的持久化方式。
-
RDB方式是通过快照(snapshotting)完成的,当符合一定条件时Redis会自动将内存中的数据进行快照并持久化到硬盘。
-
Redis会在指定的情况下触发快照(分为手动触发和自动触发)
- 1. 符合自定义配置的快照规则(自动,在redis.conf中配置的规则)
- 2. 执行save或者bgsave命令(手动)
- 3. 执行flushall命令(手动)
- 4. 如果从节点执行全量复制操作, 主节点自动执行bgsave生成RDB并发送给从节点(自动)
- 5. 默认情况下执行shutdown命令时, 如果没有开启AOF持久化功能则自动执行bgsave(自动)
- 6. 执行debug reload命令重新加载Redis时, 也会自动触发save操作 (自动)
save命令 :阻塞当前Redis服务器, 直到RDB过程完成为止, 对于内存比较大的实例会造成长时间阻塞, 线上环境不建议使用 .
bgsave命令: Redis进程执行fork操作创建子进程, RDB持久化过程由子进程负责, 完成后自动>结束。 阻塞只发生在fork阶段, 一般时间很短。
在redis.conf中设置自定义快照规则
1. RDB持久化条件
格式:save
示例:
save 900 1 : 表示15分钟(900秒钟)内至少1个键被更改则进行快照。
save 300 10 : 表示5分钟(300秒)内至少10个键被更改则进行快照。
save 60 10000 :表示1分钟内至少10000个键被更改则进行快照。
可以配置多个条件(每行配置一个条件),每个条件之间是“或”的关系。
2. 配置dir指定rdb快照文件的位置
dir ./
3. 配置dbfilename指定rdb快照文件的名称
dump.rdb
特别说明:
-
Redis启动后会读取RDB快照文件,将数据从硬盘载入到内存。
-
根据数据量大小与结构和服务器性能不同,这个时间也不同。通常将记录一千万个字符串类型键、大小为1GB的快照文件载入到内存中需要花费20~30秒钟。
1.2快照的实现原理
- 快照过程
- redis使用fork函数复制一份当前进程的副本(子进程)
- 父进程继续接收并处理客户端发来的命令,而子进程开始将内存中的数据写入硬盘中的临时文件。
- 当子进程写入完所有数据后会用该临时文件替换旧的RDB文件,至此,一次快照操作完成。
- 注意事项
1.redis在进行快照的过程中不会修改RDB文件,只有快照结束后才会将旧的文件替换成新的,也就是说任何时 候RDB文件都是完整的。
2.这就使得我们可以通过定时备份RDB文件来实现redis数据库的备份, RDB文件是经过压缩的二进制文件,占用的空间会小于内存中的数据,更加利于传输。
1.3RDB优缺点
RDB的缺点:
- RDB方式数据没办法做到实时持久化/秒级持久化。 因为bgsave每次运行都要执行fork操作创建子进程, 属于重量级操作, 频繁执行成本过高。
- RDB文件使用特定二进制格式保存, Redis版本演进过程中有多个格式的RDB版本, 存在老版本Redis服务无法兼容新版RDB格式的问题。针对RDB不适合实时持久化的问题, Redis提供了AOF持久化方式来解决。
RDB的优点:
- RDB是一个紧凑压缩的二进制文件, 代表Redis在某个时间点上的数据快照。 非常适用于备份, 全量复制等场景。 比如每6小时执行bgsave备份,并把RDB文件拷贝到远程机器或者文件系统中(如hdfs) , 用于灾难恢复。
- Redis加载RDB恢复数据远远快于AOF的方式。
2.AOF
2.1介绍
-
默认情况下Redis没有开启AOF(append only file)方式的持久化
-
开启AOF持久化后每执行一条会更改Redis中的数据的命令,Redis就会将该命令写入硬盘中的AOF文件,这一过程显然会降低Redis的性能,但大部分情况下这个影响是能够接受的,另外使用较快的硬盘可以提高AOF的性能。
-
可以通过修改redis.conf配置文件中的appendonly参数开启
appendonly yes
- AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的
dir ./
- 默认的文件名是appendonly.aof,可以通过appendfilename参数修改:
appendfilename appendonly.aof
2.2重写原理
-
Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写
-
重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。
-
整个重写操作是绝对安全的,因为Redis 在创建新 AOF 文件的过程中,会继续将命令追加到现有的 AOF 文件里面,即使重写过程中发生停机,现有的 AOF 文件也不会丢失。 而一旦新 AOF 文件创建完毕,Redis 就会从AOF文件切换到新AOF 文件,并开始对新 AOF 文件**进行追加操作。
-
重写后的AOF文件为什么可以变小 :
-
进程内已经超时的数据不再写入文件。
-
旧的AOF文件含有无效命令
-
多条写命令可以合并为一个
-
-
AOF重写过程可以手动触发和自动触发:
- 手动触发: 直接调用bgrewriteaof命令。
-
自动触发: 根据auto-aof-rewrite-min-size和auto-aof-rewrite-percentage参
数确定自动触发时机。
参数说明
auto-aof-rewrite-percentage 100 表示当前aof文件大小超过上一次aof文件大小的百分之多少的时候会进行重写。如果之前没有重写过,以启动时aof文件大小为准
auto-aof-rewrite-min-size 64mb 限制允许重写最小aof文件大小,也就是文件大小小于64mb的时候,不需要进行优化
2.3同步磁盘数据
Redis每次更改数据的时候, aof机制都会将命令记录到aof文件,但是实际上由于操作系统的缓存机制,数据并没有实时写入到硬盘,而是进入硬盘缓存。再通过硬盘缓存机制去刷新到保存到文件。
参数说明:
配置值 | 说明 |
---|---|
always | 每次执行写入都会进行同步 , 这个是最安全但是是效率比较低的方式 |
everysec | 每一秒执行 |
no | 不主动进行同步操作,由操作系统去执行,这个是最快但是最不安全的方式 |
2.4重启加载
2.5 AOF文件损坏以后如何修复
服务器可能在程序正在对 AOF 文件进行写入时停机, 如果停机造成了 AOF 文件出错(corrupt), 那么 Redis 在重启时会拒绝载入这个 AOF 文件, 从而确保数据的一致性不会被破坏。
当发生这种情况时, 可以用以下方法来修复出错的 AOF 文件:
-
为现有的 AOF 文件创建一个备份。
-
使用 Redis 附带的 redis-check-aof 程序,对原来的 AOF 文件进行修复。redis-check-aof --fix readonly.aof
-
重启 Redis 服务器,等待服务器载入修复后的 AOF 文件,并进行数据恢复。
2.6如何选择RDB和AOF
- 一般来说,如果对数据的安全性要求非常高的话,应该同时使用两种持久化功能。
- 如果可以承受数分钟以内的数据丢失,那么可以只使用 RDB 持久化。
- 有很多用户都只使用 AOF 持久化, 但并不推荐这种方式: 因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快 。
- 两种持久化策略可以同时使用,也可以使用其中一种。如果同时使用的话, 那么Redis重启时,会优先使用AOF文件来还原数据
二、事务
1.Redis事务介绍
为了保证多条命令组合的原子性, Redis提供了简单的事务功能以及集成Lua脚本来解决这个问题。事务表示一组动作, 要么全部执行, 要么全部不执行 。
- Redis的事务是通过MULTI,EXEC,DISCARD和WATCH这四个命令来完成的。
- Redis的单个命令都是原子性的,所以这里确保事务性的对象是命令集合。
- Redis将命令集合序列化并确保处于同一事务的命令集合连续且不被打断的执行
- Redis不支持回滚操作
2.基本命令
-
MULTI
用于标记事务块的开始。
Redis会将后续的命令逐个放入队列中,然后使用EXEC命令原子化地执行这个命令序列。
语法:multi
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set s1 11
QUEUED
127.0.0.1:6379> set s2 s2
QUEUED
可以看到set命令此时的返回结果是QUEUED, 代表命令并没有真正执行, 而是暂时保存在Redis中 .如果执行以下命令结果如下:
127.0.0.1:6379> EXISTS s1
(integer) 0
127.0.0.1:6379> get s1
(nil)
只有当exec执行后 ,才会真正添加到redis中。
-
EXEC
在一个事务中执行所有先前放入队列的命令,然后恢复正常的连接状态
语法:exec
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set s1 11
QUEUED
127.0.0.1:6379> set s2 s2
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) OK
因为执行了两个set命令,所以执行exec返回两条数据。
-
DISCARD
清除所有先前在一个事务中放入队列的命令,然后恢复正常的连接状态。
语法:discard
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set s3 333
QUEUED
127.0.0.1:6379> set s4 444
QUEUED
127.0.0.1:6379> DISCARD
OK
3. 事务失败处理
3.1 Redis语法错误(可以理解为编译期错误)
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set s3 333
QUEUED
127.0.0.1:6379> set s4
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379> EXEC
(error) EXECABORT Transaction discarded because of previous errors.
上面执行set命令少一个value,导致事务无法提交。
3.2 Redis类型错误(可以理解为运行期错误)
这种就是运行时命令, 因为语法是正确的 。
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> set s3 333
QUEUED
127.0.0.1:6379> LPUSH s3 444 555
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
从上面可以看出,redis事务是不支持回滚的。exec执行,返回了一个OK,一个异常。说明set s3 333已经执行成功了。
127.0.0.1:6379> get s3
"333"
4.监控命令
-
WATCH
当某个事务需要按条件执行时,就要使用这个命令将给定的键设置为受监控的状态。
语法:watch key [key…]
注意事项:使用该命令可以实现redis的乐观锁。
-
UNWATCH
清除所有先前为一个事务监控的键。
语法:unwatch
有些应用场景需要在事务之前, 确保事务中的key没有被其他客户端修改过, 才执行事务, 否则不执行(类似乐观锁) 。 Redis提供了watch命令来解决这类问题 。
客户端1 | 客户端2 |
---|---|
客户端1 执行完MULTI 命令后执行 APPED s5 world命令。
然后客户端2,在在执行APPED s5 java命令。
然后客户端1,在执行APPED s5 vue命令。
然后客户端1,在执行EXEC命令。
因为客户端1 watch的值已经发生改变,所以导致事务不会提交,返回nil.
Redis提供了简单的事务, 之所以说它简单, 主要是因为它不支持事务中的回滚特性
微信公众号