一,前言

大家好,我是小墨,这个章节我将做mysql系列文章,和大家一起分享自己学习的内容,一起进步。
我们对于mysql的索引其实或多或少都有使用到,本章节将讲述索引的使用,分类,来源,结构等,希望能看完本文后能够给大家一个对mysql索引有个更深的理解。

二,索引的来源

这节我们将从mysql的数据结构入手,从底层入手了解mysql的组织结构。
我们先从heidiSql(数据库的操作工具)一张截图看起,你可以看到heidiSql展示的一张表空间至少为16KB,但是我们显示出的数据其实只有三列而已。这里就涉及到mysql一个概念:

我们都知道操作系统中从磁盘拿到数据放到内存并不是按需拿,而是按照规划出的一个个存储单位:一页(4KB)来取的。
对于mysql而言,使用的页大小为16KB,我们可以自己查询得到这个值,所以就有我们刚才看到的,我们kill_test数据库每张表至少16KB。


接下来我们看下数据库如何使用组织页,采用:多个页使用双向链表+页中使用单向链表

我们都知道对于链表而言,便于增删改,难于查,所以我们要去查数据时,遍历的效率过低,需要另一种数据结构用于此时的查找,这就是索引的来源。

三,索引的结构

当我们谈到选择索引的数据结构时必须要结合实际的查询情况,我们知道计算机有层次存储结构


而我们数据库的数据主要存储在磁盘中,每次查询需要读取对应的磁盘中的页数据,放置到内存中进行比较,如果没有命中,再进行下一次磁盘io读取,然后我们考虑日常的查表的数据需求,所以我们提出要求

  1. 能够快速定位到指定的数据
  2. 需要比较好的支持范围查询的功能,即数据能够按照一定的顺序排列。
  3. 支持比较快的顺序检索

就这两个要求,思考如下:

1, hash查找能够快速定位但是数据不能有序排列,不支持范围查询,所以如果只想查找单条记录,可以使用hash索引
2,二叉树的话在大数据量下,产生的树高太高,导致查询时需要多次磁盘io才能定位到数据,而且也不支持顺序查找

我们借此谈谈B树和B+树,为什么数据库采用B+树作为数据结构
B 树又叫平衡多路查找树。一棵m阶的B 树(意味着某一个节点最多有m个字树,但这并不意味着m阶树就叫m叉树,这个要注意,具体可以参考
B树数据结构 B+树数据结构
B树的数据结构如下,

B+树数据结构

对比区别在于:
(1)叶子节点的不同:
B+树的叶子节点包含全部关键字信息,以及这些关键字的指针,而且叶子节点本身按照大小顺序链接
B树的叶子节点只包含当前节点的信息,没有全部的信息,而且叶子节点也没有按照顺序链接。

(2)非叶子节点:
B+树的非叶子节点可以看做是索引部分,只存储最大或者最小关键字,无法获得具体的数据。
B树的非叶子节点包含了全部信息,可以获得全部数据。

从此我们其实可以总结几点:

  • 1)B+树叶子结点有数据的全部信息 -------》数据可以全部存放在叶子中,查询稳定(B树非叶子结点也可以放数据,导致快的时候可以直接在非叶子结点上找到数据)
  • 2)B+树叶子结点按照顺序大小连接 -------》支持顺序查找
  • 3)B+树非叶子结点只有索引数据 --------》非叶子结点占用数据量小,所以一次磁盘读写可以读到更多结点,查找也将更快。
    所以索引采取了B+树这种数据结构。

那现在我们来了解下索引存了啥,如何工作
索引让无序的数据变成有序(相对),使得数据可以进行二分查找
我们应该都知道如果我们不设置主键的话数据库会报错,因为我们这里的主键就要配合对应的指针来查找具体表的数据:
索引(非叶子结点)中存储了指针,主键值(注意,我是用聚簇索引举例),第一次io查询得到的页中拿到的指针和主键值不匹配,就会根据B+树的查询方式一次次去定位,最后获得具体位置数据。

我们来估计下这种存储方式可以存储的数据值:
InnoDB存储引擎中页的大小为16KB,一般表的主键类型为INT(占用4个字节)或BIGINT(占用8个字节),指针类型也一般为4或8个字节,也就是说一个页(B+Tree中的一个节点)中大概存储16KB/(8B+8B)=1K个键值(因为是估值,为方便计算,这里的K取值为〖10〗^3) 也就是说一个深度为3的B+Tree索引可以维护10^3 * 10^3 * 10^3 = 10亿 条记录。

