mysql的事务隔离级别
Posted Dolphin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mysql的事务隔离级别相关的知识,希望对你有一定的参考价值。
事务的四大特性(ACID):
1.原子性(atomicity):一个事务必须视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作,这就是事务的原子性。
2.一致性(consistency):数据库总数从一个一致性的状态转换到另一个一致性的状态,比如无论a给b怎么转账或b转a,两个人最后的金额加起来==没转账前的总金额。
3.隔离性(isolation):一个事务所做的修改在最终提交以前,对其他事务是不可见的。
4.持久性(durability):一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失,因为数据已经保存在硬盘中了。
mysql事务隔离级别有4个级别:
数据库事务的隔离级别有4种,由低到高分别为Read uncommitted 、Read committed 、Repeatable read 、Serializable 。而且,在事务的并发操作中可能会出现脏读,不可重复读,幻读。下面通过事例一一阐述它们的概念与联系。
1.Read uncommitted
读未提交,顾名思义,就是一个事务可以读取另一个未提交事务的数据。
----------------------------------------------------------------------------------------------------------------------------
mysql数据库表准备工作,创建表并初始化数据:
create table account(
`id` int(11) NOT NULL AUTO_INCREMENT,
`balance` int NOT NULL DEFAULT 0,
PRIMARY KEY (`id`)
);
insert into account values(1,1000);
insert into account values(2,1000);
insert into account values(3,1000);
insert into account values(4,1000);
----------------------------------------------------------------------------------------------------------------------------
例子:
set session transaction isolation level Repeatable read;// 设置mysql的事务隔离级别为Read uncommitted
select @@transaction_isolation;// 查看当前事务隔离级别(检查是否设置成功)
set @@autocommit=0; // 把mysql的自动提交设置为false
start transaction;// 开启一个事务
注:这里打开两个终端来测试,分别代表一个事务,左右各一个。
把上面的语句按顺序在两个终端各执行一遍后可以看到两个事务的隔离级别都是读未提交。
1.分别在两个事务中查看表的数据,如图:
2.在右边终端执行update account a set a.balance=a.balance - 200 where a.id=1,再次查看两个终端的表数据,如图:
这就是脏读,读未提交隔离级别什么都防止不了。此时如果右终端回滚事务,那么之前update也就相当于没执行,而左终端读取了update后的数据去进行了一些业务操作,那么后果将是很严重的。因为读取数据是不准确的。
举个栗子:
老板要给程序员发工资,程序员的工资是3.6万/月。但是发工资时老板不小心按错了数字,按成3.9万/月,但是事务还没有提交,就在这时,程序员去查看自己这个月的工资,发现比往常多了3千元,以为涨工资了非常高兴。但是老板及时发现了不对,马上回滚差点就提交了的事务,将数字改成3.6万再提交。
分析:实际程序员这个月的工资还是3.6万,但是程序员看到的是3.9万。他看到的是老板还没提交事务时的数据。这就是脏读。
----------------------------------------------------------------------------------------------------------------------------
如何解决脏读的发生?----->Read committed 读已提交
读提交,顾名思义,就是一个事务要等另一个事务提交后才能读取它的数据,否则是读取不到另外一个事务的更改的。
1.将数据库中的事务隔离级别修改为Read committed,并左右终端都开启事务(可参考上面的语句),查询表数据,效果如图:
2.在右边终端执行update account a set a.balance=a.balance - 200 where a.id=1,再次查看两个终端的表数据,如图:
虽然读已提交解决了脏读的问题,又发生了问题:
比如左终端事务id为1的用户去超市消费了800,在刷卡结账的时候查询卡里有1000元,就在这时,右终端事务也就是用户的女朋友,把卡里的钱全部转到了她自己的支付宝上,并提交了事务,然后用户输入密码扣款,收银员告诉用户说卡里没有钱了(这是用户脑子在想:“刚刚刷的时候我还看到有1000的啊,怎么突然就没了”,这个时候用户的电话响了,打给他的是他女朋友跟他说“我要用钱买点东西就把你卡里的钱提到我支付宝上了”)这时候用户才缓过神来,不然以为见鬼了呢!
用实际行动来证明上面的问题是存在的:
(用户女朋友)假设右终端是用户的女朋友,把钱提到支付账号金额为200(执行的语句就是第二步的sql),并提交执行commit
(用户自己)执行扣款,扣款金额是900,执行sql:update account set balance = balance-900 where id =1
假设超市的扣款系统会再次去查询用户的金额,判断是否足够再进行扣款那么就将导致扣款失败,因为被女朋友取走200只剩800了。如果没有二次查询用户的金额那么数据库表中id为1的用户的金额就是-100了,在现实中是不允许的。强行扣款后的数据如图:
这就是读已提交,若有事务对数据进行更新操作时,读操作事务要等待这个更新操作事务提交后才能读取数据,可以解决脏读问题。但在这个事例中,出现了一个事务范围内两个相同(刷卡时查询和扣款时再次查询)的查询却返回了不同数据,这就是不可重复读。
----------------------------------------------------------------------------------------------------------------------------
那么如何解决不可重复读?----->Repeatable read 可重复读
可重复读,就是在开始读取数据(事务开启)时,不再允许修改操作(insert/delete操作除外,这会出现一个新问题(幻读)下面会讲到)
比如用户在刷卡时开启了事务,这时用户的女朋友就无法进行转账(update操作),这样就防止了钱被用户女朋友转走的可能也就不会发生扣款失败了。
1.将数据库中的事务隔离级别修改为Repeatable read,并左终端先扣1000元,先不提交,再右终端扣1000此时被阻塞了,因为左终端还未提交。效果如图:
2.当左终端提交时,右终端才能执行(可以看到左终端先执行的update,而右终端再次执行就阻塞了,谁阻塞就说明前面有人比它先update操作具体的用户,比如这两个update都是id1)
重复读可以解决不可重复读问题。写到这里,应该明白的一点就是,不可重复读对应的是修改,即UPDATE操作。但是可能还会有幻读问题。因为幻读问题对应的是插入INSERT操作,而不是UPDATE操作。
幻读:比如左终端开启了一个事务要把所有用户的信息打印,第一次查看的数据是只有4个用户,如下图:
而在此时,右终端新增了一个用户,并提交了事务,这个时候左终端再打印出来,就发现多了一个用户(点击打印的时候左终端已经把刚刚的事务提交了重新执行了一次select(不管此次有无事务),不然是看不到右终端新增的用户的)。如下图:
这个时候左终端就会发现难道我见鬼了????(可重复度(update操作)和幻读(insert/delete操作)很像),那么如何解决这种在页面上显示的数据(开启了一个事务执行select)和打印的数据(又执行了一次select不管有无事务)不一致的情况呢?
Serializable 序列化 就是解决幻读问题的,可重复读是行级锁,而Serializable是表级锁,把整张表锁住了。
把数据库隔离级别设置未Serializable 再次执行重复度发生的问题,执行效果如图:
这样就防止了打印和看到的结果不一致情况。
总结:Read uncommitted读未提交(什么都不能保障导致脏读)
Read committed 读已提交(可以解决脏读但会导致不可重复读)
Repeatable read 可重复读(可以解决不可重复读但会导致幻读)
Serializable 序列化 就是解决幻读问题的,可重复读是行级锁,而Serializable是表级锁。
mysql默认的事务隔离级别是Repeatable read 可重复读。Serializable很少用到,因为这会严重影响性能。
以上是关于mysql的事务隔离级别的主要内容,如果未能解决你的问题,请参考以下文章