本文最后更新于 259 天前,其中的信息可能已经过时,如有错误请发送邮件到wuxianglongblog@163.com
Redis的事物
一.简要阐述MySQL事务和Redis事务的区别
MySQL的事务:
基于事物日志,悲观锁机制,MVCC,ISOLASTION等机制一起保证,属于强事务支持。
在MySQL中我们执行一个事务其实是在COMMIT之前就已经修改数据了,但底层是通过隔离级别,MVCC,redo log,undo log来实现数据的一致性。
Redis的事务:
基于队列实现的,Redis是乐观锁机制,仅实现了原子性的保证,属于弱事务支持。
在Redis中事务被提交到队列,如果我们不想执行事务可以将对应的事务操作从队列中删除即可。需要注意的是,Redis中的事务提交到队列后,并不会对原数据进行修改,直到调用了EXEC指令后才会执行事务哟。
温馨提示:
综上所述,Redis的事务我们生产环境中并不会经常性使用,因为它仅实现了原子性,对于持久性,隔离性,一致性的并没有实现。因此如果非要用事务的话可以考虑MySQL,Oracle之类的数据库。
但是我们对于Redis事务这一块的知识点多少是要了解一点的,因为这也是面试的一个高频问点。
二.Redis事务的实战案例
1.Redis事务初体验
(1)单独开启一个终端执行事务:
[root@redis201.oldboyedu.com ~]# redis-cli -a oldboyedu2021 -n 13 --raw
127.0.0.1:6379[13]> KEYS *
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> MULTI # 有点类似于MySQL的"BEGIN"指令,意义在于开启事务!
OK
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> SET name oldboyedu # 注意观察,此指令被提交到队列中,并没有被真正执行,下同。
QUEUED
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> SET age 18
QUEUED
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> KEYS * # 注意观察,当前的事务已经被提交到队列中,但并未真正执行!
QUEUED
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> EXEC # 当我们调用了EXEC指令,表示执行当前事务,则会创建此事务中的两个KEY哟~
OK
OK
age
name
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> KEYS *
age
name
127.0.0.1:6379[13]>
(2)在另一个终端单独开启一个事务,在上面的终端执行"EXEC"指令之前后,可以尝试获取KEY,是否可以拿到数据:
[root@redis201.oldboyedu.com ~]# redis-cli -a oldboyedu2021 -n 13 --raw
127.0.0.1:6379[13]> KEYS *
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> GET name # 很明显,当开启的事务未执行EXEC指令,我们是获取不到数据的!
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> get age
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> KEYS *
age
name
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> GET name # 但当开启的事务执行了EXEC指令后,我们是可以获取到数据的哟~
oldboyedu
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> get age
18
127.0.0.1:6379[13]>
2.Redis默认就是乐观锁机制,多个事务可以对同一个KEY进行操作
(1)终端1执行的指令
[root@redis201.oldboyedu.com ~]# redis-cli -a oldboyedu2021 -n 13 --raw
127.0.0.1:6379[13]> KEYS *
age
name
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> MULTI
OK
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> SET address 北京朝阳
QUEUED
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> KEYS *
QUEUED
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> EXEC # 请让终端2先执行EXEC指令后再来执行该指令,以方便看到实验效果
OK
address
age
name
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> KEYS *
address
age
name
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> GET address
北京朝阳
127.0.0.1:6379[13]>
(2)终端2执行的指令
[root@redis201.oldboyedu.com ~]# redis-cli -a oldboyedu2021 -n 13 --raw
127.0.0.1:6379[13]> KEYS *
age
name
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> MULTI
OK
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> SET address 北京海淀
QUEUED
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> KEYS *
QUEUED
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> EXEC
OK
address
age
name
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> KEYS *
address
age
name
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> GET address # 请让终端1先执行EXEC指令后再来执行该指令,以方便看到实验效果
北京朝阳
127.0.0.1:6379[13]>
3.Redis可以使用WATCH指令来监控一个KEY,若该KEY被其它的事务操作过,则本次事务执行失败。WATCH指令对于数据一致性要求较高的场景很有用,比如买飞机票,火车票等场景,一张票(ticket)只能卖给一个人
(1)终端1执行的指令:
[root@redis201.oldboyedu.com ~]# redis-cli -a oldboyedu2021 -n 13 --raw
127.0.0.1:6379[13]> KEYS *
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> WATCH name # 监控name这个KEY,如果该KEY被其它事务修改,则本次事务失效!
OK
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> MULTI
OK
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> SET name JasonYin # 为name的KEY赋值
QUEUED
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> EXEC # 执行时没有任何输出,说明本次事务未执行成功。请让终端2先执行EXEC指令后再来执行该指令,以方便看到实验效果哟~
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> KEYS * # 尽管上一条命令的事务未执行成功,但其它事务是执行成功的,因此我们可以看到该KEY.
name
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> GET name
oldboyedu
127.0.0.1:6379[13]>
(2)终端2执行指令
[root@redis201.oldboyedu.com ~]# redis-cli -a oldboyedu2021 -n 13 --raw
127.0.0.1:6379[13]> KEYS *
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> MULTI
OK
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> SET name oldboyedu
QUEUED
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> KEYS *
QUEUED
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> EXEC # 请让终端1先执行EXEC指令之前的所有指令后再来执行该指令,以方便看到实验效果哟~
OK
name
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> KEYS *
name
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> GET name
oldboyedu
127.0.0.1:6379[13]>
4.使用"DISCARD"指令可以丢弃当前事务
[root@redis201.oldboyedu.com ~]# redis-cli -a oldboyedu2021 -n 13 --raw
127.0.0.1:6379[13]> KEYS *
name
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> MULTI
OK
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> SET age 18
QUEUED
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> set address BeiJing
QUEUED
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> KEYS *
QUEUED
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> DISCARD # 执行该指令后,则当前事务的所有已经提交到队列的指令都将被删除!换句话说,就是放弃本次事务!
OK
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> KEYS *
name
127.0.0.1:6379[13]>
127.0.0.1:6379[13]> QUIT
[root@redis201.oldboyedu.com ~]#