MySQL InnoDB创建索引

栏目: 数据库 · 发布时间: 6年前

内容简介:InnoDB的索引基于B+树实现,每张InnoDB的表都有一个特殊的索引,叫做聚簇索引(Clustered Index),聚簇索引存储了表中的真实数据。索引项的顺序和真实的表数据顺序是一致的,B+树的叶子节点存储了真实的数据。这也就是所谓的“数据即索引”。聚簇索引的创建方式一般有三种:InnoDB中,所有的非聚簇索引都叫二级索引(Secondary Indexes),与聚簇索引不同,二级索引的叶子节点不再是数据,而是存储类似<索引值,主键>的结构,通过主键查询聚簇索引。二级索引的创建可以由用户在SQL中自

1.基本概念

1.1 聚簇索引

InnoDB的索引基于B+树实现,每张InnoDB的表都有一个特殊的索引,叫做聚簇索引(Clustered Index),聚簇索引存储了表中的真实数据。索引项的顺序和真实的表数据顺序是一致的,B+树的叶子节点存储了真实的数据。这也就是所谓的“数据即索引”。聚簇索引的创建方式一般有三种:

  1. 用户定义了主键,那么InnoDB依据主键创建聚簇索引
  2. 用户没有定义主键,那么InnoDB根据表上的第一个唯一非空的列创建聚簇索引
  3. 如果以上两条都不符合,那么InnoDB会自动指定一个系统列作为聚簇索引(后面提到)

1.2 二级索引

InnoDB中,所有的非聚簇索引都叫二级索引(Secondary Indexes),与聚簇索引不同,二级索引的叶子节点不再是数据,而是存储类似<索引值,主键>的结构,通过主键查询聚簇索引。二级索引的创建可以由用户在 SQL 中自定义。

1.3 InnoDB系统列

InnoDB在创建表的时候,除了用户自定义的列之外,还会额外地增加几个隐藏的列,这些列在MySQL Server看来是不可见的,我们称之为系统列。比如,用户建表语句为 CREATE TABLE t (a int, b int) ENGINE=InnoDB; 在InnoDB中,实际创建的列为| DB_ROW_ID | DB_TRX_ID | DB_ROLL_PTR | a | b |,对这些字段的解释如下:

字段名

长度

描述

DB_ROW_ID

6B

一个单调递增的行ID

DB_TRX_ID

6B

表示记录最后被插入或更新时对应的事务ID

DB_ROLL_PTR

7B

指向回滚段中的UNDO log

在聚簇索引的创建过程中,若用户既没有定义主键,也没有符合要求的唯一非空列,则InnoDB使用DB_ROW_ID作为主键创建聚簇索引。

1.4 索引相关基本数据结构

  1. dict_index_t 这个是描述索引的数据结构,其大多成员会在dict_mem_index_create()被初始化成0, NULL, false. dict_mem_index_create()用于创建一个索引的内部对象。
  2. dict_table_t 这个是描述表结构的数据结构,其大多成员会在dict_mem_table_create()被初始化成0, NULL, false. dict_mem_table_create()用于创建一个表的内部对象。其中一个和索引直接相关的成员是indexes,这是一个链表,代表了该表上的所有索引。

2.代码分析

2.1 建表时创建索引

假设我们在建表时不创建主键,但是创建一个二级索引,SQL语句形如: CREATE TABLE t (a int, b int, index idx(b)) ENGINE=InnoDB; step1: 无论如何,聚簇索引都是会在第一步产生的,这里我们没有定义主键,也,没有定义唯一非空列,所以只能让InnoDB自动创建聚簇索引。其大致的函数调用关系如下:

|-ha_create_table ()
  |-handler::ha_create ()
	  |-ha_innobase::create ()
	    |-innobase_basic_ddl::create_impl<dd::Table> ()
	      |-create_table_info_t::create_table ()
	        |-create_clustered_index_when_no_primary ()
	          |-...
	            |-dict_index_add_to_cache ()
				        |-dict_index_add_to_cache_w_vcol () //把新建的索引加入索引链表
				          |-dict_index_build_internal_clust () //创建聚簇索引缓存
					          |-dict_index_add_col ()  //将需要索引的列添加到索引中

整个创建默认聚簇索引的过程在InnoDB层完成,主要涉及的开始创建的函数create_table在文件ha_innodb.cc,判断表定义是否有主键的信息来自于结构体TABLE_SHARE->primary_key以及TABLE_SHARE->keys,如果用户没有定义主键,则调用函数create_clustered_index_when_no_primary()进行默认主键的创建任务,期间的调用关系如上文所示,还需要注意的就是这个dict_index_build_internal_clust(),该函数为聚簇索引创建了一个内部数据字典缓存。

