Redis青铜修炼手册(三) 丨 持久化和事务

来自:程序员共成长(微信号:finishbug),作者:程序员共成长

什么是持久化

数据写入redis时,保存在redis客户端的内存中,这是的数据称为瞬时数据,所谓持久化就是把内存中的数据保存在存储设备中。(入磁盘)


持久化的两种方式

aof: Redis 在执行完一个写命令后,都会将执行的写命令追回到 Redis 内部的缓冲区的末尾。这个过程是命令的追加过程。


rbd: 固定的时间间隔将内存中的数据写入磁盘。默认的方式


Append Only File(aof)

以日志的形式记录redis的写操作。将redis执行过的所有写指令记录下来。只追加文件不可以改写文件、redis启动的时候回读取该文件重新构建数据。也就是说redis会将文件中的写操作从头到尾重新执行一遍

配置文件部分内容如下

appendonly no
# The name of the append only file (default: "appendonly.aof")
appendfilename "appendonly.aof"
# appendfsync always
appendfsync everysec
# appendfsync no


  • always: 同步持久化,每次发生数据变化都会被立即记录到磁盘中(appendonlu.aof)。 能保证数据的完整性,但是性能相对较差

  • Everysec: 出厂默认推荐,异步操作。每秒钟记录一次。 所以最多会只丢失最后一秒钟的数据

  • No: 执行写操作之后由操作系统自动的去同步到磁盘。性能最好


Rewrite: aof采用文件追加方式,因此appendonly.aof文件会越来越大。所以新增了重新机制。当aof文件大小超过阈值,会fork一个新的进程将文件重写,遍历新进程中的内存数据。重写aof并不会读取旧的aof文件。而是将内存中的所有数据内容用命令的方式重写了一个新的aof文件。类似快照


触发机制, redis.conf中

auto-aof-rewrite-percentage 100 # aof文件增长比例,指当前aof文件比上次重写的增长比例大小。
auto-aof-rewrite-min-size 64mb  # aof文件重写最小的文件大小


优点

AOF 持久化的方法提供了多种的同步频率,即使使用默认的同步频率每秒同步一次,Redis 最多也就丢失 1 秒的数据而已。

0 AOF 文件使用 Redis 命令追加的形式来构造,因此,即使 Redis 只能向 AOF 文件写入命令的片断,使用 redis-check-aof 工具也很容易修正 AOF 文件。

AOF 文件的格式可读性较强,这也为使用者提供了更灵活的处理方式。例如,如果我们不小心错用了 FLUSHALL 命令,在重写还没进行时,我们可以手工将最后的 FLUSHALL 命令去掉,然后再使用 AOF 来恢复数据。

同时开启时,会优先健在aof文件来恢复数据。 因为aof的数据完整性要比rdb的要好。


缺点

对于具有相同数据的的 Redis,AOF 文件通常会比 RDF 文件体积更大。

虽然 AOF 提供了多种同步的频率,默认情况下,每秒同步一次的频率也具有较高的性能。但在 Redis 的负载较高时,RDB 比 AOF 具好更好的性能保证。

RDB 使用快照的形式来持久化整个 Redis 数据,而 AOF 只是将每次执行的命令追加到 AOF 文件中,因此从理论上说,RDB 比 AOF 方式更健壮。官方文档也指出,AOF 的确也存在一些 BUG,这些 BUG 在 RDB 没有存在。


Redis Database(rbd)

redis会单独fork一个子进程,来进行持久化操作。会将内存中的数据暂时先写入一个临时文件中,当持久化过程结束后。再用这个临时文件替换之前持久化好的文件(dump.rbd )。 由于是单独创建的子进程,所以真个过程并不会影响主进程的IO操作。因此需要整个过程非常高效,如果对数据精度要求较高。rbd的方式并不适用。因为如果出现断电情况最后一个时间间隔内的数据可能会丢失。


所谓fork: 复制一个与当前进程一样的进程。所有数据都与原进程一直。 类似git的fork

redis.conf

################################ SNAPSHOTTING  ################################
#
# Save the DB on disk:
#
#   save <seconds> <changes>
#   after 900 sec (15 min) if at least 1 key changed
#   after 300 sec (5 min) if at least 10 keys changed
#   after 60 sec if at least 10000 keys changed

