MySQL索引设计

Posted 不懂ABAP的python不是好basis

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL索引设计相关的知识,希望对你有一定的参考价值。

一.一张2亿条记录的表,假设一条记录有200字节,查询一条记录使用索引和不使用索引的性能计算

  • 1.假设硬盘的IOPS为100
  • 2.索引长度为:10字节(4字节索引+6字节指针长度)
  • 3.每个页大小为16KB,一个页上可存储索引个数为16k*1024/(6+4)=1638.4
  • 4.一个页可存储的数据大小为16k*1024/200=81,即81行数据
  • 5.找到页就可以找到数据
  • 6.需要页的个数为1638.4*1638.4=2683044
  • 8.页个数乘以页内行数为表大小:2683044*81=217326564,约2亿条记录
  • 9.如果在一棵高为3的树上,只需要3次就能找到需要的记录,需要3/100s,没有索引的话需要400s?

二.binary search-二分查找法

  • 1.对一个有序的集合进行查找
  • 2.取中间值,与需要查找的值比较,如果相等,则查找结束
  • 3.如果查找值比中间值大,则从右边再进行递归二分查找,直到找到
  • 4.如果查找值比中间小,则从左边再进行递归二分查找,直到找到
  • 5.查找时间复杂度为log2(n)

    二分查找耗时:最深节点需要4,最快节点需要1,超找每个节点的成本除以节点个数

三.binary search tree - 二叉查找树

  • 1.左边的节点永远比右边节点要小
  • 2.右边的节点永远比左边的节点要大
  • 3.左边和右边的子节点也是一棵二叉查找树

四.balanced binary tree - 平衡二叉树

  • 1.首先得是一棵二叉树
  • 2.左右两边节点的高度差要么为0,要么为1,层级差不会超过1,当层高超过1时,需要旋转保持平衡
  • 3.平衡二叉树插入数字3

五.b-tree - 一个节点可以拥有多于2个节点的平衡多叉树

  • 1.p为指针,每个节点由keys+1指针
  • 2.所有关键字在整棵树中出现,在非叶子节点也可以存储值
  • 3.叶子节点之间没有链表
  • 4.对于m阶b树,每个节点至多拥有m棵子树
  • 5.根节点至少拥有2棵子树
  • 6.除根节点外,其余每个分支节点至少拥有m/2棵子树
  • 7.所有叶子节点都在同一层上
  • 8.有k棵子树的分支节点存储k-1个key,key按照递增顺序进行排列
  • 9.key的数量需要满足ceil(m/2)-1 <= n <= m-1
  • 10.树越高,磁盘IO的也就需要更多,高度与磁盘存取次数成正比
5.1 新节点插入,高度为h的m阶B树操作
  • 1.被插入节点key没有达到m-1的情况,直接插入

  • 2.插入节点key达到m-1,而父节点没有达到m-1的情况,插入key,插入节点分裂,中间key提升到父节点

  • 3.入节点key达到m-1,而父节点也达到m-1的情况,插入key,插入节点分裂 ,中间key提升到父节点,父节点分裂,中间key提升到父父节点

  • 4.如果一直到根节点都达到了m-1个,那么整棵树增加一层

5.2 删除节点
  • 1.删除节点后还是一棵B-树,直接删除

  • 2.删除节点后,不是一棵B-树,向兄弟节点借key,且兄弟节点有得借的情况,删除key,兄弟节点上移,父节点下移保持平衡

  • 3.如果兄弟节点没得借的情况下,删除,父节点下移,删除空余的叶节点,新节点整体上移

六.b+tree

  • 1.B+树的优势在于存储数据,以便在block-块级别上进行高效的检索,比如说文件系统
  • 2.B-树的非叶子节点和叶子节点都可以存储数据,但是B+树的节点只存储键值(索引)不存储数据,叶子节点才是真正存储数据
  • 3.B+树有非常高的fanout-扇出,即指向其他节点,通常超过100个,因而在查找元素的时候能有效减少I/O操作次数
  • 4.叶子节点之间有链表关系
6.1 B+树操作
  • 1.与B-树类似

  • 2.如果增加值的时候节点满员的情况,则将节点中的值作为新的索引

  • 3.删除的时候,索引的key不会被删除,也不会存在父节点关键字下层的情况

  • 4.叶子节点和父节点未满情况,直接插入

  • 5.叶子结点满员,父节点未满员情况
    分裂子节点
    将key中间值作为新索引,比如60,提升到父节点
    叶子节点的左边为原叶子节点的中间值左边的值,如50,55
    叶子节点的右边为,带中间值的索引和原叶子节点的值,加上新插入的值

  • 6.叶子节点满员,父节点满员的情况
    在叶子节点合适位置插入新的值
    叶子结点分裂,叶子节点中间值上移,右边是新值+原叶子节点分裂后的右边的值
    父节点满员,插入叶子节点的中间值
    父节点分裂,父节点中间值上移,右边是父节点中间值+原父节点分裂后的右边的值
    整棵树增加一层,直到能保持平衡

七.B树与B+树的区别?

