Redis-04-Redis的持久化和事务

01,Redis的持久化之(RDB)

  • RDB(Redis DataBase)

1.1,简介

Redis的持久化指的是:

  • 在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里
  • Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到 一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。
  • 整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能。
  • 如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。
  • RDB的缺点是最后一次持久化后的数据可能丢失(原因:分片保存的时候最后一次没有来得及保存就退出了才会出现这种情况)
ForkFork的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、
环境变量、程序计数器等) 数值都和原进程一致,但是是一个全新的进程,
并作为原进程的子进程

rdb 保存的是dump.rdb文件

相关配置在配置文件的位置 ,redis.conf中的SNAPSHOTTING

1.2,如何触发RDB

RDB的触发有两种方式:一种是手动(使用命令save),一种是自动(save m n)

  • 自动: 默认的RDB快照触发机制(可以在redis.conf中的SNAPSHOTTING中查看)
1分钟内改动了1万次
5分钟内改动了10万次
15分钟内改动了1次

以上都可以触发RDB快照操作,将内存中的内容持久化到硬盘中

默认快照的位置为,启动redis服务器的当前目录下,如果把redis的服
务器在Linux中配置了环境变量,这时候还是要注意的,如果在任意目录
下启动redis服务器,将会导致RDB快照很乱

默认的名字为:dump.rdb

为了防止出现误操作或者硬件问题,我们一般会把持久化后的快照在重新
备份一份到其他的硬盘

命令:cp dump.rdb dump_new.rdb
  • 手动: 命令save或者是bgsave
如果出现某个数据非常重要,无法等到自动备份,这时候我们可以通过这
个命令来进行手动备份

save:save时只管保存,其它不管,全部阻塞
bgsave:Redis会在后台异步进行快照操作, 快照同时还可以响应客户
端请求,可以通过lastsave 命令获取最后一次成功执行快照的时间
  • 注意:执行flushall命令的时候会产生dump.rdb文件,但是会先清空,后生成,里面是空的,无意义,前面介绍的多次备份也是为了防止这个问题

1.3,如何恢复RDB

  • 将备份文件 (dump.rdb) 移动到启动Redis服务的目录即可
  • 通过下面的命令可以获得Redis服务启动的目录
$ CONFIG GET dir

1.4,优缺点

优势

  • 适合大规模的数据恢复
  • 对数据完整性和一致性要求不高

劣势

  • 在一定间隔时间做一次备份,所以如果Redis服务器意外down掉的话,就会丢失最后一次快照后的所有修改
  • Fork的时候,内存中的数据被克隆了一份,会占用更多的内存空间

1.5,如何停止RDB

动态所有停止RDB保存规则的方法,使用下面的命令:

CONFIG SET save ""

1.6,小结

  • RDB是一个非常紧凑的文件。
  • RDB在保存RDB文件时父进程唯一需要做的就是fork出一个子进程,接下来的工作全部由子进程来做,父进程不需要再做其他I0操作,所以RDB持久化方式可以最大化redis的性能。
  • 与AOF相比,在恢复大的数据集的时候,RDB方式会更快一一些。
    数据丢失风险大。
  • RDB需要经常fork子进程来保存数据集到硬盘上,当数据集比较大的时候fork的过程是非常耗时间的,可能会导致Redis在一些毫秒级不能回应客户端请求

02,Redis持久化之AOF

  • AOF(Append Only File)

2.1,简介

  • 以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录)
  • 只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据
  • 换言之,redis 重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作

相关配置在配置文件的位置中的,APPEND ONLY MODE

AOF默认保存的是appendonly.aof文件(在配置文件可修改文件名)

2.2,如何触发AOF

  • AOF的启动
在配置文件的APPEND ONLY MODE中,AOF的开启默认是关闭的:
appendonly  on

开启的时候修改为yes即可:
appendonly yes

默认保存的文件形式为:
appendonly.aof

可以修改默认文件名字:
CONFIG SET appendfilename  "文件名"

  • 有关AOF的配置执行的保存策略
Appendfsync:
	always:同步持久化,每次发生数据变更都会被立即记录到磁盘,
			性能较差,但是数据的完整性很好
	Everysec:出厂默认推荐,异步操作,每条记录,每秒记录,如果
			一秒内出现数据库宕机没可能会丢失部分数据
	no:从不同步			
  • 注意:这个FLASHALL命令,此命令为一个写命令,在AOF文件中也会被记录进去,最后恢复的时候还是会清空数据库

2.3,如何恢复AOF

