redis的持久化和事务
持久化
redis有两种持久化方式,分别为
- RDB[Redis DataBase]
- AOF[Append Only File]
rdb【Redis DataBase】方式
在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的snapshot快照,它恢复时是将快照文件直接读到内存里。
RDB是怎么做的?
Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。
RDB的优势
- 适合大规模的数据恢复
- 对数据完整性和一致性要求不高
RDB的劣势
- 在一定间隔时间做一次备份,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改
- FORK的时候,内存中的数据被克隆了一份,大致2倍的膨胀性需要考虑。
aof
以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,Redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
1.AOF的优点
- 每修改同步:appendfsync always,同步持久化,每次发生数据变更会被立即记录到磁盘,性能较差但数据完整性比较好。
- 每秒同步:appendfsync everysec, 异步操作,每秒记录,如果一稍内宕机,有数据丢失。
- 不同步:appendfsync no,从不同步
2.AOP的缺点
- 相同数据集的数据而言aof文件要远大于rdb文件,恢复速度慢于rdb
- aof运行效率要慢于rdb,每秒同步策略效率较好。
注:
AOF和RDB是可以共存的,当即存在dump.rdb文件,也存在appendonly.aof文件时,我们启动redis-server时,它会优先读取appendonly.aof文件,因为这个文件可以更好地保证数据的稳定性。
如果因为网络问题或是丢包等事情造成appendonly.aof文件损坏,导致redis-server启不了,则可以执行如下命令来恢复appendonly.aof文件1
redis-check-aof --fix appendonly.aof
此命令会把所有不符合appendonly.aof文件格式的内容全部截掉,以保证aof文件合格。
- AOF重写[rewrite]
是什么?
重写原理:AOF文件持续增长而过大时,会fork出一条新进程来将文件重写(也是先写临时文件最后再rename),遍历新进程的内存中数据,每条记录有一条的Set语句。重写AOF文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似
触发机制:Redis会记录上次重写时的AOF文件大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发。【一般大型公司这个值会改成3G左右大小】
相关的配置项
- appendonly //改为 yes 表示启用 aof机制
- appendfilename //指定生成的 aof文件名
- appendfsync //异步
- no-appendfsync-on-rewrite //重写时是否可以运用appendfsync,用默认no即可,保数据安全性
- auto-aof-rewrite-min-size //设置重写的基准值,默认是64M
- auto-aof-rewrite-percentage //设置重写的基准值,默认是100
RDB 和 AOF使用建议
如果是只做缓存的话,可以不使用任何的持久化方式,也就是关掉RDB和AOF
如果对数据的一致性要求不高时,就可以直接使用RDB,它是最简单的持久化策略
如果同时开启2种持久化方式的话,当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整。RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件。那要不要只使用AOF呢?作者建议不要,因为RDB更适合用于备份数据库(AOF在不断变化,不好备份),快速重启,而且不会有AOF可能潜在的BUG,留着作为一个万一的手段。
性能建议
因为RDB文件只用作后备用途,建议只在slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留
save 900 1
这条规则。如果Enable AOF,好处是在最恶劣情况下也只会丢失不超过2秒数据,启动脚本较简单只load自己的AOF文件就可以了。代价一是带来了持续的IO,二是AOF rewrite的最后将rewrite过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上。默认超过原大小100%大小时重写可以改到适当的数值。
如果不Enable AOF,仅靠Master-Slave Replication实现高可用性也可以。能省掉一大笔IO也减少了rewrite时带来的系统波动。代价是如果Master/Slave同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个Master/Slave中的RDB文件,载入较新的那个。【新浪微博就选用了这种架构】
Redis事务
定义
可以一次执行多个命令,本质是一组命令的集合。一个事务中的所有命令都会被序列化。按顺序地串行化执行而不会被其它命令插入,不许加塞.
事务能干什么?
一个队列中,一次性、顺序性、排他性的执行一系列命令。
该如何使用?
常用命令
- DISCARD 消取事务,放弃执行事务块内的所有命令
- EXEC 执行所有事务块内的命令
- MULTI 标记一个事务块的开始
- UNWATCH 取消WATCH命令对所有 key的监视
- WATCH key [key …] 监视一个(或多个)key,如果在事务执行之前这个(或这些)key被其它命令所改动,那么事务将被打断
注:redis支持部份事务,它有如下几种情况
- 正常执行,就是加入事务的所有命令都是正确的,并且执行时也没有错误发生
- 放弃事务,就是加入事务的所有命令都是正确,并且可以执行的,但是,最后没有执行
EXEC
,而是执行了DISCARD
,表示放弃此事务 - 全体连坐,是指加入事务的命令中,其中有任何一条命令语法是错误的,则再加入时,REDIS就会报错,那本次事务的所有命令都会撤消。
- 冤有头债有主,是指加入事务的命令中,语法都没有错误,但是其中有任何一条或多条存在运行时错误【比如字符串值做了自增】,则这种情况下,REDIS不会回滚事务中的所有命令,而只是运行时错误的命令不执行,其它命令正常执行,这也就是所谓的部份支持事务。
- watch监控,监视一个或多个key,如果这个key被别的客户改变了,则在事务提交时会执行失败。
小结:
watch指令,类似于乐观锁,事务提交时,如果key的值已被另的客户改为,比如某个list已被别的客户端push/pop过了,整个事务队列都不会被执行。
通过watch命令在事务之前监控了多个key,倘若在watch之后有任何key的值发生了变化,EXEC命令执行的事务都将被放弃,同时返回Nullmulti-bulk应答通知调用者事务执行失败。
- 三个阶段
- 开启:以MULTI开始一个事务
- 入队:将多个命令入队到事务中,接到这些命令并不会立即执行,而是放到等待执行的事务队列里面
- 执行:由EXEC命令触发事务
- 三个特性
- 单独的隔离操作:事务中的所有命令都会序列化、按顺序执行。事务在执行的过程中,不会被其它客户端发送来的命令请求所打断。
- 没有隔离级别的概念:队列中的命令没有提交之前都不会实际的被执行,因为事务提交前任何指令都不会被实际执行,也就不存在“事务内的查询要看到事务的更新,在事务外查询不能看到”这个让人万分头痛的问题。
- 不保证原子性:redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚。