持久化

1 RDB


R edis D ata B ase


  • 在指定的时间间隔内,将内存中的数据集的快照写入磁盘;
  • 默认保存在/usr/local/bin中,文件名dump.rdb;

1.1 自动备份

  • redis是内存数据库,当我们每次用完redis,关闭linux时,按道理来说,内存释放,redis中的数
  • 据也会随之消失
  • 为什么我们再次启动redis的时候,昨天的数据还在,并没有消失呢?
  • 正是因为,每次关机时,redis会自动将数据备份到一个文件中 :/usr/local/bin/dump.rdb
  • 接下来就全方位的认识 自动备份机制

1. 默认的自动备份策略不利于我们测试,所以修改 redis.conf 文件中的自动备份策略


vim redis.conf 
/SNAP # 搜索 
save 900 1 # 900秒内,至少变更1次,才会自动备份 
save 120 10 # 120秒内,至少变更10次,才会自动备份 
save 60 10000 # 60秒内,至少变更10000次,才会自动备份


当然如果只是用 Redis 的缓存功能,不需要持久化,那么你可以注释掉所有的 save 行来停用保存功能。可以直接一个空字符串来实现停用: save ""


 


2. 使用 shutdown 模拟关机 ,关机之前和关机之后,对比 dump.rdb 文件的更新时间


 


注意:当我们使用 shutdown 命令, redis 会自动将数据库备份,所以, dump.rdb 文件创建时间更新了


 


3. 开机启动 redis ,我们要在 120 秒内保存 10 条数据,再查看 dump.rdb 文件的更新时间(开两个终端窗口,方便查看)


 


4. 120 秒内保存 10 条数据这一动作触发了备份指令,目前, dump.rdb 文件中保存了 10 条数据,将dump.rdb拷贝一份 dump10.rdb ,此时两个文件中都保存 10 条数据


 


5. 既然有数据已经备份了,那我们就肆无忌惮的将数据全部删除 flushall ,再次 shutdown 关机


 


6. 再次启动 redis ,发现数据真的消失了,并没有按照我们所想的 将 dump.rdb 文件中的内容恢复到redis中。为什么?


 


因为,当我们保存10条以上的数据时,数据备份起来了;

然后删除数据库,备份文件中的数据,也没问题;

但是,问题出在shutdown上,这个命令一旦执行,就会立刻备份,将删除之后的空数据库

生成备份文件,将之前装10条数据的备份文件覆盖掉了。所以,就出现了上图的结果。自动

恢复失败。

怎么解决这个问题呢?要将备份文件再备份


7. 将 dump.rdb 文件删除,将 dump10.rdb 重命名为 dump.rdb


 


8. 启动 redis 服务,登录 redis ,数据 10 条,全部恢复!


 


1.2 手动备份

  • 之前自动备份,必须更改好多数据,例如上边,我们改变了十多条数据,才会自动备份;
  • 现在,我只保存一条数据,就想立刻备份,应该怎么做?
  • 每次操作完成,执行命令 save 就会立刻备份

1.3 与RDB相关的配置

  • stop-writes-on-bgsave-error:进水口和出水口,出水口发生故障与否
  • yes:当后台备份时候反生错误,前台停止写入
  • no:不管死活,就是往里怼
  • rdbcompression:对于存储到磁盘中的快照,是否启动LZF压缩算法,一般都会启动,因为这点性能,多买一台电脑,完全搞定N个来回了。
  • yes:启动
  • no:不启动(不想消耗CPU资源,可关闭)
  • rdbchecksum:在存储快照后,是否启动CRC64算法进行数据校验;
  • 开启后,大约增加10%左右的CPU消耗;
  • 如果希望获得最大的性能提升,可以选择关闭;
  • dbfilename:快照备份文件名字
  • dir:快照备份文件保存的目录,默认为当前目录

优势 and 劣势


  • 优:适合大规模数据恢复,对数据完整性和一致行要求不高;
  • 劣:一定间隔备份一次,意外down掉,就失去最后一次快照的所有修改

