环境:SpringBoot2.7.16 + Redis6.2.10
1. 简介
Redis乐观锁是一种并发控制的方法,它假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自影响的那部分数据。
Redis乐观锁不是通过检测版本号来实现的,而是在执行完一个写命令后,会进行检查,检查是否是被WATCH监视的键。
开启事务后,可以执行命令入队,然后执行事务。如果在事务执行期间,被WATCH监视的键发生变化,那么事务会执行失败。
2. Redis客户端演示
WATCH令
:6379> help watchWATCH key [key ...]summary: 观察给定的键以确定MULTI/EXEC块的执行since: 2.2.0group: transactions
MULTI命令
127.0.0.1:6379> help multiMULTI -summary: 标记事务块的开始since: 1.2.0group: transactions
MULTI命令执行之后,后续所有的命令操作不会立即执行,会将这些命令放入到队列中。最后通过后面的EXEC或DISCARD命令提交事务或放弃当前所有的操作。
EXEC命令
127.0.0.1:6379> help execsummary: 执行MULTI之后发出的所有命令since: 1.2.0group: transactions
DISCARD命令
127.0.0.1:6379> help discardsummary: 放弃MULTI之后发出的所有命令since: 2.0.0group: transactions
MULTI / EXEC / DISCARD演示
127.0.0.1:6379> MULTIOK127.0.0.1:6379(TX)> SET a 1QUEUED127.0.0.1:6379(TX)> INCR aQUEUED127.0.0.1:6379(TX)> SET b 1QUEUED127.0.0.1:6379(TX)> EXEC1) OK2) (integer) 23) OK
MULTI命令之后的操作都添加到了队列中。最后执行EXEC后再依次的执行每一个命令。
127.0.0.1:6379> MULTIOK127.0.0.1:6379(TX)> SET a 1QUEUED127.0.0.1:6379(TX)> DISCARDOK127.0.0.1:6379> keys *(empty array)
DISCARD放弃操作
127.0.0.1:6379> MULTIOK127.0.0.1:6379(TX)> SET a 1QUEUED127.0.0.1:6379(TX)> DISCARDOK127.0.0.1:6379> keys *(empty array)
WATCH演示
开启2个CMD窗口
第一个执行WATCH、MULTI、EXEC
127.0.0.1:6379> set version 1OK127.0.0.1:6379> WATCH versionOK127.0.0.1:6379> MULTIOK127.0.0.1:6379(TX)> set a 1QUEUED127.0.0.1:6379(TX)> set b 1QUEUED
到此不执行EXEC或DISCARD
第二个执行修改version
127.0.0.1:6379> INCR version(integer) 2
到此再回到第一个窗口继续执行
127.0.0.1:6379(TX)> exec(nil)127.0.0.1:6379> keys *1) "version"
此时我们在MULTI后面添加的命令都没有执行,因为在第二个窗口中我们修改了version的值,所以这里EXEC后不会执行任何的命令。
3. SpringBoot中应用
在SpringBoot中使用Redis乐观锁,再结合retry框架来实现重试的功能。
引入依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>
配置redis
spring:redis:host: localhostport: 6379:lettuce:pool:maxActive: 8maxIdle: 100minIdle: 10maxWait: -1
代码实现
private StringRedisTemplate stringRedisTemplate ;// 重试,当发生IllegalStateException异常才进行重试,并且最多重试3次(maxAttempts = 3, value = {IllegalStateException.class})public List<Object> watch() {List<Object> ret = stringRedisTemplate.execute(new SessionCallback<List<Object>> () {public List<Object> execute(RedisOperations opt) throws DataAccessException {// 执行watch keyopt.watch("v") ;// 开始事务块,后续所有命令将加入队列opt.multi() ;opt.opsForValue().increment("v") ;// 执行队列中的所有命令return opt.exec() ;}});// 当放弃队列中的任务后,这里的返回值将会为空。所以只需空判断即可if (ret.isEmpty()) {System.err.printf("重试...%n") ;// 这里抛出异常,进行重试throw new IllegalStateException("数据被修改") ;}return ret ;}
总结:在SpringBoot中,通过使用Redis的Watch机制,可以实现乐观锁。这种锁机制可以有效地解决并发访问数据时可能出现的冲突问题,提高系统的并发性能。但是,需要注意的是,在事务执行过程中,如果被监控的key发生变化,会进行重试操作,这可能会对系统性能产生一定的影响。因此,在使用乐观锁时,需要根据具体业务场景进行权衡和选择。
完毕!!!



