[TOC]
# 简介
Redis和传统的关系型数据库一样,因为具有持久化的功能,所以也有事务的功能!
有关事务相关的概念和介绍,这里就不做介绍。
在学习Redis的事务之前,首先抛出一个面试的问题。
面试官:请问Redis支持事务吗?如果支持和传统的关系型数据的事务有什么区别?
应试者:支持,但是是部分支持。Redis的事务和传统的关系型数据库事务有点不一样,传统的数据库事务一组操作单元,要么全部成功,要么全都失败。而Redis在执行一个命令集合的时候,可能会出现集合的一些命令成功,一些命令失败。
# 参考文档
Redis 事务-中文官网:http://www.redis.cn/topics/transactions.html
Redis 事务| 菜鸟教程 :http://www.runoob.com/redis/redis-transactions.html
# 总结:
1. 是什么?
可以一次执行多个命令,本质是一组命令的集合。一个事务中的所有命令都会序列化,按顺序地串行化执行而不会被其它命令插入,不许加塞。
2. 能做什么?
一个队列中,一次性、顺序性、排他性的执行一系列命令
3. 常用命令
~~~
1 DISCARD
取消事务,放弃执行事务块内的所有命令。
2 EXEC
执行所有事务块内的命令。
3 MULTI
标记一个事务块的开始。
4 UNWATCH
取消 WATCH 命令对所有 key 的监视。
5 WATCH key [key ...]
监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。
~~~
4. 怎么玩?
案例1:正常执行
~~~
127.0.0.1:6379> KEYS *
(empty list or set)
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> set k3 v3
QUEUED
127.0.0.1:6379> EXEC #执行,全部执行成功
1) OK
2) OK
3) OK
127.0.0.1:6379> MGET k1 k2 k3 #查看执行结果
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379>
~~~
案例2:放弃事务
~~~
127.0.0.1:6379> MULTI #开始事务
OK
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> set k5 v5
QUEUED
127.0.0.1:6379> set k1 v11 #这里发现出错了,第一次设置k1 了
QUEUED
127.0.0.1:6379> DISCARD #放弃事务
OK
127.0.0.1:6379> MGET k1 k2 k3 k4 k5 #k4 k5 的值没有设置成功
1) "v1"
2) "v2"
3) "v3"
4) (nil)
5) (nil)
127.0.0.1:6379>
~~~
案例3:全体连坐(一个出错,全部执行失败)
~~~
127.0.0.1:6379> KEYS * #查看当前的keys
1) "k1"
2) "k3"
3) "k2"
127.0.0.1:6379> MULTI #开始事务
OK
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> set k5 v5
QUEUED
127.0.0.1:6379> getandset k1 #这里有个操作出错 ,下面报错,
(error) ERR unknown command 'getandset'
127.0.0.1:6379> set k6 v6 # 这个命令依旧执行成功,放到Queued队列
QUEUED
127.0.0.1:6379> EXEC #执行,出错
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> MGET k1 k2 k3 k4 k5 k6 #k4 k5 k6 的值都没有设置成功,操作全部失败
1) "v1"
2) "v2"
3) "v3"
4) (nil)
5) (nil)
6) (nil)
127.0.0.1:6379>
~~~
案例4:冤头债主(一个出错,出错的不执行,其他的执行成功!)
~~~
127.0.0.1:6379> set k1 1 #将k1 修改为 1
OK
127.0.0.1:6379> keys *
1) "k1"
2) "k3"
3) "k2"
127.0.0.1:6379> MULTI #开启事务
OK
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> set v5 v5
QUEUED
127.0.0.1:6379> INCR k1 #k1 加1
QUEUED
127.0.0.1:6379> INCR k2 #k2 加1 ,因为k2的值为v2 ,这里最后执行会报错
QUEUED
127.0.0.1:6379> INCR k3 #k3 加1
QUEUED
127.0.0.1:6379> set k6 v6
QUEUED
127.0.0.1:6379> EXEC #执行,除了 k2 k3 执行失败,其他的都执行成功。
1) OK
2) OK
3) (integer) 2
4) (error) ERR value is not an integer or out of range #INCR k2 执行出错
5) (error) ERR value is not an integer or out of range #INCR k3 执行出错
6) OK
127.0.0.1:6379> MGET k1 k2 k3 k4 v5 k6 #看到k4 v5 k6 设置成功
1) "2"
2) "v2"
3) "v3"
4) "v4"
5) "v5"
6) "v6"
127.0.0.1:6379>
~~~
案例5:watch监控(watch可以监控多个key,使用watch进行key的监控,相当于给key上锁,如果在事务中,监控的key的value发生变化,则整个事务的全部命令都执行失败)
~~~
127.0.0.1:6379> KEYS *
1) "k3"
2) "k4"
3) "k2"
4) "v5"
5) "k1"
6) "k6"
127.0.0.1:6379> MGET k1 k2 k3 k4 v5 k6
1) "2"
2) "v2"
3) "v3"
4) "v4"
5) "v5"
6) "v6"
127.0.0.1:6379> WATCH k1 #监控k1 第一步
OK
127.0.0.1:6379> MULTI #开始事务 第二步
OK
127.0.0.1:6379> set k7 v7 #第三步
QUEUED
127.0.0.1:6379> set k2 v2222 #第四步
QUEUED
127.0.0.1:6379> set k3 v3333 #第五步
QUEUED
127.0.0.1:6379> set k8 v8 #第六步
QUEUED
127.0.0.1:6379> EXEC #第七步 #执行结果为nil ,说明执行失败
(nil)
127.0.0.1:6379> MGET k1 k2 k3 k4 v5 k6 k7 k8
1) "110" #k1 的值被改了 ,其他的事务中的值都没有成功
2) "v2"
3) "v3"
4) "v4"
5) "v5"
6) "v6"
7) (nil)
8) (nil)
127.0.0.1:6379>
--
在执行第三 —— 第六步之间,使用另一个客户端修改k1 的值为 成110
另一个客服端执行:
127.0.0.1:6379> set k1 110
OK
~~~
案例5解释:因为另一个客服端修改 了k1的值,但是k1的值是被监控的,事务在执行的时候发现k1的值被修改了,则事务中的其他操作命令也不执行,即执行失败。
5. 执行阶段
开启:以MULTI开始一个事务
入队:将多个命令入队到事务中,接到这些命令并不会立即执行,而是放到等待执行的事务队列里面
执行/撤销:由EXEC/Discard 命令触发/撤销事务
6. 总结
* 单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断
* 没有隔离级别的概念:队列中的命令没有提交之前都不会实际的被执行,因为事务提交前任何指令都不会被实际执行,也就不存在”事务内的查询要看到事务里的更新,在事务外查询不能看到”这个让人万分头痛的问题
* 不保证原子性:redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚
# 备注知识 :
## 悲观锁:
悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block
直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁.
## 乐观锁:
乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,
乐观锁策略:提交版本必须大于记录当前版本才能执行更新
## CAS(check and set)
悲观锁和乐观锁相关的参考博文:
Java 中的悲观锁和乐观锁的实现:https://toutiao.io/posts/400085/app_preview
乐观锁的一种实现方式:CAS:http://blog.csdn.net/qq32933432/article/details/51036361
- SQL
- 名词
- mysql
- 初识mysql
- 备份和恢复
- 存储引擎
- 数据表损坏和修复
- mysql工具
- 数据库操作
- 增
- 删
- 改
- 查
- 数据类型
- 整数类型
- 小数类型
- 日期时间类型
- 字符和文本型
- enum类型
- set类型
- 时间类型
- null与not null和null与空值''的区别
- 数据表操作
- 创建
- 索引
- 约束
- 表选项列表
- 表的其他语句
- 视图
- sql增删改查
- sql增
- sql删
- sql改
- sql查
- sql语句练习
- 连接查询和更新
- 常用sql语句集锦
- 函数
- 字符函数
- 数值运算符
- 比较运算符与函数
- 日期时间函数
- 信息函数
- 聚合函数
- 加密函数
- null函数
- 用户权限管理
- 用户管理
- 权限管理
- pdo
- 与pdo相关的几个类
- 连接数据库
- 使用
- pdo的错误处理
- pdo结果集对象
- pdo结果集对象常用方法
- pdo预处理
- 常用属性
- mysql编程
- 事务
- 语句块
- mysql中的变量
- 存储函数
- 存储过程
- 触发器
- mysql优化
- 存储引擎
- 字段类型
- 三范式和逆范式
- 索引
- 查询缓存
- limit分页优化
- 分区
- 介绍
- 分区算法
- list分区
- range范围
- Hash哈希
- key键值
- 分区管理
- 特别注意
- 分表
- 数据碎片与维护
- innodb表压缩
- 慢查询
- explain执行计划
- count和max,groupby优化
- 子查询优化
- mysql锁机制
- 介绍
- 演示
- 总结
- 乐观锁和悲观锁
- 扛得住的mysql
- 实例和故事
- 系统参数优化
- mysql体系结构
- mysql基准测试
- 索引
- mysql的复制
- win配置MySQL主从
- mysql5.7新特性
- 常见问题
- general log
- 忘记密码
- uodo log与redo log
- 事务隔离级别
- mysql8密码登录
- explain
- 高效的Tree表
- on delete cascade 总结
- mongod
- 简介
- 集合文档操作语句
- 增删改查
- 索引
- 数据导入和导出
- 主从复制
- php7操作mongod
- 权限管理
- redis
- redis简介
- 3.2版本配置文件
- 3.0版本配置文件
- 2.8版本配置文件
- 配置文件总结
- 外网连接
- 持久化
- RDB备份方式保存数据
- AOF备份方式保存数据
- 总结
- win安装redis和sentinel部署
- 事务
- Sentinel模式配置
- 分布式锁
- 管道
- php中redis代码
- 发布订阅
- slowlog
- Redis4.0
- scan和keys
- elasticsearch
- 配置说明
- 启动
- kibana
- kibana下载
- kibana配置文件
- kibana常用功能
- 常用术语
- Beats
- Beats简介
- Filebeat
- Packetbeat
- Logstash
- 配置
- elasticsearch架构
- es1.7
- head和bigdesk插件
- 插件大全
- 倒排索引
- 单模式下API增删改查
- mget获取多个文档
- 批量操作bulk
- 版本控制
- Mapping映射
- 基本查询
- Filter过滤
- 组合查询
- es配置文件
- es集群优化和管理
- logstash
- kibana
- es5.2
- 安装
- 冲突处理
- 数据备份
- 缺陷不足
- 集群管理api
- 分布式事务
- CAP理论
- BASE模型
- 两阶段提交(2PC)
- TCC (Try-Confirm-Cancle)
- 异步确保型
- 最大努力通知型
- 总结