四,索引的分类

唯一索引/非唯一索引

索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一
创建语句: CREATE UNIQUE INDEX indexName ON mytable(username(length))

主键索引(主索引)

主键索引(主索引)是唯一索引的特定类型。表中创建主键时自动创建的索引 。一个表只能建立一个主索引。
创建语句 create table table ( PRIMARY KEY (key) )

聚集索引/辅助索引

聚集索引的B+Tree中的叶子节点存放的是整张表的行记录数据,辅助索引与聚集索引的区别在于辅助索引的叶子节点并不包含行记录的全部数据,而是存储相应行数据的聚集索引键,即主键
则辅助索引查出后还得回表再进行一次查找动作。
创建语句:create CLUSTERED INDEX 索引名称 ON 表名(字段名)
聚集索引优缺点
优点: 查询速度快,因为一旦具有第一个索引值的记录被找到,具有连续索引值的记录也一定物理的紧跟其后。
缺点:对表进行修改速度较慢,这是为了保持表中的记录的物理顺序与索引的顺序一致,而把记录插入到数据页的相应位置,必须在数据页中进行数据重排,降低了执行速度。在插入新记录时数据文件为了维持 B+Tree 的特性而频繁的分裂调整,十分低效。

建议使用聚集索引的场合为:
A.某列包含了小数目的不同值。
B.排序和范围查找。
C.正常而言,使用主键作为聚集索引(主键本质是聚集索引)
这里有一个主键与聚集索引比较

主键 聚集索引
用途 强制表的实体完整性 对数据行的排序,方便查询用
一个表多少个 一个表最多一个主键 一个表最多一个聚集索引
是否允许多个字段来定义 一个主键可以多个字段来定义 一个索引可以多个字段来定义
是否允许 null 数据行出现 如果要创建的数据列中数据存在null,无法建立主键。创建表时指定的 PRIMARY KEY 约束列隐式转换为 NOT NULL 没有限制建立聚集索引的列一定必须 not null . 也就是可以列的数据是 null 参看最后一项比较
是否要求数据必须唯一 要求数据必须唯一 数据即可以唯一,也可以不唯一。看你定义这个索引的 UNIQUE 设置。
创建的逻辑 数据库在创建主键同时,会自动建立一个唯一索引。如果这个表之前没有聚集索引,同时建立主键时候没有强制指定使用非聚集索引,则建立主键时候,同时建立一个唯一的聚集索引 如果未使用 UNIQUE 属性创建聚集索引,数据库引擎 将向表自动添加一个四字节 uniqueifier 列。必要时,数据库引擎 将向行自动添加一个 uniqueifier 值,使每个键唯一。此列和列值供内部使用,用户不能查看或访问。

具体比较请看:主键与聚集索引的区别

联合索引

基于多个字段而创建的索引就称为联合索引。
创建语句: create index idx1 on table1(col1,col2,col3)
创建规则:
如果没有特殊情况,我们一般建议在建立联合索引时,把选择性(不重复的索引值(基数)和表记录数的比值)最高的列放在最前面
举例:

SELECT * FROM payment WHERE staff_id = xxx AND customer_id = xxx;
单就这个语句而言, (staff_id,customer_id) 和 (customer_id, staff_id) 这两个联合索引我们应该建哪一个呢,可以统计下这两者的选择性。
SELECT
COUNT(DISTINCT staff_id)/COUNT() as staff_id_selectivity,
COUNT(DISTINCT customer_id)/COUNT(
) as customer_id_selectivity,
COUNT()
FROM payment
结果为:
staff_id_selectivity: 0.1
customer_id_selectivity: 0.373
COUNT(
): 16049
从中可以看出 customer_id 的选择性更高,所以应该选择 customer_id 作为第一列

我们来谈下多列排序是基于什么原则
节省时间使用了JAVA知音的例子