AOF的恢复和RDB的恢复有点区别:

  • RDB的恢复,直接把RDB文件里面的内容读进来就可以了
  • AOF文件里面存放的都是指令,如果那条指令存入时有问题了,或者被人为修改了,恢复的时候就会出问题

如果AOF和RDB文件共存,恢复的时候会先去读取AOF文件

正常恢复:

  • 将备份文件 (appendonly.aof) 移动到启动Redis服务的目录即可

异常恢复:

  • 第一步:先备份被写坏的AOF文件
  • 第二步:修复
在redis的客户端执行命令:redis-check-aof --fix  appendonly.aof

此命令对文件进行修复,将AOF文件中不符合规范的所有命令删除,然后再重
启redis服务器进行加载

2.4、Rewrite

Rewrite:

  • AOF采用文件追加方式,文件会越来越大。为避免出现此种情况,新增了重写机制
  • 当AOF文件的大小超过所设定的阈值时,Redis就会启动AOF文件的内容压缩, 只保留可以恢复数据的最小指令集。
  • 使用命令开启:bgrewriteaof

Rewrite原理:

  • AOF文件持续增长而过大时,会fork出一条新进程来将文件重写(也是先写临时文件最后再rename), 遍历新进程的内存中数据,每条记录有一条的Set语句。
  • 重写aof文件的操作,并没有读取旧的aof文件, 而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似

触发机制:

  • Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍或者文件大于64M时触发

1.5,优缺点

优势

  • 每修改同步:appendfsync always 同步持久化 每次发生数据变更会被立即记录到磁盘 性能较差但数据完整性比较好
  • 每秒同步:appendfsync everysec 异步操作,每秒记录 如果一秒内宕机,有数据丢失
  • 不同步:appendfsync no 从不同步

劣势

  • 相同数据集的数据而言AOF文件要远大于RDB文件,恢复速度慢于RDB
  • AOF运行效率要慢于RDB,每秒同步策略效率较好,不同步效率和RDB相同

1.6,小结

  • AOF文件和RDB文件可以共存,当两者共存的时候,服务器重启也只会读取AOF文件,尽管这样还是建议两者都是用,以防万一
  • AOF文件时一个只进行追加的日志文件
  • Redis可以在AOF文件体积变得过大时,自动地在后台对AOF进行重写
  • AOF文件有序地保存了对数据库执行的所有写入操作,这些写入操作以Redis协议的格式保存,因此AOF文件的内容非常容易被人读懂,对文件进行分析也很轻松
  • 对于相同的数据集来说,AOF文件的体积通常要大于RDB文件的体积
  • 根据所使用的appendfsync策略,AOF的速度可能会慢于RDB

03,Redis的事务(Transactions)

3.1,简介

事务可以做什么:

  • 当一次执行多个命令,本质是一组命令的集合,一个事务中的所有命令都会序列化,按顺序地串行化执行而不会被其它命令插入,不许加塞,类似于synchronsized锁一样。
  • 一个队列中,一次性、顺序性、排他性的执行一系列命令

注意:Redis的事务不是原子性

在redis中,对于一个存在问题的命令,如果在入队的时候就已经出错,整个事务内的命令将都不会被执行(其后续的命令依然可以入队),如果这个错误命令在入队的时候并没有报错,而是在执行的时候出错了,那么redis默认跳过这个命令执行后续命令。也就是说,redis只实现了部分事务

3.2,常用命令

命令描述
MULTI标记一个事务块的开始。
EXEC执行所有事务块内的命令
DISCARD取消事务,放弃执行事务块内的所有命令。
UNWATCH取消 WATCH 命令对所有 key 的监视。
WATCH key [key …]监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断

3.2,操作演示

3.2.1, MULTI,EXEC,DISCARD

MULTI,EXEC,DISCARD 用法:

  • 使用MULTI命令输入Redis事务,该命令始终以OK答复
  • 此时,用户可以发出多个命令,Redis不会执行这些命令,而是将它们排队,使用QUEUED答复。
  • 调用EXEC,将执行所有命令
  • 调用DISCARD,将刷新事务队列并退出事务

演示:

  • 正常执行:
122.1.0.1:6379> MULTI
OK
122.1.0.1:6379> SET k2 1
QUEUED
122.1.0.1:6379> INCR k2
QUEUED
122.1.0.1:6379> EXEC
1) OK
2) (integer) 2
  • 放弃事务:
122.1.0.1:6379> MULTI
OK
122.1.0.1:6379> SET k3 1
QUEUED
122.1.0.1:6379> INCR k3
QUEUED
122.1.0.1:6379> DISCARD
OK
  • 事务中出现错误(入队的时候就出错了),全体失败:
