Redis04---事务

1. Redis事务

Redis的单条命令是保证原子性的,但是redis事务不能保证原子性

  • Redis事务没有隔离级别的概念

  • Redis单条命令是保证原子性的,但是事务不保证原子性!

Redis事务本质:一组命令的集合。

----------------- 队列 set set set 执行 -------------------

事务中每条命令都会被序列化,执行过程中按顺序执行,不允许其他命令进行干扰。

  • 一次性
  • 顺序性
  • 排他性

事务操作过程

  • 开启事务(multi

  • 命令入队

  • 执行事务(exec

127.0.0.1:6379> multi # 开启事务
OK
127.0.0.1:6379> set k1 v1 # 命令入队
QUEUED
127.0.0.1:6379> set k2 v2 # ..
QUEUED
127.0.0.1:6379> get k1
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> keys *
QUEUED
127.0.0.1:6379> exec # 事务执行
1) OK
2) OK
3) "v1"
4) OK
5) 1) "k3"
   2) "k2"
   3) "k1"

# ====================取消事务(discurd)=================

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> DISCARD # 放弃事务
OK
127.0.0.1:6379> EXEC 
(error) ERR EXEC without MULTI # 当前未开启事务
127.0.0.1:6379> get k1 # 被放弃事务中命令并未执行
(nil)

# ====================事务错误(代码语法错误)=================

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> error k1 # 这是一条语法错误命令
(error) ERR unknown command `error`, with args beginning with: `k1`, # 会报错但是不影响后续命令入队 
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> EXEC
(error) EXECABORT Transaction discarded because of previous errors. # 执行报错
127.0.0.1:6379> get k1 
(nil) # 其他命令并没有被执行

# ====================事务错误(代码逻辑错误)=================

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> INCR k1 # 这条命令逻辑错误(对字符串进行增量)
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) (error) ERR value is not an integer or out of range # 运行时报错
4) "v2" # 其他命令正常执行

# 虽然中间有一条命令报错了,但是后面的指令依旧正常执行成功了。
# 所以说Redis单条指令保证原子性,但是Redis事务不能保证原子性。

2. 监控

  • 悲观锁:很悲观,认为什么时候都会出现问题,无论做什么都会加锁

  • 乐观锁

    • 很乐观,认为什么时候都不会出现问题,所以不会上锁!更新数据的时候去判断一下,在此期间是否有人修改过这个数据

    • 获取version,更新的时候比较version

使用watch key监控指定数据,相当于乐观锁加锁unwatch进行解锁。每次提交执行exec后都会自动释放锁,不管是否成功

正常执行

127.0.0.1:6379> set money 100 # 设置余额:100
OK
127.0.0.1:6379> set use 0 # 支出使用:0
OK
127.0.0.1:6379> watch money # 监视money (上锁)
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 20
QUEUED
127.0.0.1:6379> INCRBY use 20
QUEUED
127.0.0.1:6379> exec # 监视值没有被中途修改,事务正常执行
1) (integer) 80
2) (integer) 20

测试多线程修改值,使用watch可以当做redis的乐观锁操作(相当于getversion)

# 线程1:

127.0.0.1:6379> watch money # money上锁
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 20
QUEUED
127.0.0.1:6379> INCRBY use 20
QUEUED
127.0.0.1:6379> 	# 此时事务并没有执行

模拟线程插队,线程2:

127.0.0.1:6379> INCRBY money 500 # 修改了线程一中监视的money
(integer) 600

回到线程1,执行事务:

127.0.0.1:6379> EXEC # 执行之前,另一个线程修改了我们的值,这个时候就会导致事务执行失败
(nil) # 没有结果,说明事务执行失败

127.0.0.1:6379> get money # 线程2 修改生效
"600"
127.0.0.1:6379> get use # 线程1事务执行失败,数值没有被修改
"0"

3. Jedis事务操作

1、导入依赖

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.6.3</version>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.70</version>
</dependency>

2、连接数据库

  • 连接本机:Jedis jedis = new Jedis("127.0.0.1", 6379);

  • 连接阿里云(远程)https://www.cnblogs.com/qi-chao/p/15163614.html

3、测试事务

public static void main(String[] args) {

    // Jedis jedis = new Jedis("127.0.0.1", 6379); //;连接本机
    Jedis jedis = new Jedis("106.14.34.181", 6379);
    jedis.auth("123");
    System.out.println(jedis.ping());  //测试连接

    JSONObject jsonObject = new JSONObject();
    jsonObject.put("hello", "world");
    jsonObject.put("name", "qichao");
    // 开启事务
    Transaction multi = jedis.multi();
    String result = jsonObject.toJSONString();
    // jedis.watch(result)
    try {
        multi.set("user1", result);
        multi.set("user2", result);
        // 执行事务
        multi.exec();
    }catch (Exception e){
        // 放弃事务
        multi.discard();
    } finally {
        // 关闭连接
        System.out.println(jedis.get("user1"));
        System.out.println(jedis.get("user2"));
        jedis.close();
    }
}
上一篇:Redis事务


下一篇:HTML CSS3变形移动、旋转、缩放、3d 、动画 拉伸布局等笔记