事务的四大特性:
事务类型
SQL 标准定义了四个隔离级别:
表格
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| READ-UNCOMMITTED | √ | √ | √ |
| READ-COMMITTED | × | √ | √ |
| REPEATABLE-READ | × | × | √ |
| SERIALIZABLE | × | × | × |
与 SQL 标准不同的地方在于InnoDB 存储引擎在 REPEATABLE-READ(可重读) 事务隔离级别下,允许应用使用 Next-Key Lock 锁算法来避免幻读的产生。这与其他数据库系统(如 SQL Server)是不同的。所以说虽然 InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读),但是可以通过应用加锁读(例如 select * from table for update 语句)来保证不会产生幻读,而这个加锁度使用到的机制就是 Next-Key Lock 锁算法。从而达到了 SQL 标准的 SERIALIZABLE(可串行化) 隔离级别。
因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是READ-COMMITTED(读取提交内容):,但是InnoDB 存储引擎默认使用 **REPEATABLE-READ(可重读)**并不会有任何性能损失
SQL脚本
DROP TABLE IF EXISTS `t_test`;
CREATE TABLE `t_test` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`a` varchar(10) CHARACTER SET utf8 NOT NULL,
`b` varchar(10) CHARACTER SET utf8 NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
BEGIN;
INSERT INTO `t_test` VALUES (1, 'a1', 'b1');
INSERT INTO `t_test` VALUES (2, 'a1', 'b2');
INSERT INTO `t_test` VALUES (3, 'a2', 'b3');
INSERT INTO `t_test` VALUES (4, 'a3', 'b4');
INSERT INTO `t_test` VALUES (5, 'a1', 'b3');
COMMIT;
RC 的本质就是事务中每一条 SELECT 语句均可以看到其他已提交事务对数据的修改,那么只要该事物已经提交其结果就是可见的,与这两个事务开始的先后顺序无关,不完全适用于 MVCC 读。事务A第二次查询的结果是什么?

实验结果:事务A读取到事务B已经提交的数据。RC隔离级别下,会出现不可重复读。RC的本质就是事务中每一条 SELECT语句均可以看到其他已提交事务对数据的修改

事务A第二次查询获取的结果是什么?

实验结果:事务A读取到事务B已经提交的插入的数据。在RC隔离级别下会出现幻读,幻读重点在insert,不可重复读重点在Update和Delete

RR 的本质是从第一个 SELECT 语句生成 ReadView 开始,任何已经提交过的事务的修改均可见事务A第二次查询获取的结果是什么?(B事务未提交)。事务A第三次查询获取的结果是什么?(B事务已经提交)

实验结果:无论事务B是否提交,都不影响事务A的查询结果。

思考:如果想在事务A中获取最新的结果,该如何操作呢?
验证ReadView是否是事务开启时生成的?

实验结果:从第一个 SELECT 语句生成 ReadView 开始,任何已经提交过的事务的修改均可见。事务A select和事务B提交的顺序不同就会导致不同的数据

事务A是否能读取到事务B插入的数据呢?

实验结果:事务A读取不到事务B插入的数据,没有幻读发生

在快照读情况下,mysql通过mvcc(多版本并发控制)来避免幻读
思考:
如果事务A最后的查询加上for update是否可以查询出最新数据?(可以,查询出三条数据,for update强制将快照读变为当前读,即产生了幻读)
如果事务A执行完for update后再次使用普通查询是否可以查询到最新数据?(不可以,再次的普通查询只能查询出2条数据)
验证实验三的思考问题,RR隔离级别下当前读产生的幻读问题?

实验结果:如果是当前读,会出现幻读问题

在RR级别中,数据是可重复读,对于这种读取历史数据的方式,叫做快照读(snapshot read),读取数据库当前版本数据的方式,叫当前读 (current read)。
快照读就是普通的select
当前读:特殊的读操作,插入/更新/删除操作,属于当前读,处理的都是当前的数据,需要加锁
在快照读情况下,mysql通过mvcc(多版本并发控制)来避免幻读。在当前读读情况下,mysql通过next-key来避免幻读。
select * from t where a=1;属于快照读
select * from t where a=1 lock in share mode;属于当前读
在RR级别下,如果是快照读,那么其实是可以避免幻读的。如果是快照读+当前读,那么其实是不能避免幻读的