当前位置: 首页 > 科技观察

MySQL下四种事务隔离级别实例分析

时间:2023-03-13 23:57:10 科技观察

数据库事务有四种隔离级别:读未提交(ReadUncommitted):允许脏读,即可以读取其他会话中未提交事务修改的数据。ReadCommitted(已提交读):只能读取已提交的数据,Oracle等大多数数据库默认都有这个级别。可重复读(RepeatedRead):可重复读。同一个事务中的查询在事务开始时是一致的,InnoDB的默认级别。在SQL标准中,这种隔离级别消除了不可重复读,但幻读仍然存在。可序列化:完全序列化的读取。每次读都需要获取表级共享锁,读写都会互相阻塞。第一次接触事务隔离概念的朋友可能会对上面的教科书定义一头雾水。下面通过具体的例子来解释这四种隔离级别。首先我们创建一个用户表:CREATETABLEuser(`id`int(11)NOTNULLAUTO_INCREMENT,`name`varchar(255)NOTNULL,PRIMARYKEY(`id`),UNIQUE`uniq_name`USINGBTREE(name))ENGINE=`InnoDB`AUTO_INCREMENT=10DEFAULTCHARACTERSETutf8COLLATEutf8_general_ci;Readuncommitted隔离级别我们先设置事务隔离级别为readcommitted:mysql>setsessiontransactionisolationlevelreaduncommitted;QueryOK,0rowsaffected(0.00sec)mysql>select@@session.tx_isolation;+--------------------------+|@@session.tx_isolation|+------------------------+|阅读-未提交|+----------------------+1rowinset(0.00sec)下面我们开了两个终端来模拟交易一和交易二,p.s:操作一和操作二表示它们是按时间顺序执行的。Transaction1mysql>starttransaction;#operation1QueryOK,0rowsaffected(0.00sec)mysql>insertintouser(name)values('ziwenxie');#operation3QueryOK,1rowaffected(0.05sec)transaction2mysql>starttransaction;#operation2QueryOK,0rowsaffected(0.00sec)mysql>select*fromuser;#操作4+----+--------+|id|name|+----+--------+|10|ziwenxie|+----+------------+1rowinset(0.00sec)从上面的执行结果可以很明显的看出,我们可能在readuncommited级别以下的transaction1中读取到了数据事务2中没有提交,这是脏读。Readcommittedisolationlevel上面的脏读问题可以通过将隔离级别设置为committed来解决。mysql>setsessiontransactionisolationlevelreadcommitted;事务一mysql>starttransaction;#操作一QueryOK,0rowsaffected(0.00sec)mysql>select*fromuser;#操作三+----+--------+|id|name|+----+----------+|10|ziwenxie|+----+----------+1rowinset(0.00sec)mysql>select*fromuser;#操作5,操作4的修改做不影响事务1+----+----------+|id|name|+----+-----------+|10|ziwenxie|+----+------------+1rowinset(0.00sec)mysql>select*fromuser;#operationseven+----+------+|id|name|+---+------+|10|lisi|+----+-----+1rowinset(0.00sec)mysql>commit;#操作八QueryOK,0rowsaffected(0.00sec)事务二mysql>开始交易;#操作二QueryOK,0rowsaffected(0.00sec)mysql>updateusersetname='lisi'whereid=10;#Operation4QueryOK,1rowaffected(0.06sec)Rowsmatched:1Changed:1Warnings:0mysql>commit;#Operation6QueryOK,0rowsaffected(0.08sec)虽然解决了脏读问题,但是请注意在事务1的operation7中,事务2的操作6提交后,事务1会导致同一个事务有两个事务,这就是不可重复读的问题,可以使用第三个事务隔离级别可重复读来解决。可重复读隔离级别MySQL的Innodb存储引擎默认的事务隔离级别是可重复读隔离级别,所以我们不需要做多余的设置。事务1mysql>starttansactoin;#操作1mysql>select*fromuser;#操作5+----+--------+|id|name|+----+----------+|10|ziwenxie|+----+----------+1rowinset(0.00sec)mysql>commit;#操作六QueryOK,0rowsaffected(0.00sec)mysql>select*fromuser;#操作7+----+-----+|id|name|+----+------+|10|lisi|+----+------+1rowinset(0.00sec)事务2mysql>starttansactoin;#Operation2mysql>updateusersetname='lisi'whereid=10;#Operation3QueryOK,1rowaffected(0.00sec)Rowsmatched:1Changed:1Warnings:0mysql>commit;#Operation4在事务1的operation5中,我们没有读取更新操作3中的事务2,commit之后才能读取到更新后的数据。Innodb能解决幻读吗?事实上,幻读可能发生在RR级。InnoDB引擎官方宣称使用MVCC多版本并发控制来解决这个问题。我们来验证一下Innodb真的解决了幻读吗?为了方便显示,我修改了上面的用户表:mysql>altertableuseraddsalaryint(11);QueryOK,0rowsaffected(0.51sec)Records:0Duplicates:0Warnings:0mysql>deletefromuser;QueryOK,1rowsaffected(0.07sec)mysql>insertintouser(name,salary)value('ziwenxie',88888888);QueryOK,1rowaffected(0.07sec)mysql>select*fromuser;+----+----------+---------+|id|name|salary|+----+--------+--------+|10|ziwenxie|88888888|+----+----------+------------+1rowinset(0.00sec)事务一mysql>starttransaction;#操作一QueryOK,0rowsaffected(0.00sec)mysql>updateusersetsalary='4444';#操作六,居然影响了两行,不是说幻读解决了吗?QueryOK,2rowsaffected(0.00sec)Rowsmatched:2Changed:2Warnings:0mysql>select*fromuser;#操作7,Innodb没有彻底解决幻读+----+---------+--------+|id|name|salary|+----+---------+--------+|10|紫文邪|4444||11|张三|4444|+----+----------+------+2rowsinset(0.00sec)mysql>commit;#Operation8QueryOK,0rowsaffected(0.04sec)transaction2mysql>starttransaction;#Operation2QueryOK,0rowsaffected(0.00sec)mysql>insertintouser(name,salary)value('zhangsan','666666');#Operation4QueryOK,1rowaffected(0.00sec)mysql>commit;#Operation5QueryOK,0rowsaffected(0.04sec)从上面的例子可以看出,Innodb并没有像官方说的那样解决幻读,只是在上面的场景中不是很常见。不要太担心序列化隔离级别。所有交易都是串行执行的。***隔离级别,不会出现幻读,性能会差,实际开发中很少用到。