InnoDB|带你认识什么是MySQL的内存架构和索引说明( 五 )


如果事务隔离级别是默认的REPEATABLE READ , 在相同事务里的所有一致读都会读取这个事务第一次读的时候建立的快照 。
可以通过提交当前事务并在之后发出新的查询来得到新的快照
对于READ COMMITTED隔离级别 , 每个事务里的一致读都会设置并读取它自己的新快照
一致读是InnoDB在READ COMMITTED和REPEATABLE READ隔离级别下处理SELECT语句的默认模式 。
一致读不会在它访问的表上设置任何锁 , 因此其他session可以同时自由修改这些表
数据库的快照状态应用于事务里的SELECT语句 , 不一定应用于DML语句 。 如果插入或修改某些行然后提交该事务 , 则从另一个并发的可重复读事务会影响到这些刚提交的行 , 尽管这session查询不到它们 。
如果某个事务确实更新或删除了不同事务提交的 , 则这些更改对当前事务是可见的 。
例子(其他session提交记录 , 原session查询不到 , 可以删除):
#sessionA
select count(name) from child where name = 'hello100';
### 返回0行

#sessionB插入两行并提交
insert into child(idname) values(100 'hello100');
insert into child(idname) values(101 'hello100');

#sessionA统计 , 返回0行
select count(name) from child where name = 'hello100';

#sessionA删除 , 尽管查询不到 , 但可以删除2行
delete from child where name = 'hello100';
### Query OK 2 rows affected (0.00 sec)

可以通过提交事务 , 然后执行SELECT或START TRANSACTION WITH CONSISTENT SNAPSHOT来更新时间点 。 这就是多版本并发控制
下面的例子 , sessionA只在B提交了插入 , 且A已提交后 , 才能看到B插入的记录
Session A              Session B

          SET autocommit=0;      SET autocommit=0;
time
|          SELECT * FROM t;
|          empty set
|                                 INSERT INTO t VALUES (1 2);
|
v          SELECT * FROM t;
          empty set
                                 COMMIT;

          SELECT * FROM t;
          empty set

          COMMIT;

          SELECT * FROM t;
          ---------------------
          |    1    |    2    |
          ---------------------

如果想看到数据库的最新状态 , 那么要使用READ COMMITTED隔离级别 , 或者锁定读:


SELECT * FROM t FOR SHARE;
读已提交隔离级别 , 一个事务里的每个一致读都会设置并读取它自己的新快照 。 对于FOR SHARE , SELECT一直阻塞直到包含最新行的事务结束
InnoDB中不同SQL语句设置的锁锁定读 , UPDATE或者DELETE通常会在SQL语句处理的过程中对每个被扫描到的索引记录设置记录锁 。 它不管语句中的WHERE条件是否会排除掉行 。 InnoDB不会记得准确的WHERE条件 , 只知道哪部分索引范围被扫描 。
如果搜索中使用了二级索引 , 并且索引记录锁被设置为排他的 , InnoDB也会检索出相应的聚簇索引记录并对它们设置锁 。
如果没有索引适合执行语句 , 那么MySQL必须扫描整个表来处理语句 , 表的每一行都会被锁定 , 从而也会阻塞其他用户插入到这个表中 。 所以创建好的索引 , 让查询不会扫描超过需要的行是很重要的 。