不同 B+ B-
关键字数量不同 分支节点有m个关键字,叶子节点也有m个,关键字只是起到索引作用 分支节点有m个子节点但是只有m-1个关键字
存储位置不同 数据存储在叶子节点上所有叶子节点的数据链接起来就是一个有序的完整的数据集 数据存储在每一个节点上,根节点+分支节点+叶子节点
分支节点构造不同 分支节点只存储关键字信息和分支节点指针,也就是索引信息 分支节点不仅存储索引,还存储数据
查询不同 通过所有节点的索引到才能到达叶子节点的数据存储位置,每个数据耗时一致 只要在任何节点上找到数据就可以返回

八.mysql中HASH索引和B+树索引的区别?

采用哈希算法,将键值转换成哈希值,检索时不需要类似B+树那样从根节点到叶子节点检索数据,只需要一次hash算法就可以定位到数据存储位置

不同 hash B+
查询过程不同 根据hash后的key值一定定位到数据存储位置 每次查找不同数据都需要经过所有节点
查询条件不同 只能满足=/in/<=>查询 还能使用范围查询
排序操作不同 hash后的排序和原始键值排序不一致 索引顺序就是数据顺序
索引列利用不同 组合列之后再计算hash值,因此不能使用部分列进行查找 组合索引可以用上部分列进行查找
全表扫描避免不同 hash值和对应的行指针保存在hash表中,当存在相同hash值时,无法获取数据,需要全表扫描 某些情况也会发生全表扫描
效率 大量hash值相等的情况下,选择率太低效率可能不如B树效率高 每次效率都一样

九.聚簇索引与辅助索引的区别?

Type 聚簇索引 辅助索引
存储数据 每个节点存储行的所有列信息 节点存储列字段key+pk
数据查找 找到pk也就是找到了对应列的所有列信息 书签查找:先查找列key字段,再找到pk,通过pk找到一行数据进而查找到其他列值
数据量存储 每个节点存储数据行不多 每个节点存储更多索引信息
效率 更适合排序场景,因为已经排序范围查询效率更高 需要回表,可能需要消耗性能进行排序
个数 一张表只能有一个聚簇索引 可以有多个辅助索引

十.MySQL的索引类型

10.1 B+树索引
  • 1.Cluster index 聚簇索引
    存放了整条记录信息,
    在oracle里面成为索引组织表IOT,数据和索引都放在一张表里面,数据是索引,索引也是数据
  • 2.Secondary inex,二级索引,辅助索引,Non clustered index 非聚簇索引
    只要存放辅助索引的字段和主键,就是键值+主键
  • 3.B+ Tree index

聚簇索引和辅助索引数据存放,假设主键是int类型

Type Clustered Secondary
索引主键长度 4 bytes 4 bytes
索引指针长度 6 bytes 6 bytes
平均一行记录长度 假设300 bytes 假设300 bytes
一个页大小 16k bytes =16384bytes 16k bytes=16384bytes
平均每个页使用率 70% 70%
扇出指针数目(一个页的大小使用了70%除以索引主键长度,索引长度=主键+指针) 16384 * 70% / (4+6)=1000个指针 16384 * 70% /(4+6)=100个指针
平均每个页能存储的记录数 存放的是一整行数据16384*70%/300=35行数据 只存放主键+指针16384*70%/(4+6)=1000行数据
层高为2时记录数 1000 * 35 条记录 1000 * 1000个索引
层高为3时的记录数 1000 * 1000 * 35条记录 1000 * 1000 * 1000个索引
层高为4时的记录数 1000 * 1000 * 1000*35条记录 1000 * 1000 * 1000 * 1000个索引
10.2表中聚簇索引和辅助索引表现
create table userinfo(
userid int not null auto_increment,
username varchar(30),
registdate datetime,
email varchar(50),
primary key(userid),
unique key idx_username(username),
key idx_registdate(registdate)
);

三个索引分别是主键 userid,唯一键idx_username,普通索引idx_registdate
三个索引可以理解为逻辑上创建了3张表,分别是

  • 1.带有所有字段信息的主键索引表
create table userinfo(
userid int not null auto_increment,
username varchar(30),
registdate datetime,
email varchar(50),
primary key(userid)
);
  • 2.只有usernmae字段+pk的idx_username索引表
create table idx_username(
userid int not null,
username varchar(30),
primary key(username,userid)
);
  • 3.只有registdate字段+pk的idx_registdate索引表
create table idx_registdate(
userid int not null,
registdate datetime,
primary key (registdate,userid)
);
  • 4.唯一索引还可以理解为只有一个列的表,当没有主键的情况下,唯一索引作为主键使用
create table idx_username_constraint(
username varchar(30),
primary key(username)
);
10.3 插入数据时索引的表现
start transaction;
insert into userinfo values (aaa,bbb,ccc);
insert into idx_username_constraint(bbb);
insert into idx_username(bbb,aaa);
insert into idx_registdate(ccc,aaa);
commit;

这就是为什么索引越多,反而引起DML操作变慢的原因

