MYSQL INNODB事务的隔离级别有四级, 默认是 可重复读(repeatable read)。
1. 未提交读(read uncommitted). 另一个事务修改了数据, 但沿未提交, 而本事务中的SELECT会读到这些未提交的数据(脏读).
2. 提交读(READ COMMITTED). 本事务读取到的是最新的数据(其他事务提交后的). 问题是, 在同一个事务里, 前后两次相同的SELECT会读到不同的结果(不重复读)。
3. 可重复读(REPEATABLE READ)。 在同一个事务里, SELECT的结果是事务开始时时间点的状态, 因此,同样的SELECT操作读到的结果会是一致的, 但是会有幻读现象。
4. 串行化(SERIALIZABLE)。 读操作会隐式获取共享锁,可以保证不同事务间的互斥。
四个级别逐渐增强, 每个级别解决一个问题:
脏读: 最容易理解。 另一个事务修改了数据,但沿未提交,而本事务中的SELECT会读到这些未被提交的数据。
不重复读:解决了脏读后, 会遇到, 同一个事务执行过程中,另外一个事务提交了新数据, 因此本事务先后两次读到的数据结果也不一致。
幻读:解决了不重复读,保证了同一个事务里, 查询的结果都是事务开始时的状态(一致性)。 但是,如果另一个事务同时提交了新数据, 本事务再更新时, 就会发现了这些新数据,貌似之前读到的数据是影子一样的幻觉。
关于幻读的小测试:
测试一:
CREATE TABLE `t_bitfly` (
`id` bigint(20) NOT NULL default '0',
`value` varchar(32) default NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
select @@global.tx_isolation, @@tx_isolation;
mysql> select @@global.tx_isolation, @@tx_isolation;
+-----------------------+-----------------+
| @@global.tx_isolation | @@tx_isolation |
+-----------------------+-----------------+
| REPEATABLE-READ | REPEATABLE-READ |
+-----------------------+-----------------+
1 row in set (0.00 sec)
会话一:
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT * FROM t_bitfly;
Empty set (0.00 sec)
会话二:
mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO t_bitfly values(1,'a');
Query OK, 1 row affected (0.01 sec)
在这里不提交,去会话一上去查询。
mysql> SELECT * FROM t_bitfly;
Empty set (0.07 sec)
还是空的, 如果这时我去会话二上把事务提交了呢
mysql> commit;
Query OK, 0 rows affected (0.01 sec)
再去会话一上去查看。
mysql> SELECT * FROM t_bitfly;
Empty set (0.00 sec)
还是没有, 这就把可重复读体现出来了, 如果是CR的隔离级别,在这里就可以看到刚提交的数据了。 下面我们再去会话一上执行一个插入的SQL。
查询出来是空的, 但为何插入就报错了呢, 这就是幻读。