Redis事务与乐观锁

Redis事务与乐观锁

概述

Redis 事务是一种将多个命令打包在一起执行的机制。通过使用事务,可以确保一系列命令在一次执行中依次执行,而不会被其他客户端的命令请求打断。

Redis 事务的执行分为以下几个步骤:

  1. 开启事务 :使用 MULTI 命令开启一个事务。之后的命令都会被添加到事务队列中,而不会立即执行。
  2. 添加命令 :在 MULTI 和 EXEC 命令之间,可以添加任意数量的 Redis 命令,这些命令会按照添加的顺序被放入事务队列中。
  3. 执行事务 :使用 EXEC 命令执行事务。Redis 会按照事务队列中命令的顺序依次执行这些命令。在执行事务期间,Redis 不会处理其他客户端的命令请求。
  4. 事务结果 :执行事务后,Redis 会返回事务中每个命令的执行结果。结果的顺序与命令在事务队列中的顺序相对应。
  5. 取消事务 :在开启事务后,可以使用 DISCARD 命令取消事务,丢弃事务队列中的所有命令。

Redis 事务的一个重要特性是乐观锁。在执行事务期间,Redis 不会对数据进行锁定或阻塞其他客户端操作,而是在执行事务时记录了事务执行期间所依赖的键的状态,如果在执行 EXEC 命令之前检测到这些键的状态发生了变化,那么事务将会被中断并放弃执行。

需要注意的是,Redis 的事务是原子性的,要么全部执行成功,要么全部放弃执行。如果在事务执行期间出现错误,事务中的所有命令都不会被执行,并且不会对数据产生任何影响。Redis 事务的应用场景包括批量操作、原子操作、队列操作等,通过将多个命令打包在一起执行,可以减少网络通信开销,提高性能,并保持一致性。

乐观锁和悲观锁

悲观锁

  • 悲观锁的思想是,在整个数据操作过程中,将数据进行加锁,以阻止其他事务或线程对数据进行修改。
  • 当一个事务获取了悲观锁后,其他事务需要等待锁被释放才能继续操作,从而保证了数据的一致性。
  • 悲观锁适用于并发冲突较多、写操作频繁的场景。
  • 在关系型数据库中,使用行级锁或表级锁实现悲观锁。

乐观锁

  • 乐观锁的思想是,假设多个事务之间不会发生冲突,每个事务在修改数据时不会阻塞其他事务,只在提交时检查是否有冲突。
  • 乐观锁通过在数据记录中添加版本号或时间戳等标识,用于检测数据是否被其他事务修改过。
  • 当一个事务提交时,会比较当前数据的版本号与事务开始时获取的版本号是否一致,如果一致则提交成功,否则表示数据已被其他事务修改,需要进行冲突处理。
  • 乐观锁适用于并发冲突较少、读操作频繁的场景,避免了数据的阻塞等待。
  • 在关系型数据库中,使用版本号或时间戳实现乐观锁。

在实际应用中,选择使用悲观锁还是乐观锁取决于具体的需求和场景。悲观锁可以确保数据的一致性,但可能带来较高的开销和性能影响。乐观锁则通过乐观的假设减少了锁的竞争,提高了并发性能,但需要在冲突发生时进行额外的处理。

事务的错误处理

出现语法错误时,EXEC 后会直接返回错误,语法正确的命令也不会执行

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> SET key
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379> SET name cx
QUEUED
127.0.0.1:6379> EXEC
(error) EXECABORT Transaction discarded because of previous errors.

出现运行错误时,比如使用集合的命令操作字符串,这种错误在执行之前无法被发现,所以这种情况下正确的命令会被执行。Redis 的事务机制并不支持事务回滚(rollback)操作,因此当出现事务运行错误需要自己收拾烂摊子。

127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> SET name caixing
QUEUED
127.0.0.1:6379> SADD name cx
QUEUED
127.0.0.1:6379> EXEC
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value

WATCH 命令

WATCH 命令用于实现乐观锁机制,它可以在事务执行之前监视一个或多个键。WATCH 命令会监视指定的键,如果在执行事务期间有其他客户端修改了被监视的键,那么当前事务将被打断,不会执行,可以通过检查执行 EXEC 命令的返回值来判断是否执行了事务。

使用 WATCH 命令监视一个或多个键:

WATCH key1 key2 ...

可以指定一个或多个键来监视。一旦执行了 WATCH 命令,Redis 会将这些键标记为被监视状态。

比如说如果要自己利用 GET 和 SET 实现 INCR 函数,伪代码如下所示。这段代码可能出现竞态条件,key 的原始值为 6,期望情况应该是将 key 变为 8,但是如果两个客户端同时(在对方尚未返回时)开始执行这段代码,那么最后 key 是 7。

$val = GET $key
if $val is null
    $val = 0
$val = $val + 1
SET $key $val

虽然事务提供了原子性,但是由于事务每条命令的执行结果是同时返回的,所以不适用这种前后具有依赖关系的命令,为了解决这个问题,可以使用 WATCH 来实现 GET 获得键值后不允许其他客户端修改键以防止竞态条件。

在执行事务前使用 WATCH 监视 key,在执行事务前 key 发生了改变,变成了 3,因此事务未执行。WATCH 的监控一直持续到 EXEC 命令。

127.0.0.1:6379> SET key 1
OK
127.0.0.1:6379> WATCH key
OK
127.0.0.1:6379> SET key 2
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> SET key 3
QUEUED
127.0.0.1:6379> EXEC
(nil)
127.0.0.1:6379> GET key
"2"

修改刚刚的伪代码如下。

WATCH $key
$val = GET $key
if $val is null
    $val = 0
$val = $val + 1
MULTI
SET $key $val
EXEC
------本页内容已结束,喜欢请分享------

文章作者
能不能吃完饭再说
隐私政策
PrivacyPolicy
用户协议
UseGenerator
许可协议
NC-SA 4.0


© 版权声明
THE END
喜欢就支持一下吧
点赞21赞赏 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片