InnoDB一定会在索引中加上主键吗
DBA 群里在讨论一个问题,到底InnoDB会不会在索引末尾加上主键,什么时候会加? 我之前看代码记得是如果索引末尾就是主键,那么InnoDB就不再添加主键了,如果索引末尾不是主键,那么会添加主键,但是这跟测试结果不符: CREATETABLE t ( a char(32)notnullpr
DBA群里在讨论一个问题,到底InnoDB会不会在索引末尾加上主键,什么时候会加?
我之前看代码记得是如果索引末尾就是主键,那么InnoDB就不再添加主键了,如果索引末尾不是主键,那么会添加主键,但是这跟测试结果不符:
CREATETABLE t ( a char(32)notnullprimarykey, b char(32)notnull,KEY idx1 (a,b),KEY idx2 (b,a)) Engine=InnoDB; 登录后复制 |
插入部分数据后可以看到idx1和idx2两个索引的大小相同。这说明idx1和idx2的内部结构是一样的,因此 不可能 是idx1在内部存为(a,b,a)。
在登博的指导下看了 dict0dict.cc:dict_index_build_internal_non_clust() 这个函数,就是构造索引的数据字典的过程,理解了这个过程就明白了,我们接下来解读下这个函数(基于5.6最近trunk):
2727/*******************************************************************//**2728 Builds the internal dictionary cache representation for a non-clustered2729 index, containing also system fields not defined by the user.2730 @return own: the internal representation of the non-clustered index */2731static2732 dict_index_t*2733 dict_index_build_internal_non_clust(2734/*================================*/2735const dict_table_t* table, /*!mutex)));2748 ut_ad(table->magic_n == DICT_TABLE_MAGIC_N);27492750/* The clustered index should be the first in the list of indexes */2751 clust_index = UT_LIST_GET_FIRST(table->indexes);27522753 ut_ad(clust_index);2754 ut_ad(dict_index_is_clust(clust_index));2755 ut_ad(!dict_index_is_univ(clust_index));27562757/* Create a new index */2758 new_index = dict_mem_index_create(2759 table->name, index->name, index->space, index->type, 2760 index->n_fields +1+ clust_index->n_uniq);27612762/* Copy other relevant data from the old index2763 struct to the new struct: it inherits the values */27642765 new_index->n_user_defined_cols = index->n_fields;27662767 new_index->id = index->id;27682769/* Copy fields from index to new_index */2770 dict_index_copy(new_index, index, table, 0, index->n_fields);27712772/* Remember the table columns already contained in new_index */2773 indexed =static_cast<ibool>(2774 mem_zalloc(table->n_cols *sizeof*indexed));27752776/* Mark the table columns already contained in new_index */2777for(i =0; i n_def; i++){27782779 field = dict_index_get_nth_field(new_index, i);27802781/* If there is only a prefix of the column in the index2782 field, do not mark the column as contained in the index */27832784if(field->prefix_len ==0){27852786 indexed[field->col->ind]= TRUE;2787}2788}27892790/* Add to new_index the columns necessary to determine the clustered2791 index entry uniquely */27922793for(i =0; i n_uniq; i++){27942795 field = dict_index_get_nth_field(clust_index, i);27962797if(!indexed[field->col->ind]){2798 dict_index_add_col(new_index, table, field->col, 2799 field->prefix_len);2800}2801}28022803 mem_free(indexed);28042805if(dict_index_is_unique(index)){2806 new_index->n_uniq = index->n_fields;2807}else{2808 new_index->n_uniq = new_index->n_def;2809}28102811/* Set the n_fields value in new_index to the actual defined2812 number of fields */28132814 new_index->n_fields = new_index->n_def;28152816 new_index->cached = TRUE;28172818return(new_index);2819}</ibool> 登录后复制 |
这是整个函数,读者最好可以先自己读读这个函数理解一下,然后再看分析。
好了,下面我们开始分析了,首先把 dict_table_t 这个结构体的相关成员解释一下:
474unsigned n_user_defined_cols:10;475/*! 登录后复制 |
注释很好理解,主要是 n_uniq 表示索引中需要多少个字段来唯一标识一行数据,只对唯一索引有效;n_def 是有多少个字段用了扩展存储空间,就是索引中只存前缀; n_fields 是索引最终一共有多少字段,包括系统加的;n_user_defined_cols 是用户定义的字段数,不包括系统自动加的。
然后我们来看两段最主要的代码:
2772/* Remember the table columns already contained in new_index */2773 indexed =static_cast<ibool>(2774 mem_zalloc(table->n_cols *sizeof*indexed));27752776/* Mark the table columns already contained in new_index */2777for(i =0; i n_def; i++){27782779 field = dict_index_get_nth_field(new_index, i);27802781/* If there is only a prefix of the column in the index2782 field, do not mark the column as contained in the index */27832784if(field->prefix_len ==0){27852786 indexed[field->col->ind]= TRUE;2787}2788}</ibool> 登录后复制 |
InnoDB首先创建了一个布尔型数组,然后依次循环索引上的每一个字段,如果这个字段不是只有前缀,那么就在数组中记下它的索引号,标记这个字段在索引中出现了。因此indexed数组就存下了索引中用户定义的所有字段序号。
2790/* Add to new_index the columns necessary to determine the clustered2791 index entry uniquely */27922793for(i =0; i n_uniq; i++){27942795 field = dict_index_get_nth_field(clust_index, i);27962797if(!indexed[field->col->ind]){2798 dict_index_add_col(new_index, table, field->col, 2799 field->prefix_len);2800}2801} 登录后复制 |
这一段就开始循环聚集索引(主键)的每个字段,盘下indexed数组中这个字段是不是有了,如果没有,那么再调用 dict_index_add_col 把字段加到索引中。
因此只要用户定义的索引字段中包含了主键中的字段,那么这个字段就不会再被InnoDB自动加到索引中了,如果用户的索引字段中没有完全包含主键字段,InnoDB就会把剩下的主键字段加到索引末尾。
因此我们最初的例子中, idx1 和 idx2 两个索引内部大小完全一样,没有区别。
最后再补充下组合主键的例子:
CREATETABLE t ( a char(32)notnull, b char(32)notnull, c char(32)notnull, d char(32)notnull,PRIMARYKEY(a,b)KEY idx1 (c,a),KEY idx2 (d,b)) Engine=InnoDB; 登录后复制 |
这个表InnoDB会自动补全主键字典,idx1 实际上内部存储为 (c,a,b),idx2 实际上内部存储为 (d,b,a)。
但是这个自动添加的字段,Server层是不知道的,所以MySQL优化器并不知道这个字段的存在,所以如果你有一个查询:
SELECT * FROM t WHERE d=x1 AND b=x2 ORDER BY a; 登录后复制 |
其实内部存储的idx2(d,b,a)可以让这个查询完全走索引,但是由于Server层不知道,所以最终MySQL优化器可能选择 idx2(d,b) 做过滤然后排序 a 字段,或者直接用PK扫描避免排序。
而如果我们定义表结构的时候就定义为 KEY idx2(d,b,a) ,那么MySQL就知道(d,b,a)三个字段索引中都有,并且InnoDB发现用户定义的索引中包含了所有的主键字段,也不会再添加了,并没有增加存储空间。
因此,由衷的建议,所有的DBA建索引的时候,都在业务要求的索引字段后面补上主键字段,这没有任何损失,但是可能给你带来意外的惊喜。
原文地址:InnoDB一定会在索引中加上主键吗, 感谢原作者分享。

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题

oracle索引类型有:1、B-Tree索引;2、位图索引;3、函数索引;4、哈希索引;5、反向键索引;6、局部索引;7、全局索引;8、域索引;9、位图连接索引;10、复合索引。详细介绍:1、B-Tree索引,是一种自平衡的、可以高效地支持并发操作的树形数据结构,在Oracle数据库中,B-Tree索引是最常用的一种索引类型;2、位图索引,是一种基于位图算法的索引类型等等。

手机是现在中轻年人的必备之物,当然每个年龄段的人对手机需求是不一样的。RedmiK70Pro作为现在比较火爆的机型之一,内部拥有非常多样的功能以及服务,可以满足不同年龄段消费者们的需求。红米RedmiK70Pro怎么设置返回键和主键?也是需要了解清楚的,清楚之后才会决定要不要购买这款手机,那就跟随小编一起来看看下面的内容吧!红米RedmiK70Pro怎么设置返回键和主键?要访问手机的设置菜单,您可以通过下拉通知栏或在主屏幕上查找设置图标来打开设置界面。在设置界面中,查找并点击“按钮”或“导航栏”

如何让MySQL外键和主键自动关联起来?在MySQL数据库中,外键和主键是非常重要的概念,它们能够帮助我们在不同表之间建立关联关系,保证数据的完整性和一致性。在实际的应用过程中,经常需要让外键自动关联到对应的主键上,以避免数据不一致的情况发生。下面将介绍如何通过具体的代码示例实现这一功能。首先,我们需要创建两个表,一个表作为主表,另一个表作为从表。在主表中创

解决方法有:1、检查索引值是否正确:首先确认你的索引值是否超出了数组的长度范围。数组的索引从0开始,所以最大索引值应该是数组长度减1;2、检查循环边界条件:如果是在循环中使用索引进行数组访问,要确保循环的边界条件正确;3、初始化数组:在使用数组之前,要确保数组已经被正确地初始化;4、使用异常处理:在程序中可以使用异常处理机制来捕获索引超出数组界限的错误,并进行相应的处理。

InnoDB的全文搜索功能非常强大,能够显着提高数据库查询效率和处理大量文本数据的能力。 1)InnoDB通过倒排索引实现全文搜索,支持基本和高级搜索查询。 2)使用MATCH和AGAINST关键字进行搜索,支持布尔模式和短语搜索。 3)优化方法包括使用分词技术、定期重建索引和调整缓存大小,以提升性能和准确性。

