*************************索引分类*************************

逻辑分类:
Single column indexes单列索引
Combined Indexes,Multiple-Column Indexes 多列索引
Unique 唯一索引
NonUnique 非唯一索引


物理存储分类:
clustered index 聚簇索引(主键和数据存放到一起)
not-clustered index非聚簇索引(普通索引)


数据结构分类:
BTREE索引,是B+树(B+ Tree)的简写
HASH索引(InnoDB是自适应哈希索引),只用于HEAP表
空间索引(5.6以前Myisam存储引擎支持,5.7InnoDB引入空间索引),很少用
Fractal Tree索引,用于TokuDB表

全文索引(5.6.10版本开始支持)

*************************B+TREE索引*************************

演化:二分查找->平衡二叉树->B树->b+树

介绍:所有记录节点都是按键值的大小顺序存放在同一层的叶子节点上,由各叶子节点指针进行连接

插入操作:必须保证插入后叶子节点记录依然排序,需要考虑的几种情况


Leaf Page满
Index Page满操作
NONO直接将记录插入叶子节点
YES
NO

1)拆分Leaf Page

2)将中间的节点放入到Index Page中

3)小于中间节点的记录放左边

4)大于或者等于中间节点的记录放到右边

YESYES

1)拆分Leaf Page

2)小于中间节点的记录放左边

3)大于或者等于中间节点的记录放到右边

4)拆分IndexPage

5)小于中间节点的记录放左边

6)大于中间节点的记录放右边

7)中间节点放入上一层IndexPage

YES左右页节点未满旋转,首先检查左兄弟节点,左兄弟节点如果未满,则移到左兄弟节点,这样做的好处是没必要做拆分

删除操作:使用填充因子来控制树的删除变化,50%是填充因子可设的最小值,删除操作同样必须保证删除后叶子节点的排序性,删除操作的几种情况:


叶子节点小于填充因子中间节点小于填充因子
操作
NONO直接将记录从叶子节点删除,如果该节点还是Index page的节点,那么则用该页节点的右节点替代
YESNO合并叶子节点和它的兄弟节点,同时更新Index Page
YESYES

1)合并叶子节点和他的兄弟节

2)更新index page

3)合并index page和它的兄弟节点

*************************B+TREE索引分类*************************

聚集索引:按照每张表的主键构造一颗B+TREE,叶子节点中存放的就是整张表的行记录数据,也将聚集索引的叶子节点称为数据页。在InnoDB表中,可使用py_innodb_page_info工具来分析页,可看出索引是单独存放的一个页,该页储存键值以及指向数据页的偏移量。

非聚集索引:叶子节点不包含行记录的全部数据,叶子节点除了包含键值外,每个叶子节点中的索引行中还包含了聚集索引键,通过该聚集索引键可获取行内容。

*************************B+TREE索引管理*************************

创建索引语法:


CREATE [ONLINE|OFFLINE] [UNIQUE|FULLTEXT|SPATIAL] INDEX index_name

    [index_type]

    ON tbl_name (index_col_name,...)

    [index_option]

    [algorithm_option | lock_option] ...


index_col_name:

    col_name [(length)] [ASC | DESC]


index_type:

    USING {BTREE | HASH}


index_option:

    KEY_BLOCK_SIZE [=] value

  | index_type

  | WITH PARSER parser_name

  | COMMENT 'string'


algorithm_option:

    ALGORITHM [=] {DEFAULT|INPLACE|COPY}


lock_option:

    LOCK [=] {DEFAULT|NONE|SHARED|EXCLUSIVE}

或者

ALTER  TABLE tbl_name

  | ADD {INDEX|KEY} [index_name]

        [index_type] (index_col_name,...) [index_option] ...

  | DROP PRIMARY KEY

  | DROP {INDEX|KEY} index_name

index_col_name:

    col_name [(length)] [ASC | DESC]

index_type:

    USING {BTREE | HASH}

index_option:

    KEY_BLOCK_SIZE [=] value

  | index_type

  | WITH PARSER parser_name

  | COMMENT 'string'

查看表上索引信息:

mysql> show index from t1\G;