举例 :create index idx_obj on user(age asc,height asc,weight asc)
联合索引的排序有这么一个原则,从左往右依次比较大小,就拿刚才建立的索引举例子,他会先去比较age的大小,如果age的大小相同,那么比较height的大小,如果height也无法比较大小, 那么就比较weight的大小,最终对这个索引进行排序里是引用
表如下:
B+树:(注意是非簇集索引,最后数据是主键)
1,所以当我们要去比较时,请思考如果是select * from user where height = 2 and weight = 7,此时联合索引能起效果么?
回答:肯定是不行的,因为我联合索引是需要从左往右依次比较大小,就这个索引我们就可以看出
我们把缺失的这一列写作一个问号,那么这条语句的查询条件就变成了?27,那么我们从这课B+树的根节点开始,根节点上有127和365,那么以height和weight来进行比较的话,走的一定是127这一边,但是如果缺失的列数字是大于3的呢?比如427,527,627,那么如果走索引来查询数据,将会丢失数据,错误查询。所以这种情况下是绝对不会走索引进行查询的。这就是最左前缀匹配原则的成因

联合索引需要满足规则:最左匹配原则
联合索引只能用于查找key是否存在(相等),遇到范围查询 (>、<、between、like左匹配)等就不能进一步匹配了,后续退化为线性查找,这里还有个索引下推的优化,下面再谈
我举了个例子,创建了使用联合索引的表,用执行计划前后做了比较,看到不使用范围查找的key_len大于使用范围查找的,(explain执行计划后面再单独解释),说明 查询条件 a=1 and b=2 and c>5 and d=4会在每个节点依次命中a、b、c,无法命中d

mysql> create table test (
-> id int auto_increment,
-> a int,
-> b int ,
-> c int,
-> d int ,
-> e int ,
-> PRIMARY KEY (id),
-> index abcd (a,b,c,d)
-> );

1)覆盖索引

这里其实可以多引出一个概念:覆盖索引
覆盖索引只是特定于具体select语录而言的联合索引。也就是说一个联合索引对于某个select语句,通过索引可以直接获取查询结果,而不再需要回表查询,就称该联合索引覆盖了这条select语句。

例如查询语句是select age from user where name = ‘小墨’,name为非聚簇索引。这种需要用名字来查询年龄经常用的,那么如果使用覆盖索引,建立索引(name ,age),那么就可以直接从索引得到age数据,而不需要再从索引中获取id,再回一次表查出age。

2)索引下推

大家可以注意下我们在使用联合索引时使用这个sql,explain时 extra提示:Using index condition

这里就是指使用索引下推:
索引条件下推优化(Index Condition Pushdown (ICP) )是MySQL5.6添加的,用于优化数据查询。

  • 不使用索引条件下推优化时存储引擎通过索引检索到数据,然后返回给MySQL服务器,服务器然后判断数据是否符合条件,就是回退化为线性查询
  • 使用索引条件下推优化时,如果存在某些被索引的列的判断条件时,MySQL服务器将这一部分判断条件传递给存储引擎,然后由存储引擎通过判断索引是否符合MySQL服务器传递的条件,只有当索引符合条件时才会将数据检索出来返回给MySQL服务器
    从这里可以看出区别,就是对于那些数据如:1,(1,2,6,3)2,(1,2,7,4)3,(1,2,8,1)4,(1,2,9,4).
    存储引擎会提前先帮我们判断,剔除掉1,3,减少回表次数,加快查询速度

五 索引碎碎念

这部分主要是把一些其他有关索引但是又涉及mysql其他部分的也讲一讲,发散下。

1)表碎片导致索引失效

这部分涉及到mysql的表碎片内容。

我们先了解下:为什么说删除和修改会产生碎片?
插入为了高效,都是直接添加到末尾。
删除会去掉该位置的数据,自然相对理解就是一个空格,除非回收整理不然就是无法使用该位置存储空间。
修改是由于如果使用可变长度数据的话,原来插入的数据为了空间利用,会按照字节长度放入,如果新修改数据大于原来数据长度,导致该空间放不下,需要删除原来位置数据,然后插入到末尾去。

所以碎片过多,会导致生成的索引结构不紧凑,从而mysql认为走索引没什么用,导致索引失效,需要我们进行表的重建操作。

2)唯一索引与普通索引的选择