#
   save ""  如果要禁用rdb,不设置save或者给sava传个空字符串就ok 
# 出发rdb的条件
save 900 1  # 900秒改一次
save 300 10 # 300秒改10次
save 60 10000 # 60秒改1W次


当遇到特别重要的数据我们需要它立即进行持久化。可以通过save命令来完成


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

劣势
由于会fork一份一样的进程。因此会占用翻倍的膨胀性
发生意外时,最后一份数据可能会丢失

什么是Redis事务

事务的概念这里就不说了,主要说一下Redis中的事务

可以一次执行多个命令,但本质上是一个命令集。按顺序的执行每一个命令,不会被其他以外的命令影响。

MULTI 标记一个事务的开始 EXEC 执行所有事务块内的命令,执行结束之后释放事务锁 DISCARD取消事务,放弃执行事务块内的所有命令。不保证原子性,一个事务中,有命令执行失败并不会影响事务中的其他命令。没有回滚。


# 开启事务
127.0.0.1:6379MULTI
OK
127.0.0.1:6379incr num
QUEUED  # 返回QUEUED 表示该命令已经加入队列中
127.0.0.1:6379incr num
QUEUED
127.0.0.1:6379decr num
QUEUED
EXEC 执行事务队列中的所有命令
127.0.0.1:6379EXEC
1) (integer) 1
2) (integer) 2
3) (integer) 1


127.0.0.1:6379MULTI
OK
127.0.0.1:6379set str abc
QUEUED
127.0.0.1:6379set str qwer
QUEUED
# 取消事务
127.0.0.1:6379DISCARD
OK
set str value并没有生效
127.0.0.1:6379get str
(nil)


WATCH 监控


WATCH key [key …]

监视一个或者多个Key。如果在执行事务之前这些key被其他命令改动,则事务被打断

UNWATCH

取消watch对key的监视。


WATCH锁类似乐观锁


乐观锁

假设出现最好的情况,从取数据到修改数据完毕这个时间内,不会有任何人去操作这个数据,因此不会上锁。但是为了确定有没有人真正的去操作这条数据,可以通过版本号(version)的方法实现。

如A同学取一条数据的时候版本号为1,在没有更新完毕的时候,B同学也取了同样的数据,版本号也为1。当A更新完毕之后版本号为2了,B同学再去更新发现版本不一致,就需要重新获取最新的数据。


悲观锁

假设出现最坏的情况,从取数据到更新完毕这个时间内,认为数据会被别人修改。所有拿到数据时会进行加锁操作。这样别人就一直处于阻塞状态,从而无法修改数据。如表级锁,行锁,读写锁等…


如下所示,我们有999块钱,消费了199之后又消费了100(199和100为整体)。此时一切正常,最终剩余700块


127.0.0.1:6379set money 999
OK
127.0.0.1:6379WATCH money
OK
127.0.0.1:6379MULTI
OK
127.0.0.1:6379DECRBY money 199
QUEUED
127.0.0.1:6379DECRBY money 100
QUEUED
127.0.0.1:6379EXEC
1) (integer) 800
2) (integer) 700
127.0.0.1:6379get money
"700"


另一种场景,A请求:我得知卡上余额999元

127.0.0.1:6379set money 999
OK
# 开启对money的监控
127.0.0.1:6379WATCH money
OK


但是此时又一个请求,B:请求将money减少了100

127.0.0.1:6379get money
"999"
127.0.0.1:6379DECRBY money 100
(integer) 899


重新回到最开始的A请求,执行一个事务操作

127.0.0.1:6379DECRBY money 100
QUEUED
127.0.0.1:6379DECRBY money 99
QUEUED
# 执行事务时返回了nil
127.0.0.1:6379EXEC
(nil)


此时查看money的值

127.0.0.1:6379get money
"899"


因为监控(watch)money开始之后,该字段的值被其他命令改动了,所以事务将不会被执行。

推荐↓↓↓
数据库开发
上一篇:Redis青铜修炼手册(二) 丨 五大基本类型的用法 下一篇:Redis青铜修炼手册(四) 丨 Redis的发布和订阅