2 AOF


A ppend O nly F ile


  • 以日志的形式记录每个写操作;
  • 将redis执行过的写指令全部记录下来(读操作不记录);
  • 只许追加文件,不可以改写文件;
  • redis在启动之初会读取该文件从头到尾执行一遍,这样来重新构建数据;

2.1 开启AOF

1. 为了避免失误,最好将redis.conf总配置文件备份一下,然后再修改内容如下:

appendonly yes 
appendfilename appendonly.aof

2. 重新启动redis,以新配置文件启动

redis-server /usr/local/redis5.0.4/redis.conf

3. 连接redis,加数据,删库,退出


4. 查看当前文件夹多一个 aof 文件,看看文件中的内容 , 保存的都是 操作


  • 文件中最后一句要删除,否则数据恢复不了
  • 编辑这个文件,最后要 :wq! 强制执行

5.只需要重新连接,数据恢复成功

2.2 共存?谁优先?


我们查看 redis.conf 文件, AOF 和 RDB 两种备份策略可以同时开启,那系统会怎样选择?


 


1. 动手试试,编辑appendonly.aof,胡搞乱码,保存退出

2. 启动redis 失败,所以是AOF优先载入来恢复原始数据!因为AOF比RDB数据保存的完整性更高!

3. 修复AOF文件,杀光不符合redis语法规范的代码

reids-check-aof --fix appendonly.aof

2.3 与AOF相关的配置

  • appendonly:开启aof模式
  • appendfilename:aof的文件名字,最好别改!
  • appendfsync:追写策略
  • always:每次数据变更,就会立即记录到磁盘,性能较差,但数据完整性好
  • everysec:默认设置,异步操作,每秒记录,如果一秒内宕机,会有数据丢失
  • no:不追写
  • no-appendfsync-on-rewrite:重写时是否运用Appendfsync追写策略;用默认no即可,保证数据安全性。
  • AOF采用文件追加的方式,文件会越来越大,为了解决这个问题,增加了重写机制,redis会自动记录上一次AOF文件的大小,当AOF文件大小达到预先设定的大小时,redis就会启动AOF文件进行内容压缩,只保留可以恢复数据的最小指令集合
  • auto-aof-rewrite-percentage:如果AOF文件大小已经超过原来的100%,也就是一倍,才重写压缩
  • auto-aof-rewrite-min-size:如果AOF文件已经超过了64mb,才重写压缩

3 总结(如何选择?)

  • RDB:只用作后备用途,建议15分钟备份一次就好
  • AOF:
  • 在最恶劣的情况下,也只丢失不超过2秒的数据,数据完整性比较高,但代价太大,会带来持续的IO
  • 对硬盘的大小要求也高,默认64mb太小了,企业级最少都是5G以上;
  • 后面的master/slave才是新浪微博的选择!!

 

事务

  • 可以一次执行多个命令,是一个命令组,一个事务中,所有命令都会序列化(排队),不会被插队;
  • 一个队列中,一次性,顺序性,排他性的执行一系列命令
  • 三特性
  • 隔离性:所有命令都会按照顺序执行,事务在执行的过程中,不会被其他客户端送来的命令打断
  • 没有隔离级别:队列中的命令没有提交之前都不会被实际的执行,不存在“事务中查询要看到
  • 事务里的更新,事务外查询不能看到”这个头疼的问题
  • 不保证原子性:冤有头债有主,如果一个命令失败,但是别的命令可能会执行成功,没有回滚
  • 三步走
  • 开启multi
  • 入队queued
  • 执行exec
  • 与关系型数据库事务相比,
  • multi:可以理解成关系型事务中的 begin
  • exec :可以理解成关系型事务中的 commit
  • discard :可以理解成关系型事务中的 rollback

1 开启事务,加入队列,一起执行,并成功

