1.没有索引时的查找流程:
主键查找:逐页查找,定位到记录所在的页,在页中用二分法定位到槽,便利槽中的记录进行查找。
非主键查找:逐页逐条比对,查找数据。
2.索引方案:
主键索引:在存储实际数据的页上层,用数据页中最小主键和页号构建一个目录层,目录层的数据也存储在数据页中,如果数据过多就继续拆分目录页,每次查找时根据主键在目录层定位到数据页号,再到数据页中用二分法查找槽,定位到数据;目录层页数大于1时,就在目录页的上层根据主键和目录页号再构建一层高级目录,用于定位下层的目录项页号,构造成一颗B+树,每次查找从上至下检索,查找到叶子节点的完整数据,主键索引由Innodb引擎自动创建。
二级索引:二级索引需要重新构建一棵B+索引树,叶子节点记录的是按搜索条件顺序排列的主键数据,目录层也是按照搜索条件排序,记录了主键和叶子节点的页号(记录主键是为了防止搜索条件重复过多,降低了索引查找效率)。
二级索引查找时先遍历二级索引树查找到对应主键,再到主键索引树查找到具体数据(回表)。
联合索引:以多个列的大小作为排序规则,如c1和c2的联合索引,即将记录按照c1进行排序,c1相同的情况下,按照c2排序。联合索引本质上也是二级索引,只是用多个字段作为排序规则。只构建一棵索引树。
3.索引树生成过程:
以主键索引为例,一开始会为索引生成一个根节点页面,后面插入数据时,都存储在此页,当数据不断增加,跟节点空间用完时,会将根节点数据复制到一个新分配页面a,然后对新页进行页分裂操作,得到页b,新插入的记录根据键值大小分配到页a或者叶b,根节点页面升级为存储目录项记录的页。
4.MyISAM索引方案:
MyISAM也采用了索引方案,但是完整数据乱序存储在一个完整的数据文件中,不划分数据页,每条数据记录一个行号;
索引信息存储在索引文件中,格式为主键+行号,查询时先同过索引树找到对应的行号,再通过行号找记录。
5.索引代价:
(1)索引树占用空间。
(2)每次增删改操作时,需要修改所有索引树。
6.全值匹配:
搜索条件和索引列一致,称为全职匹配,写查询语句时查询条件顺序不需要完全一致,Mysql的优化器会进行处理。
7.匹配左边的列:
非全值匹配时,如果查询条件包含联合索引最左边的列字段,也可以用到联合索引,如果不包含最左边的字段,即使包含了后面的索引字段,也不能使用索引。
8.匹配列前缀:
因为字符串排序是从左到右每个字符逐个比较的,第一个字符先比,如果第一个字符相同,则比较第二个字符,所以除了匹配最左边的列,也可以根据查询条件的前缀使用索引,如:SELECT * FROM person_info WHERE name LIKE 'As%';也可以使用name字段在最左边的索引。
9.匹配范围值:
单个字段的范围匹配,可以用到索引,如果多个字段的范围匹配,只有对索引最左边的列进行范围查找时才能用到索引; 如索引idx_name_birthday_phone,查询条件name和birthday都是范围查询时,只能用到name部分的索引,birthday部分是获取到name查询结果后进行排序的,因为索引中birthday只有在name相同情况下的排序,name范围查找后的结果包含多个name值,无法使用索引中birthday的顺序。
10.精确匹配某一列并范围匹配另外一列:
例如 索引idx_name_birthday_phone,查询条件name = 'Ashburn' AND birthday > '1980-01-01',因为最左边的name是精确查询,查询结果name值相同,所以birthday字段可以直接使用索引中name相同的birthday字段,无需重新排序。
11.排序使用索引:
排序字段与索引字段相同且顺序一致或与索引左边的列一致时,可以使用到索引,直接取出无需在内存中排序;
但是如果排序条件中asc和desc混用则不能直接使用索引, 函数修饰过的排序条件也不能使用索引。
12.用于分组:
分组的顺序如果与索引列顺序一致,分组时也可以直接使用。
13.回表的代价:
回表时即根据范围查找获取到一些连续的主键值,然后根据主键值到主键索引树中查找数据。如果这些主键相连则可能存储在连续的数据页中,则可以用顺序IO从磁盘读取。如果是乱序,数据分布在不同数据页,则是随机IO访问,速度会慢一些。如果需要回表的数据量过大,则可能直接使用全表扫描,效率较低。
所以查询时使用limit限制条数,可以减少回表,提高效率。
14.覆盖索引:
索引列表正好包含了要查询的数据,则无需回表,直接从二级索引树取出所需数据,称为覆盖索引。在查询时也建议查询具体数据而不用*。
14.索引的选择:
(1)为用于搜索,排序,分组的列创建索引,可以覆盖索引,无需回表。
(2)为列的基数大的列创建索引,即重复值少的列,性能更好。
(3)索引列的类型要小一些,节省索引存储空间,比对操作也越快。
(4)查询列的值很长时,可以在索引中只保存前面的一段前缀,节约空间,加快字符串比对。不过无法使用覆盖索引,必须回表。
(5)索引列必须单独出现,不能以某个表达式或者函数调用。
14.主键插入顺序:
主键最好AUTO_INCREMENT顺序递增,减少数据插入时的页分裂。
14.冗余和重复索引:
在联合索引中的最左列,不需要再定义一个普通索引。