另外一个比较有意思的小优化是,对于AUTOCOMMIT的只读查询,关闭视图时,并不是立刻从视图链表中移除,而是设置一个简单的close标记;该session下次需要打开该read view时,如果这期间没有任何读写事务,就可以直接重用上次的read view,清楚close标记,这样打开、关闭视图都无需获取trx_sys->mutex。
问题四:隐式锁转换为显式锁的开销
Innodb对于类似INSERT操作,采用的是隐式锁的方式,隐式锁不是锁,只是一种称呼而已,只有在需要的时候,才会转换为显式锁。例如如下:
Session 1: BEING; INSERT INTO t1(pk, val) VALUES (1,2); //不创建锁对象
Session 2: UPDATE t1 SET val=val+1 WHERE pk=1; //创建两个锁对象,一个是为session1创建一个记录锁对象,另外一个是给自己创建一个等待类型的记录锁对象,然后session2加入锁等待队列。
在Session 2中为Session1创建锁对象的过程即是所谓的隐式锁向显式锁转换。 当session2扫描到session 1插入的记录时,发现session 1的事务依然活跃,就会进入转换逻辑。
在5.6版本中,其转换过程如下:
1.持有lock_sys->mutex
2. 持有trx_sys->mutex;
根据事务ID,扫描读写事务链表,找到对应的事务对象;
释放trx_sys->mutex;
3.创建显式锁对象
4.释放lock_sys->mutex
可以看到,在该操作的过程中,全程持有lock_sys->mutex,持有锁的原因是防止事务提交掉。当读写事务链表非常长时(例如高并发写入时),这种开销将是不可接受的。
在5.7版本中,上述逻辑则优化成:
1. 持有trx_sys->mutex
根据事务ID找到对应的事务对象(直接查找trx_sys->rw_trx_set,其保存了trx_id和事务对象的映射关系,因此无需扫描读写事务链表)
增加事务对象引用计数(++trx->n_ref)
释放trx_sys->mutex
2. 持有lock_sys->mutex;
创建显式锁对象;
释放lock_sys->mutex;
3.递减事务对象引用计数
在事务commit,释放记录锁前,会先判断引用记录数是否为0,如果不为0,表示正有其他事务为其转换显式锁,这时候需要等待,直到计数为0,才能进入释放事务记录锁阶段。
总的来说,该优化减少了隐式锁转换时持有LOCK_sys->mutex的时间,从而提升性能。
除了上述提到的几点事务优化外,在5.7版本中还对事务系统部分的代码做了重构,完全用C++重写;引入了一个POOL结构,事务对象和锁对象都可以缓存复用。大家可以阅读几个相关的worklog,以更好的理解上述优化:
混用引擎的问题
在MySQL中,存储引擎是通过插件方式使用的,事务是由存储引擎自己实现,MySQL服务层是不管理事务的,所以在同一个事务中混用不同的存储引擎是不可靠的。 如果混用事务引擎和非事务引擎的话,事务如果正常提交的话,5.5不会有问题,但是5.6版本如果开了 GTID 的话就会报错,因为GTID模式下不允许事务中同时更新事务引擎和非事务引擎(Restrictions on Replication with GTIDs); 如果事务回滚的话,不管是 5.5 还是 5.6 都会有问题的,因为对非事务引擎表的操作是无法回滚的,这样就会造成数据不一致,因为只有部分操作成功,并且结果不可预知,事务的原子性和一致性被破坏。
我们下面举例子来说明,t1_i 是事务引擎表(InnoDB),ti_m 是非事务引擎(MyISAM)。
在事务执行前2张表里的数据如下:
mysql> SELECT * FROM t1_i; +------+------+ | id | name | +------+------+ | 1 | test | | 2 | test | +------+------+ 2 rows in set (0.00 sec) mysql> SELECT * FROM t1_m; +------+------+ | id | name | +------+------+ | 1 | test | | 2 | test | +------+------+ 2 rows in set (0.00 sec)事务如下,对2张表分别插一条数据,然后 rollback 模拟出错回滚,
BEGIN; INSERT INTO t1_m VALUES (3, "test"); INSERT INTO t1_i VALUES (3, "test"); ROLLBACK;执行回滚后,我们会看到 MySQL 返回信息中有 warnings;
Query OK, 0 rows affected, 1 warning