*************************** 1. row ***************************

        Table: t1

   Non_unique: 0

     Key_name: PRIMARY

 Seq_in_index: 1

  Column_name: id

    Collation: A

  Cardinality: 9

     Sub_part: NULL

       Packed: NULL

         Null: 

   Index_type: BTREE

      Comment: 

Index_comment: 

*************************** 2. row ***************************

        Table: t1

   Non_unique: 1

     Key_name: idx_name

 Seq_in_index: 1

  Column_name: name

    Collation: A

  Cardinality: 9

     Sub_part: NULL

       Packed: NULL

         Null: 

   Index_type: BTREE

      Comment: 

Index_comment: 

2 rows in set (0.02 sec)


ERROR: 

No query specified

命令show index结果中每列的含义:

1、Table 表的名称。

2、 Non_unique 如果索引不能包括重复词,则为0,如果可以则为1。

3、 Key_name 索引的名称

4、 Seq_in_index 索引中的列序列号,从1开始。

5、 Column_name 列名称。

6、 Collation 列以什么方式存储在索引中。在MySQL中,有值‘A’(升序)或NULL(无分类)。

7、Cardinality 索引中唯一值的数目的估计值。通过运行ANALYZE TABLE或myisamchk -a可以更新。基数根据被存储为整数的统计数据来计数,所以即使对于小型表,该值也没有必要是精确的。基数越大,当进行联合时,MySQL使用该索引的机会就越大。

8、Sub_part 如果列只是被部分地编入索引,则为被编入索引的字符的数目。如果整列被编入索引,则为NULL。

9、 Packed 指示关键字如何被压缩。如果没有被压缩,则为NULL。

10、 Null 如果列含有NULL,则含有YES。如果没有,则该列含有NO。

11、 Index_type 用过的索引方法(BTREE, FULLTEXT, HASH, RTREE)。

12、 Comment 多种评注,您可以使用db_name.tbl_name作为tbl_name FROM db_name语法的另一种形式。这两个语句是等价的:

*************************B+TREE创建和删除的四种方式*************************

1、传统索引创建方式

  • 首先创建一张新的临时表,表结构为通过命令ALTER TABLE新定义的结构

  • 然后把原始表中数据导入到临时表

  • 删除原表

  • 最后把临时表重新命名为原来的表名

总结:对于大表的索引创建和删除,需要较长时间,若是有大量事务需要访问正在被修改的表,意味着此时数据库不可用

2、FIC索引创建方式(inplace)

对于辅助索引的创建需要加一个S锁,创建过程不需要重建表,删除索引时只需要更新内部视图,并将付诸索引的空间标记为可用,同时删除MySQL内部视图上对该表的索引定义。

缺点:创建过程中只能对该表进行读操作,另外该操作只限定于辅助索引,对于主键的创建和修改同样需要重建一张表。

3、Online Schema Change(OSC)

  • init 初始化阶段,检查表是否有主键,是否有外键或者触发器,如果无主键或者有外键和触发器,那么直接报错

  • createCopyTable 创建和原始表结构一样的新表

  • alterCopyTable 对创建的新表进行alter table操作,如添加索引或者列

  • createDeltasTable 创建deltas表,该表的作用是为下一步创建的触发器所使用之后对原表的所有的DML操作都会被记录到deltas表中

  • createTriggers 对原表创建INSERT,DELETE,UPDATE操作的触发器,触发操作产生的记录被写入到deltas表中

  • startSnpshotXact 开始OSC操作的事务

  • selectTableIntoOutfile 将原表中的数据写入到新表中。为了减少对原表的锁定时间,这里通过分片将数据输出到多个外部文件,然后将外部文件的数据导入到copy表中,分片的大小可以指定,默认为500000

  • dropNCIndexs 在导入到新表前,删除新表中所有的辅助索引

  • loadCopyTable 将导出的分片文件导入到新表

  • replayChanges 将OSC过程中原表的DML操作的记录应用到新表中,这些记录被保存在deltas

  • recreateNCIndexes 重建辅助索引

  • replayChanges 再次回放DML日志的,这些日志是在创建辅助索引期间产生的

  • swapTables 原表和新表交换名字,速度很快

缺点:修改的表一定要有主键,不能有外键和触发器,如果Binlog复制模式不是row模式,那么可能主从复制有问题,因为过程中会有set sql_bin_log=0操作。

