企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
[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