深入浅出Redis(二)高级特性:事务
2023-09-11 14:15:00 时间
第一篇中介绍了Redis是一个强大的键-值仓储,支持五种灵活的数据结构。其实,Redis还支持其他的一些高级特性:事务、公布与订阅、管道、脚本等,本篇我们来看一下事务。
前一篇中我们提到,在Redis中每一个命令都是原子性的,由于Redis内部的实现是单线程的。当然Redis也支持多个命令之间的事务,只是事务在Redis中相对来说非常easy。不像数据库事务那样涉及传播级别、隔离级别等特性。
使用multi命令開始一个新的事务。exec命令提交,discard命令回滚。假设把信用卡的可用额度存入balance。欠额存入debt,在消费的时候就必须在一个事务内同一时候更新这两个键。
127.0.0.1:6379> set balance 100 OK 127.0.0.1:6379> set debt 0 OK 127.0.0.1:6379> multi OK 127.0.0.1:6379> decrby balance 25 QUEUED 127.0.0.1:6379> incrby debt 25 QUEUED 127.0.0.1:6379> exec 1) (integer) 75 2) (integer) 25 127.0.0.1:6379> get balance "75" 127.0.0.1:6379> get debt "25"
在multi命令后的其他命令,返回结果都是"QUEUED“,这些命令不会马上运行,仅仅是简单的在Server端缓存起来了。在发出exec命令后,他们才会被一起运行。或者discard命令回滚事务。
在Redis官方文档有指出事务的两个特点:
- 事务中的命令都是按着他们进入缓存队列的顺序依次运行的。在事务运行中,Redis不会受理其他client的命令(隔离性)
- 事务中的命令,或者所有运行,或者所有不运行。(原子性)
上面的样例是信用卡扣减,但实际中在做扣减时我们须要检查剩余金额是否足够,所以通常会这么做:
redis.multi() balance = redis.get('balance') if (balance < amtToSubtract) { redis.discard() } else { redis.decrby('balance', amtToSubtract) redis.incrby('debt', amtToSubtract) redis.exec() }
对于普通数据库事务,上面的代码没问题。但对于Redis事务来说行不通。由于在exec命令之前,全部的命令都被Redis缓存起来了,根本就拿不到balance的值。那类似这样的须要基于已经存在的某个值的事务在Redis中怎样实现呢?答案是Watch命令:
redis.watch('balance') balance = redis.get('balance') if (balance < amtToSubtract) { redis.unwatch() } else { redis.multi() redis.decrby('balance', amtToSubtract) redis.incrby('debt', amtToSubtract) redis.exec() }
通俗点讲,watch命令就是标记一个键。假设标记了一个键,在提交事务前假设该键被别人改动过,那事务就会失败,这样的情况通常能够在程序中又一次再尝试一次。
像上面的样例,首先标记了键balance,然后检查剩余金额是否足够,不足就取消标记。并不做扣减;足够的话,就启动事务进行更新操作,假设在此期间键balance被其他人改动,那在提交事务(运行exec)时就会报错。程序中通常能够捕获这类错误再又一次运行一次。直到成功。
Redis事务失败后不支持回滚
与数据库事务非常重要的一个差别是Redis事务在运行过程中出错后不会回滚。在exec命令后。Redis Server開始一个个的运行被缓存的命令,假设当中某个命令运行出错了,那之前的命令并不会被回滚。
127.0.0.1:6379> set value 1 OK 127.0.0.1:6379> set value2 abc OK 127.0.0.1:6379> multi OK 127.0.0.1:6379> incr value QUEUED 127.0.0.1:6379> incr value2 QUEUED 127.0.0.1:6379> exec 1) (integer) 2 2) (error) ERR value is not an integer or out of range 127.0.0.1:6379> get value "2" 127.0.0.1:6379>
exec提交事务后。在运行到incr value2时错误了(数据类型不对),但事务对value的操作却是生效的。这点能够从后面的get value的返回值看到。
相关文章
- 各种redis的介绍:ServiceStack.Redis,StackExchange.Redis,CSRedis
- Redis性能测试怎么做?看看大佬写的测试总结
- c#实例化继承类,必须对被继承类的程序集做引用 .net core Redis分布式缓存客户端实现逻辑分析及示例demo 数据库笔记之索引和事务 centos 7下安装python 3.6笔记 你大波哥~ C#开源框架(转载) JSON C# Class Generator ---由json字符串生成C#实体类的工具
- Golang操作Redis
- Redis开发与运维. 3.4 事务与Lua
- Redis之Redis事务
- 【Redis数据结构 序】使用redis-py操作Redis数据库
- Golang 实现 Redis(10): 本地原子性事务
- Redis基础之事务及乐观锁(三)
- Redis_redis分布式锁-SETNX
- Redis基础:特点(内存运行、原子性、持久化)、5种基本数据类型与3种特殊数据类型、redis命令、发布订阅机制、事务(单条命令原子性事务不是原子性)、数据备份与恢复、安全
- 中间件:Redis-x64-5.0.14.1集群主从复制(Win10)
- windows下使用Redis
- 【转】Redis概念原理、redis面试
- Redis(1.11)Redis4.0.11 cluster 分布式集群搭建
- Redis集群(三):主从配置一
- spring-data-redis工程
- 曹工说Redis源码(7)-- redis server 的周期执行任务,到底要做些啥