2. Redis事务
2.1 Redis 事务 和 MySQL 事务区别
Redis 事务 和 MySQL 事务概念上类似,都是指 把一系列操作在一起执行,不允许中间插入。
Redis 事务特点:
- 弱原子性 或 无原子性:redis 没有回滚机制,只是批量一起执行,里面有失败就是失败了。
- 不保证一致性:redis 不涉及约束,没有回滚。前后结果不都合理有效,可能有非法中间态。
- 不需要保隔离性:没有隔离级别,因为不会并发执行事务(redis 单线程处理请求)
- 不需持久性:存在内存里,是否开启持久,是配置的问题,和事务无关。
Redis 事务本质上,是客户端上有一个 "事务队列",每次客户端开启事务,命令先存到队列中,服务器收到 exec 命令,按顺序执行队列中的操作。
2.2 使用场景示例
Redis 事务,主要就是避免其他客户端命令插队执行,将操作打包执行。
如:秒杀活动
一个经典写法:
bash
//获取仓库剩余商品个数
if(个数 > 0) {
下单成功
个数减一;
}
如果不加锁,就存在 "线程安全" 问题,在 Redis 使用事务来解决。
redis 原生命令中不支持类似上面的条件判断,但是 redis 支持 lua 脚本,通过 lua 脚本就可以实现,且和 redis 事务一样是打包批量执行。
2.3 使用事务
redis 中通过 MULTI 开启事务,EXEC 执行事务, DISCARD 放弃当前事务。

开启事务后,先放入事务队列,此时没有执行事务,在其他客户端上是看不到这几个键的。

执行 EXEC ,

这时,这几个键才真正放入 redis 中,同时在 新窗口中也可查询到。

如果,事务结束前,出现了服务器重启等情况,就相当于是 DISCARD 。
下面讲一种情况,key 的值初始为 111,客户端1 开启了事务,并将 key 设为 222,但是在客1事务执行前,客户端2 将 key 设为了 333。虽然,从顺序上看,客1 是先设置的 key 为 222,但实际 set key 222 执行的晚,结果为 222。
为此,redis 中有 WATCH 命令可以监视某个或某些 key 在事务执行前是否发生了变化。

客户端1 设置 key1 为 222,但未执行。客户端2 设置 key1 为 .
666。

客户端1 执行事务

由于 key1 在事务执行前发生了改变,所以返回了 nil 没有执行。
2.4 WATCH 的实现原理
watch 的实现,类似于 "乐观锁"。
乐观锁 是一类锁的特质,即加锁前,预期锁冲突几率比较低,先不加锁。冲突真正发生再加锁。
想要看 乐观锁 的更细节讲述,可看此文章:多线程进阶
redis 的 watch 基于版本号这样的机制,来实现了 "乐观锁"。
watch key 的时候,会给 key 安排一个 版本号,给 exec 加了一个判定条件,每次修改 key 都会引起版本号变化,当执行 EXEC 时,就会进行判定,版本号不一致说明 key 被修改过,就丢弃该操作,返回 nil;反之,未被修改,进行真正执行。