1.事务的并发问题
-
脏读: 事务A读取了事务B更新的数据(不管事务B有没有提交数据),然后B回滚操作,那么A读取到的数据是脏数据。
-
不可重复读: 不可重复读指的是在同一事务内,不同的时刻读到的同一批数据可能是不一样的,可能会受到其他事务的影响,比如其他事务改了这批数据并提交了。通常针对数据更新 (UPDATE) 操作。
-
幻读: 幻读是针对数据插入 (INSERT) 操作来说的。假设事务A对某些行的内容作了更改,但是还未提交,此时事务B插入了与事务A更改前的记录相同的记录行,并且在事务A提交之前先提交了,而这时,在事务A中查询,会发现好像刚刚的更改对于某些数据未起作用,但其实是事务B刚插入进来的,让用户感觉很魔幻,感觉出现了幻觉,这就叫幻读。
2.事务的隔离级别
| 事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| 读未提交(read-uncommitted) | 是 | 是 | 是 |
| 读已提交(read-committed) | 否 | 是 | 是 |
| 重复读(repeatable-read) | 否 | 否 | 是 |
| 串行化(serializable) | 否 | 否 | 否 |
3.案列
首先查看mysql默认的隔离级别是什么,默认隔离级别是 repeatable-read,两种方式都可查看
SELECT @@tx_isolation;
show variables like 'tx_isolation';
设置mysql事务隔离级别
set [作用域] transaction isolation level [事务隔离级别],
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}。
创建测试数据表
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(30) DEFAULT NULL,
`age` tinyint(4) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
INSERT INTO user VALUES(NULL,'张飞',38);
INSERT INTO user VALUES(NULL,'刘备',46);
3.1 读未提交(read-uncommitted)
设置事务隔离级别为read-uncommitted
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
隔离级别为
read-uncommitted,其他事务未提交时的INSERT、UPDATE、DELETE操作都可以被看见
3.2 读已提交(read-committed)
设置事务隔离级别为read-committed
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
隔离级别为
read-committed,其他事务未提交时的INSERT、UPDATE、DELETE操作都不会被看见,当commit后,才可以被其他事务看见
3.3 重复读(repeatable-read)
设置事务隔离级别为repeatable-read
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;
隔离级别为
repeatable-read,不管其他事务有没有被提交 INSERT、UPDATE、DELETE操作都不会被看见。注意:如果当前事务修改了某条记录,则该条记录被其他已提交的事务修改的结果可以被看见,就出现了幻读。如果当前事务查询的时候使用 for update 语句,则是可以看到其他已经提交事务做的修改操作
事务A,事务B同时对同一张表的同一条记录做修改,后执行的事务可能会执行失败,此时可能会发生行锁的lock情况,执行不成功。后执行的事务必须等待先执行的事务先commit或者rollback,才能对这同一条要修改的记录作出修改;
3.3 串行化(serializable)
设置事务隔离级别为serializable
SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;
一个个事务排成序列的形式。事务一个挨一个执行,等待前一个事务执行完,后面的事务才可以顺序执行