背景
上一篇说到MySQL 5.5加主键导致阻塞源表的读的问题。
有同学提到从调用函数看,在默认的old_alter_table=off的情况下,加主键过程没有看到copy to tmp table。
这里我们再细说一下。
说说fast index creation
在MySQL 5.1以后InnoDB引入了fast index creation。在有这个feature之前,MySQL认为所有的加减索引操作都必须创建临时表、拷贝数据、删除源表。
有了这个神奇的功能之后,InnoDB跟MySQL说,稍等,要是加索引,不要你拷贝数据了,“放着我来”。怎么作的呢?
1) 扫描源表,将每行中用于索引的字段取出来
2) 排序构造新索引
3) 插入源表中
这个就是我们说的inplace-DDL了,大家看到,没有拷贝新表的操作。性能因此也提高了。这个过程叫做row_merge_build_indexes。
再说说加主键
对于InnoDB来说,数据也是以索引组织的,因此加主键,在概念上说,也是加索引。于是在old_alter_table=off的情况下,MySQL照着上面的约定,调用row_merge_build_indexes。
由于在5.5,MySQL认为InnoDB还是inplace操作,因此锁策略就如同上一篇处理。
那么InnoDB是不是在源表加主键呢?
这显然不可能。
Fast-index-creation的精髓是只需要读主键数据,生成新索引插入到源表中。若加的是主键,说明原来没有定义主键(多么正确的废话),这时候InnoDB是使用系统分配的row_id作为主键。当我们增加主键以后,不可能“构造主键,插入源表”----那样就俩主键了。。。
源码实现
实际上row_merge_build_indexes有两个参数(old_table, new_table)。 若新增的索引中不包含主键,则new_table == old_tablie,否则不等。
对于不等的情况,这个过程就变成
1) 扫描old_table主键上的数据
2) 按照新增的索引插入到new_table中
3) 而由于“主键”索引的叶子节点,实际上是包含所有数据,所以这个过程,其实就是copy table。
对于new_table!=old_table的情况,在拷贝数据期间只需要加共享锁即可,只是5.5下没有意识到这个问题而已。