10.4 如何使用B+Tree索引
  • 1.Cardinality 基数
    不包含唯一记录的记录数
    高选择率的列作为索引
    使用B+树索引是为了访问更少的数据

  • 2.Not use B+ Tree index situation 不适用B+树索引的情况
    前提是一个辅助索引
    需要大量的随机读
    访问超过所有行的20%
    优化器可能会选择顺序遍历代替索引查找,因而可能会使用主键全表扫描而放弃使用辅助索引
    比如要查找10000条记录,每条记录花费0.00003s,则需要30s才能全部找出来
    整表有500000条记录,根据主键顺序读每次花费0.00003s,只需要15s全部找出来

  • 3.Compound index 联合索引
    多列字段组成的索引,index on(column A,column B)
    A列所有排序的,B列没有
    能使用索引的情况,只要a条件写在前面

select * from t where a=?
select * from t where a=? and b=?

不能使用索引的情况,把b写在了前面select * from t where b=?
同时使用a,b列,b列作为排序情况下,直接在索引里排列,不需要再filesortselect * from t where a=? order by b

  • 4.Covering index 覆盖索引
    三星索引
    第一星:where谓词里的列都在索引中包含,并且作为索引的最开头的列
    第二星:order by里有索引的列,与where的列不重复
    第三星:select中的列都是剩余的列
select cno,fname
from cust
where lanme=\'xxx\'
and
city=\'cityname\'
order by fname;

(lname,city,fname,cno)或者(city,lname,fname,cno)
不需要通过书签查找法,也就是回表
所有需要的数据都在索引包含的列上了
使用了(A,B)中的B也能用索引的情况:单纯统计记录数不找记录的情况select count(1) from t where b>=? and b<=?

  • 5.Index with included column 包含列的索引(SQLSERVER)
    select email from userinfo where username=\'joe\'
    不需要回表,因为索引中含有email这一列
    mysql不行,可以通过多创建一张表,该表含有email这一列
    以空间换时间,需要同时更新两张表

十一.索引规范

  • 1.单张表索引数量不超过5个
  • 2.单个索引中的字段数不超过5个
  • 3.索引名称全部小写
  • 4.非唯一索引命名:idx_字段名_字段名,idx_age_name
  • 5.唯一索引命名:uniq_字段名_字段名,uniq_age_name
  • 6.组合索引建议包含所有字段名,过长字段名缩写,idx_age_name_add
  • 7.表必须有主键,推荐使用unsigned自增列作为主键
  • 8.唯一键由3个以下字段组成,可使用唯一键作为主键,其他情况使用自增列或者发号器做主键
  • 9.禁止冗余索引,如(a,b,c),(a,b)
  • 10.禁止重复索引,如primary key a, uniq index a,将会占用磁盘空间,增加维护负担
  • 11.禁止使用外键
  • 12.join表查询时,join列的数据类型必须一致,并且要建立索引
  • 13.不在低基数的列上键索引,如性别
  • 14.读选择率高的列建索引,组合索引中,选择率高的列放在最前
  • 15.对字符串使用前缀索引,前缀索引长度不超过8个字符
  • 16.不多过长的varchar字段列键索引,优先考虑前缀索引,CRC32/MD5作为索引
  • 17.合理创建联合索引,(a,b,c)相当于(a),(a,b),(a,b,c)
  • 18.合理使用覆盖索引减少IO,避免排序

十二.总结

12.1 索引的有点
  • 1.加快数据检索效率
  • 2.创建唯一性约束索引,保证表的每一行数据的唯一性
  • 3.加速表和表的连接效率
  • 4.使用分组和排序字句记性数据检索时,可以显著减少查询中的分组和排序的事件
12.2 索引的缺点
  • 1.占用更多的物理存储空间
  • 2.对表中数据进行增加、删除、修改时,索引也需要维护更新,降低了数据的维护效率
12.3 那些情况建议创建索引
  • 1.作为主键的列,有唯一约束的索引列
  • 2.经常被用在select/where的列
  • 3.经常用在表连接的列
  • 4.经常用在order by,group by的列
12.4 覆盖索引,通过索引数据结构,找到所需数据,不需要回表
12.5 联合索引,把过滤性号的字段放在前面
12.6 主键的特点,一个表只能有一个主键,用显示声明的主键,最好用自增长的主键
12.7 不能用索引的情况
  • 1.不经常被搜索的列
  • 2.基数值很低的列
  • 3.长文本字段类型列
12.8 不支持函数索引/表达索引,索引列上使用了函数后,无法使用索引,将会导致全表扫描

https://www.cnblogs.com/hanybblog/p/6485419.html
https://www.cnblogs.com/George1994/p/7008732.html
https://www.cnblogs.com/eudiwffe/p/6207196.html

以上是关于MySQL索引设计的主要内容,如果未能解决你的问题,请参考以下文章

solr分布式索引实战分片配置读取:工具类configUtil.java,读取配置代码片段,配置实例

MySQL索引设计一些策略

java.lang.IllegalStateException:键 f0 的片段不再存在:索引 1

Mysql 学习-索引的设计原则

MySQL 索引设计概要

Mysql 索引设计与优化