redis专项练习三redis的事务和乐观锁

一、redis的事务

  Redis目前对事务的支持相对简单。Redis只能保证一个client发起的事务中的命令可以连续的执行,而中间不会插入其他的client命令。当一个client在一个链接中发出multi命令时,这个链接会进入一个事务上下文,该连接后续的命令不会立即执行,而是先放到一个队列中,当执行exec命令时,redis会顺序的执行队列中的所有命令。注意redis的事务和乐观锁只针对于redis单机实例,redis的集群没有事务相关的概念。

  redis事务设置的命令有 MULTI (multi)(开启事务), exec(执行队列中的redis命令),discard(取消)Redis支持简单的事务

 1、Redis与 mysql事务的对比

 

Mysql

Redis

开启

start transaction

muitl

语句

普通sql

普通命令

失败

rollback 回滚

discard 取消

成功

commit

exec

注: rollback与discard 的区别

如果已经成功执行了2条语句, 第3条语句出错.

Rollback后,前2条的语句影响消失.

Discard只是结束本次事务,前2条语句(在exec命令之前已经成功的命令)造成的影响仍然还在

2、客户端命令:

#先设置金额为10000
127.0.0.1:6379> set money 10000
OK
#获取金额
127.0.0.1:6379> get money
"10000"
#执行事务操作
127.0.0.1:6379> MULTI
OK
#将所有的redis命令都存在到队列中,在执行discard(取消执行队列中的命令)
#或者exec操作(执行存放在队列中的redis命令)
127.0.0.1:6379> set money 9000
QUEUED
127.0.0.1:6379> get money
QUEUED
#执行操作
127.0.0.1:6379> exec

 3、java代码实现

    /**
     * redis的事务操作
     */
    @Test
    public void testTransaction(){
        ValueOperations<String,String> stringOper = redisTemplate.opsForValue();
        stringOper.set("张三","10000");
        stringOper.set("李四","10000");

        //开启redis的事务支持
        redisTemplate.setEnableTransactionSupport(true);
        //开启事务
        redisTemplate.multi();

         //执行转账操作 张三 减去500 李四加上500
        stringOper.increment("张三",-500);
        stringOper.increment("李四",500);

        //取消操作 则上面的转账命令不会执行,则两人的金额不会发生改变
        //redisTemplate.discard();
        //执行操作,转账成功
        redisTemplate.exec();

        String  zhangsanMoney = stringOper.get("张三");
        String lisimoney = stringOper.get("李四");

        log.info("redis discard取消(没有发生变量) 转账后张三的资金:{},李四的资金:{}",zhangsanMoney,lisimoney);
    }

 4、注意 

在mutil后面的语句中, 语句出错可能有2种情况

1: 语法就有问题,

这种,exec时,报错, 所有语句得不到执行

2: 语法本身没错,但适用对象有问题. 比如 zadd 操作list对象

Exec之后,会执行正确的语句,并跳过有不适当的语句.

(如果zadd操作list这种事怎么避免? 这一点,由程序员负责)

二、redis的乐观锁

1、redis的乐观锁

乐观锁:redis大多数是基于数据版本(version)的记录机制实现的。即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表添加一个version字段来实现。在读取数据时,将此版本号一同读出,之后更新时对此版本号加1。此时,将提交数据的版本号与数据库表对应记录的当前版本号进行对比,如果提交的数据版本号大于数据库当前版本号,则予以更新,否则认为是过期数据。

watch监控:watch命令会监控给定的key,当exec时如果监视的key从调用watch后发生过变化,则整个事务会失败。也可以调用watch多次监视多个key,这样就对指定事务key加乐观锁了。注意watch的key是对整个链接有效的,事务也一样。如果链接断开,监视和事务都会被自动清除。当然exex、discard、unwatch命令都会自动清除链接中的所有监视。

2、客户端命令

  2.1 只事务实现两个人抢一张票

 client 1

#开启事务
127.0.0.1:6379> MULTI
OK
#票数自减
127.0.0.1:6379> DECR ticket
QUEUED
#等待客户端2操作 在执行该操作
#客户端2操作完成后,再执行则票数在0的基础上-1 则票数为-1 不合理
127.0.0.1:6379> exec
1) (integer) -1
127.0.0.1:6379> get ticket
"-1"

client 2

#客户端2操作
127.0.0.1:6379> MULTI
OK
#票数自减
127.0.0.1:6379> decr ticket
QUEUED
执行 票数为0
127.0.0.1:6379> exec
1) (integer) 0

出现 票数为负数的情况,则无法确保业务正确性。

2、使用乐观锁配合事务完成抢票操作

 client 1

#使用watch命令来监听ticket  如果在针对该key操作的时候 发现key已经被操作过
#则这次事务所涉及的redis操作都被取消
127.0.0.1:6379> watch ticket
OK
127.0.0.1:6379> MULTI
OK
#在票数自减之前,client2已经对ticket进行了操作,则该次事务取消
127.0.0.1:6379> decr ticket
QUEUED
#操作为 nil 说明别的客户端已经操作过,无法进行相关的操作
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> get ticket
"0"

client 2

#客户端2操作
127.0.0.1:6379> MULTI
OK
#票数自减
127.0.0.1:6379> decr ticket
QUEUED
执行 票数为0
127.0.0.1:6379> exec
1) (integer) 0

  则不会出现业务超售情况,确保了事务,watch命令在一次key的修改事务完成后失效,如果需要继续监听,则重新运行watch命令。

有关redis的事务和乐观锁的知识先写到这里,以后有什么新的理解,在重新补充,第一次学习redis,如果有什么错误的地方,希望大家指出,手打不易,看完请点赞,您的点赞是我继续更新的动力。

猜你喜欢

转载自blog.csdn.net/liushangzaibeijing/article/details/85016494