122.1.0.1:6379> MULTI
OK
122.1.0.1:6379> SET k4 1
QUEUED
122.1.0.1:6379> INCR k4  1
(error) ERR wrong number of arguments for 'incr' command
122.1.0.1:6379> EXEC
(error) EXECABORT Transaction discarded because of previous errors.
  • 事务中出现错误(入队的时候并没有报错),错误语句执行失败,事务继续执行:
122.1.0.1:6379> MULTI
OK
122.1.0.1:6379> SET k5 1
QUEUED
122.1.0.1:6379> INCR k5
QUEUED
122.1.0.1:6379> SET k6 123456@qq.com
QUEUED
122.1.0.1:6379> INCR k6
QUEUED
122.1.0.1:6379> EXEC
1) OK
2) (integer) 2
3) OK
4) (error) ERR value is not an integer or out of range
3.2.2,监控(WATCH ,UNWATCH)

悲观锁:

  • 悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁

乐观锁:

  • 乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量
  • 乐观锁策略:提交版本必须大于记录当前版本才能执行更新

CAS(Check And Set):

  • 比较然后设置,自旋锁

WATCH ,UNWATCH用法:

  • 用于为Redis事务提供检查
  • 监视设定被监视的键,以便检测对它们的更改
  • 如果在EXEC命令之前修改了监视键,则整个事务将中止,并且EXEC返回NULL答复以通知该事务失败
  • 被WATCH监视的键,就像被加了一把乐观锁

演示(我们使用银行转账的例子来进行说明):

  • 初始化转账账户:account_send和收款账户:account_accept
122.1.0.1:6379> SET account_send 1000
OK
122.1.0.1:6379> SET account_accept 1000
OK
  • 正常执行情况(先监控,在开事务)
开启监听account_send
122.1.0.1:6379> WATCH account_send
OK
开启事务
122.1.0.1:6379> MULTI 
OK
122.1.0.1:6379> DECRBY account_send 100
QUEUED
122.1.0.1:6379> INCRBY account_accept 100
QUEUED
122.1.0.1:6379> EXEC
1) (integer) 900
2) (integer) 1100
  • 出现被人篡改的情况
线程A:

开启监听account_send
122.1.0.1:6379> WATCH account_send
OK
开启事务
122.1.0.1:6379> MULTI 
OK
122.1.0.1:6379> INCRBY account_accept 100
QUEUED
122.1.0.1:6379> DECRBY account_send 100
QUEUED
122.1.0.1:6379> EXEC
(nil)
线程B:
线程B在线程A开启监听和事务之后,执行EXEC之前,执行下面的操作篡改
account_send的值,导致线程A执行失败

123.1.0.1:6379> DECRBY  account_send 100
(integer) 800
123.1.0.1:6379> INCRBY account_accept 100
(integer) 1200
原因分析:
因为在redis中开启监听之后,就相当于给这个key加了一个锁(类似乐观锁),
线程A去修改account_send的时候会拿到一个版本号,假设为1,
但是线程A在事务还没提交的时候,线程B也去修改account_send的值,
此时也会拿到一个版本号,假设为1(因为线程A为提交),线程B执行结束,
修改版本号为2,这是线程A开始执行事务中的代码,发现版本号为2,
不是之前拿到的1,导致失败
  • 注意:一旦执行了EXEC,之前加的监控会被取消掉(一次性)
3.2.3,Redis事务的三阶段三特性

三阶段:

  • 开启:以MULTI开始一个事务
  • 入队:将多个命令入队到事务中,接到这些命令并不会立即执行,而是放到等待执行的事务队列里面
  • 执行:由EXEC命令触发事务

三特性

  • 单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中不会被其他客户端发送来的命令请求所打断
  • 没有隔离级别的概念:队列中的命令没有提交之前都不会实际的被执行,因为事务提交前任何指令都不会被实际执行, 也就不存在事务内的查询要看到事务里的更新,在事务外查询不能看到这个让人万分头痛的问题
  • 不保证原子性:redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚。

总结:不遵循传统的ACID中的AI(原子性,隔离性)

3.2.4,小结
  • Watch指令,类似乐观锁,事务提交时,如果Key的值已被别的客户端改变, 比如某个list已被别的客户端push/pop过了,整个事务队列都不会被执行
  • 通过WATCH命令在事务执行之前监控了多个Keys,倘若在WATCH之后有任何Key的值发生了变化, EXEC命令执行的事务都将被放弃,同时返回Null应答以通知调用者事务执行失败
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

彤彤的小跟班

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值