视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
MySQL5.6Internals--隐藏的索引列_MySQL
2020-11-09 18:30:08 责编:小采
文档


bitsCN.com

MySQL5.6 Internals-隐藏的索引列

Louis Hust

0 前言

今天本来想跟踪MySQL5.6中的新特性Index Merge,结果在跟踪的过程中,发现了一个问题,即InnoDB的二级索引中 可能会包含主索引,当然这里的包含并不是说二级索引的row里面会有pk的记录,这一点是一直存在的,这里的包含 是指,二级索引也会包含主索引进行排序。

1 现场重现

1.1 初始化数据

mysql> show create table index_merge/G*************************** 1. row *************************** Table: index_mergeCreate Table: CREATE TABLE `index_merge` ( `c1` int(11) NOT NULL AUTO_INCREMENT, `c2` int(11) DEFAULT NULL, `c3` varchar(100) DEFAULT NULL, PRIMARY KEY (`c1`), KEY `c2` (`c2`)) ENGINE=InnoDB AUTO_INCREMENT=10002 DEFAULT CHARSET=latin11 row in set (0.00 sec)mysql> show create procedure fill_index_merge/G*************************** 1. row *************************** Procedure: fill_index_merge sql_mode: NO_ENGINE_SUBSTITUTION Create Procedure: CREATE DEFINER=`root`@`127.0.0.1` PROCEDURE `fill_index_merge`(c1_cnt int, c2_cnt int)begindeclare i,j int default 1;repeat repeat insert into index_merge(c2,c3) values(j, repeat('a', )); set j = j+1; until j > c2_cnt end repeat;set i = i + 1;set j = 1;until i > c1_cntend repeat;endcharacter_set_client: utf8collation_connection: utf8_general_ci Database Collation: latin1_swedish_ci1 row in set (0.00 sec)mysql> call fill_index_merge(100, 100);

1.2 查看计划

mysql> explain select * from index_merge where c1 < 100 and c2 = 50/G*************************** 1. row *************************** id: 1 select_type: SIMPLE table: index_merge type: rangepossible_keys: PRIMARY,c2 key: c2 key_len: 9 ref: NULL rows: 1 Extra: Using index condition1 row in set (0.00 sec)

可以看到,计划中竟然是range的查询,我的第一直觉应该是ref const的查询,但是确实是range,使用c2的range, 但是c2的key_len竟然是9,我去,各种疑惑啊,于是跟踪计划生成,发现key有两个(PRIMARY, c2),但是c2的index上的列确有两列,(c2,c1)。 瞬间凌乱了,竟然隐式的修改我的index,还把用户蒙在鼓里。

2 原因分析

由上面的分析,可以知道c2的index在生成时,其实被MySQL隐式修改为(c2,c1)的index了,那我们就看下代码吧。

一开始我以为这些修改是在创建表的时候就隐式的修改了,我又错了,木有,看遍了mysql_create_frm()都没找到哪里修改了, frm文件还是那个frm文件。

OK,不是创建的时候修改,那只能是加载frm时候修改了,也就是内存的修改。看看数据字典的加载函数open_binary_frm(),bingo, 5.6相比5.5确实做了修改,重点代码如下:

open_binary_frm(){ ... use_extended_sk= (legacy_db_type == DB_TYPE_INNODB); ... for (i=0 ; i < keys ; i++, keyinfo++) { ... for (j=keyinfo->user_defined_key_parts ; j-- ; key_part++) { ... /* Add PK parts if engine supports PK extension for secondary keys. Atm it works for Innodb only. Here we add unique first key parts to the end of secondary key parts array and increase actual number of key parts. Note that primary key is always first if exists. Later if there is no PK in the table then number of actual keys parts is set to user defined key parts. */ keyinfo->actual_key_parts= keyinfo->user_defined_key_parts; keyinfo->actual_flags= keyinfo->flags; if (use_extended_sk && i && !(keyinfo->flags & HA_NOSAME)) { add_pk_parts_to_sk(keyinfo, share->key_info, &key_part, &rec_per_key); } share->key_parts+= keyinfo->hidden_key_parts; } ... } ...}

重点来了,首先只有InnoDB引擎才支持隐式修改second index,然后是通过add_pk_parts_to_sk()函数将pk的列 加入到second index中,当然加入过程中有些,如对key中可列数和key的长度的。

3 总结

InnoDB引擎原本的二级索引中的记录就会包含pk的列,之前只是为了通过二级索引去定位主索引,也就时pk和second key之间数据 的一一映射,并没有别的用途,现在增加了在sk中对pk的排序,可以说是在没有增加存储开销的情况下,使得记录有序性更强,也就是 更加有利于最优计划的生成。当然代价可能就是插入时候key compare的时间会稍微变长。

总的来说,InnoDB在不断完善,Oracle也可以说是功不可没吧,虽然我个人很讨厌Oracle现在把MySQL搞的没什么活力了,很多东西不再那么open了, 比如mysql-test的test case,bug系统等,希望Oracle能把MySQL做的更好吧。


File translated fromTEXby TTH,version 4.03.
On 27 Jan 2013, 16:51.

bitsCN.com

下载本文
显示全文
专题