redis事务的介绍
redis事务的本质:一组命令的集合。redis单条命令保证原子性,但是事务不保证原子性。
一个事务中所有的命令都会被序列化,在事务执行过程中,会按照顺序执行,但是redis事务是没有隔离级别的概念的。所以redis的事务具有以下特性:一次性,顺序性,排他性。
redis事务的执行过程:
- 开启事务。
- 命令入队。
- 执行事务。
如何使用事务
multi // 开启事务
// 以下操作就是命令入队
set key1 value1
set key2 value2
get key1
exec // 执行事务
multi // 开启事务
// 以下操作就是命令入队
set key1 value1
set key2 value2
get key1
DISCARD // 放弃事务,放弃后队列里的命令都不会执行
事务的注意事项
-
当事务执行时,发生了编译型异常(代码有问题),事务汇总的所有命令都不会被执行。
multi // 开启事务 testredis key1 value1 //这里的testredis命令根本不存在,所以编译时就会报错,导致编译型异常 set key2 value2 //因为上面的编译型异常,下面的正常的命令都不会执行 get key1 exec // 执行事务
-
运行时异常,如果编译通过了,但是事务队列中存在逻辑上的错误导致出错,除报错的命令,其他的命令都会正常执行下去。
multi // 开启事务 get key1 //这里的key1事先根本没有设置,就会报错,导致运行型异常 set key2 value2 //因为上面是运行型异常,下面的正常命令都还是会执行,也是redis事务不保证原子性的体现 get key2 exec // 执行事务
-
redis是没有事务回滚的。
redis实现乐观锁
悲观锁:很悲观,认为什么时候都会出现问题,所以无论做什么都会直接加锁。
乐观锁:很乐观,认为什么时候都不会出问题,所有不会直接加锁。而是需要操作的时候会先去获取一次值,更新数据的时候去判断一下,之前获取到的值和该数据现在的值是否一致,如果一致表示中间没有被修改过,这时再去更新。
- 正常情况下
set balance 10000 // 初始化余额
set consume 0 //初始化消费金额
watch balance //开启对balance的监视
multi //开启事务
decrby balace 100 // 余额减100
incrby consume 100// 消费的金额增加100
exec //提交事务
- 非正常情况
set balance 10000
set consume 0
watch balance
multi
decrby balace 100
incrby consume 100
exec //前面的操作是一模一样的,但是这里假如在使用exec提交事务之前,有人执行了下面的命令对数据进行了修改,这里监视到balance被修改过,那么整个事务执行会失败
incrby balance 100
- 取消监视
unwatch//比如你监视错了对象,取消监视,然后再重新写一次
watch balance
multi
....
exec
和关系型数据库对比
redis作为非关系型数据库,和关系型数据库还是有着许多区别,接下来就和关系型数据库里常用的mysql进行对比,并分析出他们之间对事务的不同解释。
Mysql
-
mysql中的事务有4大特性:原子性、一致性、隔离性、持久性。
- 原子性 (Atomicity):事务中的全部操作在数据库中是不可分割的,要么全部完成,要么均不执行。
- 一致性 (Consistency):几个并行执行的事务,其执行结果必须与按某一顺序串行执行的结果相一致。
- 隔离性 (Isolation):事务的执行不受其他事务的干扰,当数据库被多个客户端并发访问时,隔离它们的操作,防止出现:脏读、幻读、不可重复读。
- 持久性 (Durability):对于任意已提交事务,系统必须保证该事务对数据库的改变不被丢失。
-
针对脏读(一个事务读取到另一个事务未提交的数据),不可重复读(一个事务读取到另一个事务已经提交的数据),幻读(一个事务多次查询整表数据,由于其他事务新增(删除)记录造成多次查询的记录条数不同(一个事务读取到另一个事务已经提交的数据)),提出四大隔离级别。
- read uncommitted——不作任何隔离,具有脏读、不可重复读、幻读问题
- read committed——可防止脏读,不能防止不可重复读和幻读问题
- repeatable read——可以防止脏读、不可重复读,不能防止幻读问题(mysql默认是这个隔离级别)
- serializable——数据库运行在串行化,上述问题都可以防止,只是性能非常低
Redis
- 不保证原子性:redis同一个事务中如果发生了运行时异常(编译时异常会执行失败),其后的命令仍然会被执行。
- 没有隔离级别的概念:队列中的命令没有提交之前都不会实际的被执行。
- 顺序性和排他性:事务中所有的命令都会被序列化,是先入队,再执行的操作。所以都是按照队列里的顺序挨个执行的,执行的过程中,不会被其他客户端发送来的命令请求所打断。