这篇文章将为大家详细讲解有关PHP返回一个字符串在另一个字符串中开始位置到结束位置的字符串,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。PHP中使用substr()函数从字符串中提取子字符串substr()函数可从字符串中提取指定范围内的字符。其语法如下:substr(string,start,length)其中:string:要从中提取子字符串的原始字符串。start:子字符串开始位置的索引(从0开始)。length(可选):子字符串的长度。如果未指定,则提

如何通过索引提升PHP与MySQL的数据分组和数据聚合的效率?引言:PHP和MySQL是目前应用最广泛的编程语言和数据库管理系统,常常被用于构建web应用程序和处理大量数据。在处理大量数据时,数据分组和数据聚合是常见的操作,但如果不合理地设计和使用索引,这些操作可能会变得非常低效。本文将介绍如何通过索引来提升PHP与MySQL的数据分组和数据聚合的效率,并提

荣耀X50Pro是最近刚刚正式发布的一款旗舰手机,这款手机采用了全新的外观设计,颜值确实是非常不错的,除了颜值之外这款手机的性能配置也很出色,那么荣耀X50Pro怎么设置返回键和主键?下面就让我们来一起看看吧!荣耀X50Pro怎么设置返回键和主键?1、在主屏幕上找到“设置”图标,点击“系统和更新”功能选项;2、点击系统和更新功能项目中的“系统导航方式”;3、荣耀手机提供三种返回键设置,可以根据自己的设置进行调整:荣耀X50Pro这款手机设置返回键和主键的方法还是很简单的,大家可以按照上面的步骤来
