说到事物,一般的文章会从ACID开始讲,一直讲到事物的传播特性。
今天这篇文章就讲事物的隔离级别,不讲ACID,也不讲事物的传播特性。
在讲事物的隔离级别之前先来了解一下事物并发情况下可能会出现的三个问题,了解了这三个问题后,那么事物的隔离级别就很好理解了。
事物并发可能会引起的问题
脏读
脏读指的是一个事物可以读取到另一个未提交事物的数据。
举个例子:A向B转账1000元买包。A开启事物并转账1000元给B,但是A的事物还没提交,此时A让B查看余额,B一看1000元到账了,就把包给A了。然后B拿着包就跑了,并且造了一个异常导致事物回滚,此时B会发现自己账户里刚刚到手的1000元又没有了,但是包已经被A拿跑了,导致人财两空。
这就是脏读,在B的事物里能查看到A事物未提交的数据。
不可重复读
不可重复读指的是一个事物可以读取到另一个事物提交了的数据。会导致在一个事物里的两次查询结果可能会不一致。
举个例子:这次是A向B借1000元。B开启了一个事物看了一下自己账户余额只有700元,告诉B自己钱不够,此时事物还没有关闭,A不相信,让B再查一次看看,结果B当着A的面又查了一次,显示卡里还有1200元(B老婆刚刚给B转了500元)。A当场就炸了,你这明明有钱还说没有,B马上回应刚刚还没有的,A又怼回去,你这事物还没关闭,同一个事物内两次查询结果怎么会不一样?你就是不想接给我,哼!友尽。
这就是不可重复读,一个在B的事物里能查到B的老婆后提交的数据。
幻读
幻读又叫虚读指的是在一个事物的查询重出现了之前未出现的数据。
幻读和不可重复读的区别在于幻读针对的是数据库重数据增删带来的变化,而不可重复读针对的是对数据库中某一条数据的修改带来的变化。
举个例子:由于最近开支越来越大,A决定打印一份银行卡消费记录出来反思一下,于是去银行帮忙开启一个事物开始打印消费记录,正打印着呢,突然想了想还是打两份好一点,给一份给老婆也反思反思,于是在事物还没关闭的情况下又打印了一份消费记录。此时A的老婆正拿着卡在买买买,于是等两份消费记录一起打印出来的时候A就很蒙圈了,我这是在同一个事物里面的两次查询呀,怎么第二份消费记录还多了个2000块钱的包包,这银行是在骗我吧,不行,赶紧打投诉电话。
事物的四种隔离级别
上面已经分析了事物并发可能会引起的三个问题,那么这里再讲一下事物的隔离级别。
事物的隔离级别一共有四个:
读未提交(read-uncommitted)
读已提交(read-committed)
可重复读(repeatable-read)
序列化(serializable)
对着上面脏读、不可重复读和幻读来理解这四种事物隔离级别就很容易了,下面看一个表格:
通过上面的表格可以很清楚的看出每个隔离级别是否会发生哪种事物并发问题。
事物隔离级别性能问题
通过上面的表格可以看出事物隔离级别越高越安全,这里再来聊一下不同事物隔离级别的性能问题。
首先先来看一下不同事物隔离级别对于数据库的锁的问题:
读未提交:不会对数据库产生锁
读已提交:写数据只会锁住相应的行
可重复读:如果有索引(包括主键索引)的时候,以索引列为条件更新数据,会存在间隙锁间隙锁、行锁、下一键锁的问题,从而锁住一些行;如果没有索引,更新数据时会锁住整张表
序列化:读写数据都会锁住整张表
所以事物的隔离级别越高越能保证数据的完整性和一致性,但是对并发情况下性能的影响也就越大。
Mysql的默认隔离级别是可重复读;Sql Server和Oracle默认的事务隔离级别是读已提交。当然了这些默认的事物隔离级别都是可以修改的。
由于性能原因,一般不会将事务隔离级别设置成序列化,而只会设置成读已提交和可重复读,至于可能发生数据不一致的问题,可以使用乐观锁、悲观锁等方式去解决。
领取专属 10元无门槛券
私享最新 技术干货