最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

今日播报!数据库事务隔离级别

来源:博客园


【资料图】

标准隔离级别

读未提交、读已提交、可重复读、串行化

串行化

对事务中所有读写的数据加上读锁、写锁、范围锁。所以冲突的事务必须同步执行。
//console1start transaction ;select * from transaction_test where `key`=1;update transaction_test set name="newTest" where `key`=1;//console2start transaction ;select * from transaction_test where `key`=1;(由于事务1没有释放写锁,所以这里的查询会阻塞如果等待时间过长,会报如下的错误;如果事务1只是查询,那么事务2也可以查询)[40001][1205] Lock wait timeout exceeded; try restarting transaction//console1commit ;(提交完之后如果事务2没有等待超时,那么会立即执行)//console2;commit ;

可重复读

核心是只对事务中所有读写的数据加上读锁、写锁,不加范围锁。相比于读已提交,由于对整个事务都加上了读锁,避免其他事务可以进行更新,进而保证同一个事务多次读到的数据都是没有被修改过的数据。
----避免可重复读----name初始值为init//console1start transaction ;select * from transaction_test where `key`=1;(查询结果是init)//console2start transaction ;update transaction_test set name="test" where `key`=1;(理论上,由于事务1已经获取了读锁,事务2这里添加写锁应该是添加不上的,应该是阻塞中才对;但是,实操发现,执行成功了,且在事务2中通过下面这个语句查询是test,这应该也是mvcc导致的select * from transaction_test where `key`=1;//console1select * from transaction_test where `key`=1;console1的第2次查询,查询结果和第一次一样,还是init另外,事务2都获得写锁了,怎么能允许你事务1再去获得读锁commit ;//console2commit ;
相比于串行化,由于没有加范围锁,会引发一种叫幻读的情况所谓幻读是指在同一个事务中,第一次查询id<10的假定有1条,第二次查询可能会有2条,原因是在两次查询的中间,存在别的事务插入或者删除了数据,由于事务A只加了读锁或者写锁,只能防止其他事务对已经加锁的这几条数据进行修改,但避免不了插入和删除,所以才会出现这个问题。
----幻读----初始是1,name//console1start transaction ;select * from transaction_test where `key`<10;//console2start transaction ;insert into transaction_test ( `key`,`name`) value (3,"newddd");select * from transaction_test where `key`<10;commit;//console1select * from transaction_test where `key`<10;理论上来讲,这个地方应该会查到三条,但是实操发现,在事务2添加并提交之后,事务1查到了依然是原来的样子commit ;select * from transaction_test where `key`<10;(提交之后再次查询就有新结果了)

读已提交

核心是对事务中需要更新的操作行加写锁,直到事务结束,但对查询的操作行加读锁,但在查询完之后立即释放,即不是在整个事务范围锁定。读已提交通过对查询操作加锁来避免读未提交,在事务B修改数据时因为其在事务结束之前一直持有写锁,事务A无法对数据加读锁,只能等待事务B提交事务才可以读取,这也是读已提交的名称的由来。虽然解决了读未提交的问题,但是由于只在查询的时候短暂加了写锁,引发了另一个不可重复读的问题;所谓不可重复读是指在同一个事务中,对于同样一条数据的两次查询结果不一样,那么这个和幻读有什么区别呢?幻读整个事务中都存在读锁或者写锁,其他事务无法修改,只能增删;但是不可重复读,则是指当前已经查到的结果被更新了。原因是假如同一个事务两次查询中间,别的事务进行了修改,由于事务A没有加整个事务范围的读锁,所以事务B是可以成功获取写锁的,进而修改数据,最终导致了不可重复读。
---避免读未提交----name初始值是init//console1start transaction ;select * from transaction_test where `key`=1;update transaction_test set name="test" where `key`=1;//console2start transaction ;select * from transaction_test where `key`=1;(由于读不到未提交的,所以肯定获取不到修改后的test值,理论上只能等待事务1结束)这个地方由于事务1已经添加了写锁,原则上事务2根本查询不了,应该阻塞,就像串行化那里一样但是实际结果却是可以查到以前的值,即init;所以这里应该是mvcc的作用在读已提交的级别下,mvcc机制总是取最新的版本即可,即最近被 Commit 的那个版本的数据记录。这样保证了读到的都是已提交的事务,且保留了幻读问题最新版本的快照读,不是当前读//console1commit;//提交之后,事务2再次查询,发现已经可以获取到改动后的值了,即test---不可重复读----name初始值是init//console1start transaction ;select * from transaction_test where `key`=1;(第一次查询是init)//console2start transaction ;update transaction_test set name="test" where `key`=1;(在事务2中更新并提交)commit ;//console1select * from transaction_test where `key`=1;(第二次查询是test)commit ;

读未提交

核心是对事务中需要更新的操作行加写锁,直到事务结束,但对查询的操作行不加锁。引发的问题是脏读,其实就是读到了其他事务还没有提交的数据;那么为什么事务A可以读到事务B还没有提交的数据?分为两步理解:1.为什么存在可以读的新的数据?核心原因应该是write-ahead logging的设计。即上一章提到的允许在事务提交之前提前写入数据,理论上肯定是写到了内存中,并且记录到undolog里面,虽然还不太情况事务的提交真正干了什么操作,但目前来,在内存是可以读到已经修改好的数据。2.为什么可以读到已经加了写锁的数据原因是读未提交读取数据是不加读锁的,而写锁只能防止其他事物不能加读锁和写锁,而不能防止没有锁 也可以看一下这篇博客的解释
show variables like "transaction%";set global transaction isolation level read uncommitted ;//设置完之后要重新登录CREATE TABLE `transaction_test` (`key` int(11),`name` varchar(10) DEFAULT NULL) ENGINE=InnoDB;---read uncommitted---读未提交//console1start transaction ;insert into transaction_test value (1,"test");//console2start transaction ;select * from transaction_test where `key`=1; (查询结果为1,test)//console1commit ;//console2commit;两个事务都是写事务,晚开启的事务更新会阻塞//console1start transaction ;update transaction_test set name="newTest" where `key`=1;//console2start transaction ;update transaction_test set name="Test" where `key`=1;(会阻塞,一直在执行中)//console1commit ;(在事务1提交成功后,事务2的更新立马就成功了)//console2commit;
参考资料:12 | 本地事务如何实现隔离性?-极客时间03 | 事务隔离:为什么你改了我还看不见?-极客时间读未提交-为什么事务没提交就可以读到别人修改的数据 - 秦一居 - 博客园

关键词: