Redis-事务
1.1 为什么要用事务
我们知道 Redis 的单个命令是原子性的(比如 get set mget mset),如果涉及到多个命令的时候,需要把多个命令作为一个不可分割的处理序列,就需要用到事务。
例如我们之前说的用 setnx 实现分布式锁,我们先 set,然后设置对 key 设置 expire,防止 del 发生异常的时候锁不会被释放,业务处理完了以后再 del,这三个动作我们就希
望它们作为一组命令执行。
Redis 的事务有两个特点:
1、按进入队列的顺序执行。
2、不会受到其他客户端的请求的影响。
Redis 的事务涉及到四个命令:
multi(开启事务),exec(执行事务),discard(取消事务),watch(监视)。
1.2 事务的用法
案例场景:tom 和 mic 各有 1000 元,tom 需要向 mic 转账 100 元。tom 的账户余额减少 100 元,mic 的账户余额增加 100 元。
127.0.0.1:6379> set tom 1000
OK
127.0.0.1:6379> set mic 1000
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> decrby tom 100
QUEUED
127.0.0.1:6379> incrby mic 100
QUEUED
127.0.0.1:6379> exec
1) (integer) 900
2) (integer) 1100
127.0.0.1:6379> get tom
"900"
127.0.0.1:6379> get mic
"1100"
通过 multi 的命令开启事务。事务不能嵌套,多个 multi 命令效果一样。multi 执行后,客户端可以继续向服务器发送任意多条命令, 这些命令不会立即被执行,
而是被放到一个队列中, 当 exec 命令被调用时, 所有队列中的命令才会被执行。
通过 exec 的命令执行事务。如果没有执行 exec,所有的命令都不会被执行。如果中途不想执行事务了,怎么办?可以调用 discard 可以清空事务队列,放弃执行。
multi
set k1 1
set k2 2
set k3 3
discard
1.3 watch 命令
在 Redis 中还提供了一个 watch 命令。它可以为 Redis 事务提供 CAS 乐观锁行为(Check and Set / Compare andSwap),也就是多个线程更新变量的时候,
会跟原值做比较,只有它没有被其他线程修改的情况下,才更新成新的值。
我们可以用 watch 监视一个或者多个 key,如果开启事务之后,至少有一个被监视key 键在 exec 执行之前被修改了, 那么整个事务都会被取消(key 提前过期除外)。
可以用 unwatch 取消。
1.4 事务可能遇到的问题
我们把事务执行遇到的问题分成两种,一种是在执行 exec 之前发生错误,一种是在执行 exec 之后发生错误。
1.4.1 在执行 exec 之前发生错误
比如:入队的命令存在语法错误,包括参数数量,参数名等等(编译器错误)。
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set gupao 666
QUEUED
127.0.0.1:6379> hset qingshan 2673
(error) ERR wrong number of arguments for 'hset' command
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because
在这种情况下事务会被拒绝执行,也就是队列中所有的命令都不会得到执行。
1.4.2 在执行 exec 之后发生错误
比如,类型错误,比如对 String 使用了 Hash 的命令,这是一种运行时错误。
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 1
QUEUED
127.0.0.1:6379> hset k1 a b
QUEUED
127.0.0.1:6379> exec
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> get k1
"1"
最后我们发现 set k1 1 的命令是成功的,也就是在这种发生了运行时异常的情况下,只有错误的命令没有被执行,但是其他命令没有受到影响。
这个显然不符合我们对原子性的定义,也就是我们没办法用 Redis 的这种事务机制来实现原子性,保证数据的一致。
- Flash/Flex学习笔记(56):矩阵变换
- js小技巧:tab页切换
- c#字符串操作方法实例
- Android中Fragment+ViewPager的配合使用
- 结合机器学习与生物医学技术,寻找Uber司机出行模式
- ASP.NET MVC 4 - 测试驱动 ASP.NET MVC
- LVS+Keepalived高可用环境部署梳理(主主和主从模式)
- 随着区块链的火爆,相关顶级域名“矿池”KC.com已建站
- Flash/Flex学习笔记(50):3D线条与填充
- LVM常规操作记录梳理(扩容/缩容/快照等)
- Flash/Flex学习笔记(55):背面剔除与 3D 灯光
- 资源等待类型sys.dm_os_wait_stats
- NVIDIA不再允许数据中心用GeForce驱动,提供区块链服务除外
- 非常强悍并实用的双机热备+负载均衡线上方案
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 探花交友_搭建开发环境
- 编程体系结构(04):JavaIO流文件管理
- Hadoop框架:HDFS简介与Shell管理命令
- OpenCV的Mat类型以及基本函数使用
- Hadoop框架:HDFS读写机制与API详解
- 编程体系结构(06):Java面向对象
- RabbitMQ在分布式系统中的应用
- spring5新特性
- 进阶!MyBatis-Plus(基于 Springboot 演示)
- 运维人员常用的Linux命令总结
- Java反射机制的原理及在Android下的简单应用
- 温故而知新:MySQL存储引擎入门介绍
- 终于明白 Java 为什么要加 final 关键字了!
- 学习git这一篇就够了!!!
- 如何在nodejs中实现兄弟进程通信