127.0.0.1:6379> multi # 开启事务 
OK127.0.0.1:6379> set k1 v1 
QUEUED # 加入队列 
127.0.0.1:6379> set k2 v2 
QUEUED # 加入队列 
127.0.0.1:6379> get k2 
QUEUED # 加入队列 
127.0.0.1:6379> set k3 v3 
QUEUED # 加入队列 
127.0.0.1:6379> exec # 执行,一起成功! 
1) OK 
2) OK 
3) "v2" 
4) OK

2 放弃之前的操作,恢复到原来的值 (队列中的值全部放弃)

127.0.0.1:6379> multi # 开启事务 
OK
127.0.0.1:6379> set k1 v1111 
QUEUED 
127.0.0.1:6379> set k2 v2222 
QUEUED 
127.0.0.1:6379> discard # 放弃操作 
OK
127.0.0.1:6379> get k1 
"v1" # 还是原来的值

3 一句报错,全部取消,恢复到原来的值

127.0.0.1:6379> multi 
OK
127.0.0.1:6379> set k4 v4 
QUEUED 
127.0.0.1:6379> setlalala # 一句报错 
(error) ERR unknown command `setlalala`, with args beginning with: 
127.0.0.1:6379> set k5 v5 
QUEUED 
127.0.0.1:6379> exec # 队列中命令全部取消 
(error) EXECABORT Transaction discarded because of previous errors. 
127.0.0.1:6379> keys * # 还是原来的值 
1) "k2" 
2) "k3" 
3) "k1"

4 冤有头债有主(追究责任,谁的错,找谁去 )

127.0.0.1:6379> multi 
OK
127.0.0.1:6379> incr k1 # 虽然v1不能++,但是加入队列并没有报错,类似java中的通过编译
QUEUED 
127.0.0.1:6379> set k4 v4 
QUEUED 
127.0.0.1:6379> set k5 v5 
QUEUED 
127.0.0.1:6379> exec 
1) (error) ERR value is not an integer or out of range # 真正执行的时候,报错 
2) OK # 成功 
3) OK # 成功 
127.0.0.1:6379> keys * 
1) "k5" 
2) "k1" 
3) "k3" 
4) "k2" 
5) "k4"

5 watch监控



测试:模拟收入与支出



  • 正常情况下:
127.0.0.1:6379> set in 100 # 收入100元 
OK
127.0.0.1:6379> set out 0 # 支出0元 
OK
127.0.0.1:6379> multi 
OK
127.0.0.1:6379> decrby in 20 # 收入-20 
QUEUED 
127.0.0.1:6379> incrby out 20 # 支出+20 
QUEUED 
127.0.0.1:6379> exec 
1) (integer) 80 
2) (integer) 20 # 结果,没问题!
  • 特殊情况下:
127.0.0.1:6379> watch in # 监控收入in 
OK
127.0.0.1:6379> multi 
OK
127.0.0.1:6379> decrby in 20 
QUEUED 
127.0.0.1:6379> incrby out 20
QUEUED 
127.0.0.1:6379> exec 
(nil) # 在exec之前,我开启了另一个窗口(线程),对监控的in做了修改,所以本次的事务将 被打断(失效),类似于“乐观锁”
  • unwatch:取消watch命令对所有key的操作
  • 一旦执行了exec命令,那么之前加的所有监控自动失效!

Redis的发布订阅

  • 进程间的一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。例如:微信订阅号
  • 订阅一个或多个频道
127.0.0.1:6379> subscribe cctv1 cctv5 cctv6 # 1.订阅三个频道 
Reading messages... (press Ctrl-C to quit) 
1) "subscribe" 
2) "cctv1" 
3) (integer) 1 
1) "subscribe" 
2) "cctv5" 
3) (integer) 2 
1) "subscribe" 
2) "cctv6" 
3) (integer) 3 
1) "message" # 3.cctv5接收到推送过来的信息 
2) "cctv5" 
3) "NBA"





127.0.0.1:6379> publish cctv5 NBA # 2.发送消息给cctv5 
(integer) 1