WHCSRL 技术网

Redis事务及乐观锁

Redis事务

概念

任何数据库都要有一套自己的事务控制机制,redis事务是一次可以执行多个命令,它的本质是一组命令的集合。
一个事务中所有的命令都会被序列化,在事务执行的过程中会按照顺序执行队列中的命令。其它客户端提交的命令请求会等到事务执行完毕再执行。

特点

1、redis事务是分为三个阶段:开始事务、命令入队、执行事务。
2、redis事务不具有隔离级别的概念:redis在发送exec命令之前,命令操作只是被放入到队列缓存当中,并不会被实际执行,因此也就不能类似关系型数据中,在事务内查询已经变更的操作,事务外的客户端更不能查询到事务内的数据。
3、redis事务是不保证原子性的:redis事务只保证在命令格式只有在都正确的情况下才会都执行,要不就都不执行命令。但是事务的整体是不保证原子性的,且没有回滚,当事务中任意一个命令执行失败,其余的命令依然会执行。

Redis 事务相关命令

命令作用
multi开启一个事务
exec提交事务
discard放弃一个事务,清空命令对列
watch用于监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断

命令入队执行流程图

在这里插入图片描述

事务流程

在这里插入图片描述

事务ACID

原子性

① 在执行EXEC命令前,客户端发送操作命令本身存在错误(比如语法错误,使用了不存在的命令),在命令入队时就被 Redis 实例判断出来了。对于这种情况Redis 就会拒绝执行所有提交的命令操作,即这个事务将不会被执行。保证了事务的原子性。
在这里插入图片描述
② 命令入队是均为正常,但命令和操作的数据类型不一致,Redis 实际执行这些事务操作时,就会报错。这样事务执行中会报错,但对于正确的命令依然会继续执行。在这种情况下,事务的原子性就无法得到保证了。
在这里插入图片描述
一致性
这个就和上面原子性描述的请款一样,但还有第三种情况。
EXEC命令执行时实例发生故障,实例故障后会进行重启,这就和持久化有关了。
① 没有开启 RDB 或 AOF,那么,实例故障重启后,数据都没有了,数据库是一致的。
② 使用了RDB 快照,因为 RDB 快照不会在事务执行时执行,所以,事务命令操作的结果不会被保存到 RDB 快照中,使用 RDB 快照进行恢复时,数据库里的数据也是一致的。
③ 使用 AOF 日志,而事务操作还没有被记录到 AOF 日志时,实例就发生了故障,那么,使用 AOF 日志恢复的数据库数据是一致的。如果只有部分操作被记录到了 AOF 日志,我们可以使用 redis-check-aof 清除事务中已经完成的操作,数据库恢复后也是一致的。

总的来说在命令执行错误或 Redis 发生故障的情况下,Redis 事务机制对一致性属性是有保证的。

隔离性
因为Redis使用单线程的方式来执行事务(以及事务队列中的命令),并且服务器保证, 在执行事务期间不会对事务进行中断,因此,Redis的事务总是以串行的方式运行的,并且 事务也总是具有隔离性的。

持久性
Redis事务的耐久性由服务器所使用持久化模式决定的。
(1) 当服务器在无持久化的内存模式下运作时,事务不具有耐久性。因为一旦服务器停机,服务器所有的数据都将丢失。
(2) 当服务器在ROB持久化模式下运作时,事务同样不具有耐久性。因为服务器只会在特定的保存条件下才会执行BGSAVE命令,并且异步执行的BGSAVE命令不能保证事务的数据第一时间被保存到硬盘上。
(3) 当服务器运行在AOF持久化模式下,并且appendfsync选项的值为always时,程序总会在执行命令之后调用同步(sync)函数,将命令数据真正地保存到硬盘里。

Redis 乐观锁

WATCH命令监控

Redis Watch 命令用于监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。

左边的先开始事务:
在这里插入图片描述
右边的先开始事务:
在这里插入图片描述

从上图可以看出被打断的事务都是后执行的。先开始的事务正常执行。简单的说就是"先到先得"。

乐观锁的实现方式

MVCC方式实现

一般是在数据表中加上版本号字段 version,表示数据被修改的次数。当数据被修改时,这个字段值会加1。

推荐阅读