step2: 还是基于上述的建表语句,当聚簇索引创建成功后,紧接着需要创建二级索引,也即上表中'idx'. 创建二级索引的函数调用和创建聚簇索引基本一致,大致关系如下:

|-ha_create_table ()
  |-handler::ha_create ()
	  |-ha_innobase::create ()
	    |-innobase_basic_ddl::create_impl<dd::Table> ()
	      |-create_table_info_t::create_table ()
	        |-create_index ()
	          |-...
	            |-dict_index_add_to_cache ()
				        |-dict_index_add_to_cache_w_vcol () //把新建的索引加入索引链表
				          |-dict_index_build_internal_non_clust () //创建二级索引缓存
				    	      |-dict_index_add_col ()  //将需要索引的列添加到索引中

二级索引的判断依据是TABLE_SHARE->keys,keys代表了表中定义的索引键值的数量,在创建二级索引的过程中,会通过一个for循环扫描所有键,并为之创建二级索引,当然,主键已经创建了聚簇索引,所以会被排除在外。create_index()函数是创建二级索引的入口,最后通过dict_index_build_internal_non_clust()创建二级索引的内部缓存,从整个流程上来看,两者的区别不大。

2.2 重启后创建索引

MySQL重启后,内部索引对象丢失,需要在启动后重新创建相关的索引。MySQL重启后首先会将数据字典内的信息进行读取和初始化,然后根据数据字典的信息进行索引的创建。还是以上文的表t为例,假设现在 MySQL 重启,如何在t上构建索引? step1: 创建聚簇索引 无论如何,聚簇索引都会第一个创建。当我们首次对t进行查询或者更新时,由于默认聚簇索引不会被持久化到数据字典,所以对于表t来说,重启之后系统需要重新为之创建一个默认的聚簇索引。其大致的函数调用关系如下:

|-open_tables_for_query ()
  |-open_tables ()
    |-open_and_process_table ()
	    |-open_table ()
	      |-open_table_from_share ()
		      |-handler::ha_open ()
		        |-ha_innobase::open ()
			        |-dd_open_table<dd::Table> ()
			          |-dd_open_table_one<dd::Table> ()
				          |-dd_fill_dict_index ()
				            |-dict_index_add_to_cache ()
                      |-...	// 和建表时创建索引的流程一致

可以看出,首先我们会打开一个表对象,并获取其中的信息。当然,首先会打开系统表以查看持久化的信息。关键的函数是dd_fill_dict_index(),该函数用于实例化索引相关的元数据。对于没有定义聚簇索引的情况,在该函数中作出判断,并创建新的聚簇索引,加入缓存。 step2: 创建二级索引 创建二级索引的过程和创建聚簇索引的过程稍有不同,原因在于用户自定义的二级索引是需要持久化的,所以需要先读数据字典,然后建立索引。在dd_fill_dict_index()中一个一个建立二级索引,基于函数dd_fill_one_dict_index(),将索引添加到表中。其大致的函数调用关系如下:

|-open_tables_for_query ()
  |-open_tables ()
    |-open_and_process_table ()
	    |-open_table ()
	      |-open_table_from_share ()
		      |-handler::ha_open ()
		        |-ha_innobase::open ()
			        |-dd_open_table<dd::Table> ()
			          |-dd_open_table_one<dd::Table> ()
				          |-dd_fill_dict_index ()
				            |-dd_fill_one_dict_index () //创建二级索引
				              |-dict_index_add_to_cache ()
                        |-...	// 和建表时创建索引的流程一致

综上所述,其实建表时创建索引和重启后创建索引的本质过程是一样的,只是在前期步骤有所区别,到了真正为某张表添加索引的时候,所走的路径可以说是完全一样的,不同情况下创建索引的步骤是殊途同归。

How to create index Clustered and Secondary Indexes

封面图片: Marcin Wichary , used under a Creative Commons license


以上所述就是小编给大家介绍的《MySQL InnoDB创建索引》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

结网

结网

王坚 / 人民邮电出版社 / 2010-12-10 / 59.00元

本书以如何创建、发布、推广互联网产品为主线,介绍了互联网产品经理的工作内容以及应对每一部分工作所需的方法和工具。为用户创造价值是产品经理的第一要务,产品经理的工作是围绕用户及具体任务展开的,本书丰富的案例和透彻的分析道出了从发现用户到最终满足用户这一过程背后的玄机。 本书面向现在正在从事及未来将要从事互联网相关工作的创业者和产品经理,也可以作为互联网产品策划人员或相关专业学生的参考书。新版完......一起来看看 《结网》 这本书的介绍吧!

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具