这部分涉及到mysql的change buffer方面知识
我们先了解唯一索引和普通索引的查询过程

  • 唯一索引取值唯一,当查到第一个值时就会停止。
  • 普通索引查到第一个记录后,还会继续向下查找一个,直到遇到第一个不是该记录值为止。我们知道mysql会直接将整个数据页(16KB)读取到内存,除非万一找到的值存放在当前页的最后一条,这时需要在进行一次磁盘的io读写,判断还有没有其他相同数据,不然的话只会多出一步查找和判断过程
    所以查找过程 差距不大。如果更新呢?
    因为有了第三者change buffer,则不一样了,因为唯一索引不适用change buffer ,而普通索引使用!!!

我们了解下changebuffer(写缓冲)
在执行更新数据(insert delete update)操作时,mysql会在不影响数据一致性份上,先将更新操作缓存到change buffer上,后台会定时或者在打满缓冲区后进行写缓冲区内容进去硬盘的操作。

有了这个概念后我们谈谈两者会如何更新
注意分两种情况:

  1. 当前更新数据页在内存中,1)唯一索引会找到合适位置,判断无冲突插入值 2)普通索引,找到位置,插入值
  2. 当前数据页不在内存中,之前加载的时候没加载到这个页。1)唯一索引由于需要保证数据一致性,得时时刻刻保证当前数据是否一致,如果写到change buffer的话不能保证原来库是否无此数据。要先将数据页读入内存,判断冲突后再插入。 2)普通索引,直接记录语句在change buffer,end!!

大家应该注意到这里就是大区别了,在更新操作的时候当前更新数据页不在内存中,唯一索引多出一步随机IO的访问

所以如果需要保证数据唯一性,可以使用唯一索引,不然选择普通索引更好

六,总结

通过上面的一系列学习,我们对索引已经有了比较好的了解,我们接下来谈谈该怎么使用好索引来帮助我们加快查询速度:

  1. 最佳左前缀法则:索引了多列,要遵守最左前缀法则。指的是查询从索引的最左前列开始并且不跳过索引中的列
  2. 不在索引列上做任何操作,不然导致索引失效而转向全表扫描
  3. 范围条件放最后
  4. 覆盖索引尽量用。尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少select *
  5. 不等于要甚用,mysql 在使用不等于(!= 或者<>)的时候无法使用索引会导致全表扫描。
  6. Like查询要当心。通配符开头(’%abc…’)mysql索引失效会变成全表扫描的操作,解决方式:覆盖索引

这种sql优化可以直接右转看这个,总结的不错。

各位觉得可以点个赞哈。

参考文章
https://blog.csdn.net/qq_36098284/article/details/80178336
https://www.runoob.com/mysql/mysql-index.html
https://cloud.tencent.com/developer/article/1193302
https://mp.weixin.qq.com/s/-gmAPfiKMNJgHhIZqR2C4A
https://mp.weixin.qq.com/s?__biz=MzI4Njc5NjM1NQ==&mid=2247490706&idx=1&sn=d98cd10845923c2bf5d933a8fd963cb5&chksm=ebd623bedca1aaa87256f9729192d024897ba70bc38a0abc314cd59b1a1349c52ec33f1074a1&scene=21#wechat_redirect

