阅读 150

(转)MySQL索引设计

(转)MySQL索引设计

原文:https://www.cnblogs.com/jenvid/p/9022469.html

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算法就可以定位到数据存储位置

不同hashB+
查询过程不同根据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类型

TypeClusteredSecondary
索引主键长度4 bytes4 bytes
索引指针长度6 bytes6 bytes
平均一行记录长度假设300 bytes假设300 bytes
一个页大小16k bytes =16384bytes16k 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,fnamefrom custwhere lanme='xxx'andcity='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


文章分类
后端
文章标签
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