Redis与 mysql事务的对比
- 开启命令:mysql 用start transaction redis 使用 multi
- 执行内容:mysql 执行普通sql语句 redis 执行普通命令
- 失败取消:mysql 用rollback进行回滚 redis 用discard 取消本次事务执行
- 提交确认:mysql用commit 提交本次事务 redis 用exec
redis事务执行过程:
执行multi之后
redis把后续的命令放入一个队列,
- 如果命令语法错误(不是执行错误),则本次事务无法exec提交,可以用discard取消比如:
1 2 3 4 5 6 7
127.0.0.1:6379> flushdb OK 127.0.0.1:6379> mset a 100 b 200 c 300 OK 127.0.0.1:6379> multi OK 127.0.0.1:6379> dcerby a 50 (error) ERR unknown command 'dcerby' 127.0.0.1:6379> decr a 50 (error) ERR wrong number of arguments for 'decr' command
- 如果语法都正确,最后执行exec后,有命令执行错误,则会跳过错误,继续执行,此时执行discard依然无法撤销正确命令的影响比如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
127.0.0.1:6379> mget a b c 1) "100" 2) "200" 3) "300" 127.0.0.1:6379> multi OK 127.0.0.1:6379> decr a QUEUED 127.0.0.1:6379> sadd b 5 QUEUED 127.0.0.1:6379> decr c QUEUED 127.0.0.1:6379> exec 1) (integer) 99 2) (error) WRONGTYPE Operation against a key holding the wrong kind of value 3) (integer) 299 127.0.0.1:6379> discard (error) ERR DISCARD without MULTI 127.0.0.1:6379>
有上面的测试可以发现,redis的事务 中的命令,exec之前并不是真正的执行了,而是放到了一个队列中,最后exec时才执行。
redis简单的事务支持明显不符合正常逻辑。
如,库存有1件商品,此时有两个人(A,B)去购买 测试:
在两个终端分别开启两个事务
终端1
1 2 3 4 5 6 7 8 9 10 11 12 | 127.0.0.1:6379> 127.0.0.1:6379> set agg 1 OK 127.0.0.1:6379> get agg "1" 127.0.0.1:6379> multi OK 127.0.0.1:6379> decr agg QUEUED 此时去了终端2里面执行一个完整的事务,且agg的只变成了0 127.0.0.1:6379> exec 1) (integer) -1 127.0.0.1:6379> |
终端2
1 2 3 4 5 6 7 8 9 10 11 12 | [root@zhang bin]# ./redis-cli 127.0.0.1:6379> get agg "1" 127.0.0.1:6379> 此时在终端2执行一个事务 127.0.0.1:6379> multi OK 127.0.0.1:6379> decr agg QUEUED 127.0.0.1:6379> exec 1) (integer) 0 现在回到终端1去执行exec 127.0.0.1:6379> |
对比可以看出,redis并没有mysql中事务隔离级别
不过好在redis提供了watch(unwatch) 用来监视key的变化
和上面相同的命令,只需要在终端1中multi前面加上watch agg
则不会发生agg值变成负一的情况
watch 可以监控多个值,如果有一个变化,则整个事务执行取消。(exec 执行结果提示nli)
redis中使用的是乐观锁,也就是在最后执行exec的时候才去检查watch的监测的key是否有变化