4、Online DDL

注(何登成大神博客)

online方式实质也包含了copy和inplace方式,对于不支持online的ddl操作采用copy方式,比如修改列类型,删除主键等;对于inplace方式,mysql内部以“是否修改记录格式”为基准也分为两类,一类需要重建表(修改记录格式),比如添加、删除列、修改列默认值等;另外一类是只需要修改表的元数据,比如添加、删除索引、修改列名等。Mysql将这两类方式分别称为rebuild方式和no-rebuild方式。online ddl主要包括3个阶段,prepare阶段,ddl执行阶段,commit阶段,rebuild方式比no-rebuild方式实质多了一个ddl执行阶段,prepare阶段和commit阶段类似。下面将主要介绍ddl执行过程中三个阶段的流程。

Prepare阶段:

创建新的临时frm文件 持有EXCLUSIVE-MDL锁,禁止读写 根据alter类型,确定执行方式(copy,online-rebuild,online-norebuild) 更新数据字典的内存对象 分配row_log对象记录增量 生成新的临时ibd文件

ddl执行阶段:

降级EXCLUSIVE-MDL锁,允许读写 扫描old_table的聚集索引每一条记录rec 遍历新表的聚集索引和二级索引,逐一处理 根据rec构造对应的索引项 将构造索引项插入sort_buffer块 将sort_buffer块插入新的索引 处理ddl执行过程中产生的增量(仅rebuild类型需要)

commit阶段

升级到EXCLUSIVE-MDL锁,禁止读写 重做最后row_log中最后一部分增量 更新innodb的数据字典表 提交事务(刷事务的redo日志) 修改统计信息 rename临时idb文件,frm文件 变更完成

关键函数堆栈

拷贝数据

row_merge_build_indexes

row_merge_read_clustered_index

//拷贝全量

{

遍历老表的聚集索引

row_build //创建一个row

row_merge_buf_add //将row加入到sort_buffer

row_merge_insert_index_tuples //插入到新表(聚集索引+二级索引)

}

row_log_table_apply // 对于rebuild类型, 处理增量

{

row_log_table_apply_insert //以insert为例

row_log_table_apply_convert_mrec //将buf项转为tuple

{

插入聚集索引 // row_ins_clust_index_entry_low

插入二级索引 // row_ins_sec_index_entry_low

}

}

修改表数据字典

commit_try_norebuild,commit_try_rebuild

常见的ddl操作

类型

并发DML

算法

备注

添加/删除索引

Yes

Online(no-rebuild)

全文索引不支持

修改default值

修改列名

修改自增列值

Yes

Nothing

仅需要修改元数据

添加/删除列

交换列顺序

修改NULL/NOT NULL

修改ROW-FORMAT

添加/修改主键

Yes

Online(rebuild)

由于记录格式改变,需要重建表

修改列类型

Optimize table

转换字符集

No

Copy

需要锁表,不支持online

若干问题

1.如何实现数据完整性

使用online ddl后,用户心中一定有一个疑问,一边做ddl,一边做dml,表中的数据不会乱吗?这里面关键部件是row_log。row_log记录了ddl变更过程中新产生的dml操作,并在ddl执行的最后将其应用到新的表中,保证数据完整性。

2.online与数据一致性如何兼得

实际上,online ddl并非整个过程都是online,在prepare阶段和commit阶段都会持有MDL-Exclusive锁,禁止读写;而在整个ddl执行阶段,允许读写。由于prepare和commit阶段相对于ddl执行阶段时间特别短,因此基本可以认为是全程online的。Prepare阶段和commit阶段的禁止读写,主要是为了保证数据一致性。Prepare阶段需要生成row_log对象和修改内存的字典;Commit阶段,禁止读写后,重做最后一部分增量,然后提交,保证数据一致。

3.如何实现server层和innodb层一致性

在prepare阶段,server层会生成一个临时的frm文件,里面包含了新表的格式;innodb层生成了临时的ibd文件(rebuild方式);在ddl执行阶段,将数据从原表拷贝到临时ibd文件,并且将row_log增量应用到临时ibd文件;在commit阶段,innodb层修改表的数据字典,然后提交;最后innodb层和mysql层面分别重命名frm和idb文件。