MySQL事务隔离级别-案例驱动
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL事务隔离级别-案例驱动相关的知识,希望对你有一定的参考价值。
SQL92将事务隔离级别分为了4种,读未提交,读已提交,可重复读,可串行化。不同的隔离级别,对于数据的可见性不同,就会导致不同的正确性。事务的核心在于控制多个客户端在读写共享数据时候的并发问题。这篇文章通过实例研究隔离级别,我们将从读未提交开始,一步一步分析。
step1 创建表和基础数据
CREATE TABLE `traning`.`account` (
`id` INT NOT NULL AUTO_INCREMENT COMMENT ‘主键id‘,
`uid` INT NOT NULL COMMENT ‘用户uid‘,
`amount` INT NOT NULL COMMENT ‘金额‘,
PRIMARY KEY (`id`));
INSERT INTO `traning`.`account` (`uid`, `amount`) VALUES (‘1‘, ‘1000‘);
INSERT INTO `traning`.`account` (`uid`, `amount`) VALUES (‘2‘, ‘2000‘);
step2 修改隔离级别
场景1 读未提交导致脏读
Client1:SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
Client2:SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
关闭会话重新进入mysql客户端。
Client2:show variables like ‘transaction_isolation‘;
Client1: start TRANSACTION;
Client2: start TRANSACTION;
Client2: select amount from account where uid=1; (1000)
update account set amount=amount-500 where uid=1;
Client1: select amount from account where uid=1; (读到别人没提交事务:500)
-- update account set amount=(amount+100) where uid=1;(数据被锁,不能更新)
Client2: rollback;(Client1再次读到的是1000,但是没有读)
-- Client1: select amount from account where uid=1; (500)
Client1: update account set amount=(500+100) where uid=1; (读到旧数据去进行更新操作,导致账户丢失500)
场景2 读已提交解决脏读问题
Client1:SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
Client2:SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
关闭会话重启。
Client1:start TRANSACTION;
Client2:start TRANSACTION;
Client2: select amount from account where uid=1; (1000)
update account set amount=amount-500 where uid=1; (500)
Client1:select amount from account where uid=1 (读取不到别人没提交的事务:1000);
update account set amount=1000-500 where uid=1;(数据被锁不能更新)
Client2: rollback;
Client1: update account set amount=(1000+100) where uid=1;
Client1: commit;
场景3 读已提交导致不可重复读问题
Client1:SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
Client2:SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
关闭会话重启。
Client1:start TRANSACTION;
Client2:start TRANSACTION;
Client1:select amount from account where uid=1; (1000);
Client2:update account set amount=amount-100 where uid=1;
Client2: commit;
Client1: select amount from account where uid=1(amount=900,发现自己钱少了100);
场景4 可重复读解决不可重复读问题
Client1:SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
Client2:SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
Client1:start TRANSACTION;
Client2:start TRANSACTION;
Client2: select amount from account where uid=1;(1000)
Client1:select amount from account where uid=1; (1000);
Client2:commit;
Client1:select amount from account where uid=1;(amount=1000);
场景5 可重复读导致幻读问题
Client1:SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
Client2:SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
Client1:start TRANSACTION;
Client2:start TRANSACTION;
Client2: select amount from account ; (2行数据)
Client1:select amount from account ; (2行数据);
Client2:INSERT INTO traning
.account
(uid
, amount
) VALUES (‘3‘, ‘3000‘);
Client2:commit;
Client1:select amount from account ;(3条数据);
Clinet1 :update account set amount=1 where id=3(Client读不到但是可以更新);
Clinet1 :select amount from account ;(4条数据-phantom read here!!!!影响了client1的判断和数据);
Clinet1 :commit;
场景6 可序列化解决幻读问题
Client1:SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;
Client2:SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;
Client1:start TRANSACTION;
Client2:start TRANSACTION;
Client2: select amount from account ; (2行数据)
Client1:select amount from account ; (2行数据);
Client2:INSERT INTO traning
.account
(uid
, amount
) VALUES (‘3‘, ‘3000‘); (插入不进去,数据被锁)
Lock wait timeout exceeded; try restarting transaction
以下是整理的图:
当然还有第一类丢失更新(脏写)和第二类丢失更新,第一类丢失更新永远不会发生,第二类丢失更新会在RU和RC情况下发生。
这是完整的图
包含了更多的隔离级别和可能发生数据异常情况:
以上是关于MySQL事务隔离级别-案例驱动的主要内容,如果未能解决你的问题,请参考以下文章