【小墨mysql】mysql系列之一---索引相关推荐

  1. 【MySQL 面试系列】索引原理

    文章目录 一.索引概述 1.索引的定义 2.索引的作用 3.索引的优缺点 二.索引结构 1.概述 2.索引的演进 1.二叉树 2.B-Tree 3.B+Tree 4.Hash 二.索引分类 1.MyS ...

  2. MySQL数据库系列

    MySQL基础系列 1.SQL语句的分类与MySQL简单查询 2.MySQL条件查询 3.排序与分组函数 4.group by和having 5.连接查询 6.子查询及limit分页 7.MySQL数 ...

  3. MySQL优化系列(二)--查找优化(1)(非索引设计)

    MySQL优化系列(二)--查找优化(1)(非索引设计) 接下来这篇是查询优化,用户80%的操作基本都在查询,我们有什么理由不去优化他呢??所以这篇博客将会讲解大量的查询优化(索引以及库表结构优化等高 ...

  4. MySQL索引系列--联合索引--使用/原理/优化

    原文网址:MySQL索引系列--联合索引--使用/原理/优化_IT利刃出鞘的博客-CSDN博客 简介         本文介绍MySQL的联合索引(也可以称为:组合索引.复合索引)的用法. MySQL ...

  5. mysql优化-----多列索引的左前缀规则

    索引优化策略1:索引类型1.1B-tree索引 关注的是:Btree索引的左前缀匹配规则,索引在排序和分组上发挥的作用.注:名叫btree索引,大的方面看都用的二叉树.平衡树.但具体的实现上,各引擎稍 ...

  6. 数据库MYSQL学习系列三

    数据库MYSQL学习系列三 三.MYSQL事务与存储引擎 3.1-数据库事务 什么是事务 一系列有序的数据库操作: o要么全部成功 o要么全部回退到操作前的状态 o中间状态对其他连接不可见 事务的 ...

  7. 面试mysql中怎么创建索引_阿里面试:MySQL如何设计索引更高效?

    有情怀,有干货,微信搜索[三太子敖丙]关注这个不一样的程序员. 本文 GitHub https://github.com/JavaFamily 已收录,有一线大厂面试完整考点.资料以及我的系列文章. ...

  8. mysql匹配数据结构_MySQL索引背后的数据结构及原理

    前两天经历了武汉一行腾讯面试,数据库索引是一个面试热点,在此搜集相关资料,以备学习之用. 下面是一位牛人写得关于数据库索引的精品之作,因为很好,不敢修饰,转载至此与博友共享. 本文以MySQL数据库为 ...

  9. Mysql(三)索引、视图、存储过程、触发器、分区表

    文章目录 一.索引 1.1 索引概述 1.2 索引的基本原理 1.3 索引的优缺点 1.4 索引的创建与删除 1.5 索引分类[逻辑角度] 1.5.1 主键索引 1.5.2 唯一索引 1.5.3 普通 ...

  10. MySQL优化系列12-MySQL分区表

    备注:测试数据库版本为MySQL 8.0 文章目录 一.分区表简介 二.分区的类型 2.1 range分区 2.2 list分区 2.3 colums分区 2.3.1 RANGE COLUMNS分区 ...

最新文章

  1. 什么样的人适合做产品经理
  2. mysql中的强制索引_MYSQL中常用的强制性操作(例如强制索引)
  3. 意大利不禁止华为;13 款 5G 手机时间表公布;亚马逊 CEO 遭威胁 | 极客头条
  4. 程序设计实践(评注版) 评注者序
  5. 连表查询 个人感觉就是根据笛卡尔积产生的数据后 每条去匹配如果 匹配成功那么就筛选出来...
  6. python 把4个二进制组成float_4个方法用Python自由定制Excel表格
  7. ASP.NET页面刷新的几种方法
  8. C语言的32个关键字怎么背,谁知道c语言的32个关键字怎么读,还有语法。
  9. qqkey获取原理_HIT我守护的一切手游电脑版苹果版有吗 HIT我守护的一切iOS电脑版模拟器...
  10. JavaScript使用手册
  11. SICP第一章:构造过程抽象(1.3)
  12. 在被线上大量日志输出导致性能瓶颈毒打了很多次之后总结出的经验
  13. solid works定义样条曲线
  14. 如何查询主机IP地址
  15. ChemOffice Suite 2018 18.1.2.18
  16. Uber提出基于Metropolis-Hastings算法的GAN改进思想
  17. ABP 详解系列9:基于ABP框架实现RBAC(角色访问控制)
  18. 子域名挖掘,子域名爆破,Python脚本编写(Python安全攻防)。
  19. 【认证】【DVB】T2 PLP
  20. 做外贸,我们应该如何做推广?

热门文章

  1. 【西北师大-2108Java】第二次作业成绩汇总
  2. Mac 好用的 Android 模拟器整理(玩游戏、装应用、支持咸鱼、拼多多...)
  3. 2022美团CTF个人决赛WP
  4. python爬取谷歌学术_JS反爬绕过思路之--谷歌学术镜像网链接抓取
  5. 软件测试人员如何月薪过万、月薪过万的秘籍
  6. vm虚拟机搭建click house(单机)
  7. 基于Java的卡诺图化简
  8. 解决在iOS复制失败问题 iOS/Android通用
  9. 大数据可以考哪些证书?
  10. 怎么篡改计算机硬盘大小,怎么样更改Parallels Desktop硬盘大小 Parallels Desktop虚拟机硬盘大小如何更改...