目录
一、Redis事务的本质
Redis事务的本质是一组命令的集合,相当于一个队列,一个事务中的的所有命令都会被序列化,在事务执行过程中,按照顺序执行,且不会被中断,所以redis的事务有一次性(执行完则当前事务结束,下次事务需要重新开启)、顺序性、排他性。Redis中的事务是比较简单的。
二、事务的操作命令
先说一下事务的操作,这样在介绍Redis特性的时候举例也比较方便
multi #开启事务
set name "zs" #回车,将命令添加到事务队列
exec #执行事务
discard #放弃事务
三、不存在原子性
在MySQL中,事务具有一致性,而一致性由原子性保证,即事务中所有的sql要么都成功,要么都失败,在Redis中,单条命令具有原子性,而事务是不具备原子性的
1)事务中命令语法错误(不体现非原子性)
如果一个事务中存在有语法错误的指令,类似于Java中的编译时异常,即检查错误,此时,这个事务中的所有命令均不会被执行
第一条命令是错误的,在放入队列的时候就已经提示错误了,但是后边的命令依旧可以加入事务队列,只是在exec的时候会自动放弃事务,所有命令都不执行。
2)事务中存在非语法错误(体现非原子性)
如果事务中存在非语法错误,类似于Java中的运行时异常,此时没有错误的指令依旧会正常执行,举例说明:
首先存入一个name字符串,然后开启事务,第一条指令对name字符串进行自增1,就相当于Java中的10 / 0,是个运行时异常,但是它可以成功加入队列,最终执行结果我们可以看到,除了第一条命令未执行,其余均执行,所以Redis事务并非原子性
三、不存在隔离性
在redis中事务中所有的命令并没有真正地执行,而是放到了事务队列中,所以不存在MySQL中的脏读、幻读、不可重读等问题,所以没有隔离性。
四、乐观锁
乐观锁:对应于现实生活中乐观的人,即把什么事都想得比较好,在进行数据访问的时候,总是认为没有人访问,所以乐观锁是在使用数据的时候不会上锁,而是在最后更新的时候通过版本号机制或CAS算法判断有没有其他线程更新,再决定是否驳回操作。
redis中的乐观锁机制使用watch实现的,类似于MySQL的版本号机制,首先使用watch对key对应的value进行监听,然后开启事务,将命令放入队列,在执行的时候,监听会再次读取被监听的key的value,如果此时的value与最初监听的value不一致,说明有另外的线程操作过数据,那么exec命令执行失败,所有命令不执行,下面通过两个redis-cli模拟两个线程操作数据:
上面两个线程模拟存钱取钱,首先线程1执行命令:
1)存入余额money=100 ---> set money 100
2)支出out=0 ---> set out 0
3)然后对money进行监听,此时监听money=100 ---> catch money
4)然后开启事务 ---> multi,取10元命令加入队列 ---> decrby out 10 (对out自减10,此处10相当于java的增量)
5)支出加10命令加入队列,以上命令均只加入事务队列不执行 ---> incrby money 10
线程2执行命令:
1)先获取一下money=100,获得money的值为100 ---> get money
2)然后对money+10,此时money变为110 ---> incrby money 10
下面执行线程1的提交事务,返回nil,即事务未执行成功:
在使用exec执行事务的时候,监听会再次查看money的值是否跟监听时的一致,如果不一致则驳回操作,跟MySQL中的版本号(select version)一个道理。这里需要注意的是,如果想要再次开启事务,需要先用unwatch命令取消对money的监听,因为监听的还是money=100,然后再次watch新的money。