整体概念

在学习索引之前,先了解下数据库的整体概念。

逻辑架构

MySQL的逻辑结构分为三层:客户端层、核心服务层、存储引擎层。

  • 客户层:连接处理、授权认证、安全等功能均在这一层处理。
  • 核心服务层:包括查询解析、分析、优化、缓存、内置函数(比如:时间、数学、加密等函数)。所有的跨存储引擎的功能也在这一层实现,如:存储过程、触发器、视图等。
  • 存储引擎层:其负责MySQL中的数据存储和提取。和Linux下的文件系统类似,每种存储引擎都有其优势和劣势。中间的服务层通过API与存储引擎通信,这些API接口屏蔽了不同存储引擎间的差异。

SQL语句的查询过程

  1. 客户端向MySQL服务器发送一条查询请求
  2. 服务器首先检查查询缓存,如果命中缓存,则立刻返回存储在缓存中的结果,否则进入下一阶段。
  3. 服务器进行SQL解析、预处理、再由优化器生成对应的执行计划。
  4. MySQL根据执行计划,调用存储引擎的API来执行查询 将结果返回给客户端,同时缓存查询结果。

常用sql优化策略

  • 对查询进行优化,要尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引
  • 应尽量避免在 where 子句中的索引字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描
  • 最好不要给数据库索引字段留NULL,尽可能的使用 NOT NULL填充数据库。
  • 应尽量避免在 where 子句中使用 != 或 <> 操作符,否则将引擎放弃使用索引而进行全表扫描
  • 应尽量避免在 where 子句中使用 or 来连接条件,如果一个字段有索引,一个字段没有索引,将导致引擎放弃使用索引而进行全表扫描。可使用union all将两个查询分开后结果组合。
  • in 和 not in 也要慎用,否则会导致全表扫描,可使用exists替换,如果是连续的数字,可改为between。
  • 模糊查询like将导致全表扫描,可以改为 like 'xxx%',可以使用索引。
  • 不要在where子句中使用变量,因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时,它必须在编译时进行选择。因此,在编译时建立访问计划,变量的值还是未知的,故无法作为索引选择的输入项。可使用强制索引 select id from t with(index(索引名)) where num = @num
  • 避免在where子句中对字段进行表函数、算术运算或其他表达式运算。
  • 避免使用 * 指明需要的列,返回必需数据,减小通信间数据包的大小和数量。

基础规范

  • 使用innodb存储引擎,支持事务、行级锁(非索引使用表锁)、并发性能更好、CPU及内存缓存页优化使得资源利用率更高。
  • 使用UTF8字符集,万国码,无需转码,无乱码风险,节省空间。
  • 数据表、数据字段必须加入中文注释,降低时间久了后期维护困难。
  • 避免使用存储过程、视图、触发器、Event。高并发大数据的互联网业务,架构设计思路是“解放数据库CPU,将计算转移到服务层”,并发量大的情况下,这些功能很可能将数据库拖死,业务逻辑放到服务层具备更好的扩展性,能够轻易实现“增机器就加性能”。数据库只做最擅长的存储与索引。
  • 避免存储大文件或者大照片。不要让数据库做它不擅长的事情,可将大文件和照片存储在文件系统,数据库里存url。

  • 避免使用外键,如果有外键完整性约束,需要应用程序控制。外键会导致表与表之间耦合,update与delete操作都会涉及相关联的表,十分影响sql 的性能,甚至会造成死锁。高并发情况下容易造成数据库性能,大数据高并发业务场景数据库使用以性能优先。

  • 避免使用TEXT、BLOB类型,会浪费更多的磁盘和内存空间,非必要的大量的大字段查询会淘汰掉热数据,导致内存命中率急剧降低,影响性能。

  • 避免使用enum,增加新的ENUM值要做DDL操作,ENUM的内部实际存储就是整数。

  • 禁止使用SELECT *,只获取必要的字段,需要显示说明列属性,读取不需要的列会增加CPU、IO、NET消耗,不能有效的利用覆盖索引,使用SELECT *容易在增加或者删除字段后出现程序BUG。

  • 禁止使用INSERT INTO t_xxx VALUES(xxx),必须显示指定插入的列属性,增加或者删除字段后出现程序BUG。

  • 禁止使用属性隐式转换,SELECT uid FROM t_user WHERE phone=13812345678 会导致全表扫描,而不能命中phone索引。

  • 禁止负向查询,以及%开头的模糊查询,负向查询条件:NOT、!=、<>、!<、!>、NOT IN、NOT LIKE等,会导致全表扫描。

  • 禁止大表使用JOIN查询、子查询。

B+Tree

是数据库索引实现的底层原理,一棵 m 阶的 B+ 树需满足下列条件:

  1. 每个分支结点最多有 m 棵子树。
  2. 非叶根节点至少有两棵树,其他每个分支结点至少有(m/2)(向上取整) 棵子树.
  3. 结点的子树个数等于关键字的个数。
  4. 所有叶结点包含全部关键字及指向相应记录的指针,叶结点中将关键字按大小顺序排列,并且相邻叶结点按大小顺序相互链接起来。
  5. 所有分支结点中仅包含它的各个子结点中关键字的最大值及指向其子结点的指针。

数据节点只存在于叶子节点中,且叶子节点间增加了横向的指针,这样顺序遍历所有数据将变得非常容易。

B+树由最初的二叉树演变而来:

B树的分裂:下图的红色值即为每次新插入的节点。每当一个节点满后,就需要发生分裂,分裂是一个递归过程。

B+树的分裂:当一个结点满时,分配一个新的结点,并将原结点中1/2的数据复制到新结点,最后在父结点中增加新结点的指针;B+树的分裂只影响原结点和父结点,而不会影响兄弟结点,所以它不需要指向兄弟节点的指针。

数据库中的B+树应该是非常扁平的,数据库里B+树的尺度:

  1. 每个叶子节点存储了468行数据,每个非叶子节点存储了大约1200个键值,这是一棵平衡的1200路搜索树!
  2. 对于一个22.1G容量的表,也只需要高度为3的B+树就能存储了,这个容量大概能满足很多应用的需要了。如果把高度增大到4,则B+树的存储容量立刻增大到25.9T之巨!
  3. 对于一个22.1G容量的表,B+树的高度是3,如果要把非叶节点全部加载到内存也只需要少于18.8M的内存(如何得出的这个结论?因为对于高度为2的树,1203个叶子节点也只需要18.8M空间,而22.1G从良表的高度是3,非叶节点1204个。同时我们假设叶子节点的尺寸是大于非叶节点的,因为叶子节点存储了行数据而非叶节点只有键和少量数据。),只使用如此少的内存就可以保证只需要一次磁盘IO操作就检索出所需的数据,效率是非常之高的。

索引

可以说数据库必须有索引,没有索引则检索过程变成了顺序查找,O(n)的时间复杂度几乎是不能忍受的。我们非常容易想象出一个只有单关键字组成的表如何使用B+树进行索引,只要将关键字存储到树的节点即可。当数据库一条记录里包含多个字段时,一棵B+树就只能存储主键,如果检索的是非主键字段,则主键索引失去作用,又变成顺序查找了。这时应该在第二个要检索的列上建立第二套索引。  这个索引由独立的B+树来组织。有两种常见的方法可以解决多个B+树访问同一套表数据的问题,一种叫做聚簇索引(clustered index ),一种叫做非聚簇索引(secondary index)。这两个名字虽然都叫做索引,但这并不是一种单独的索引类型,而是一种数据存储方式。对于聚簇索引存储来说,行数据和主键B+树存储在一起,辅助键B+树只存储辅助键和主键,主键和非主键B+树几乎是两种类型的树。对于非聚簇索引存储来说,主键B+树在叶子节点存储指向真正数据行的指针,而非主键。

为了更形象说明这两种索引的区别,我们假想一个表如下图存储了4行数据。其中Id作为主索引,Name作为辅助索引。

聚簇索引(InnoDB存储引擎)

InnoDB使用的是聚簇索引,将主键组织到一棵B+树中,而行数据就储存在叶子节点上,若使用"where id = 14"这样的条件查找主键,则按照B+树的检索算法即可查找到对应的叶节点,之后获得行数据。若对Name列进行条件搜索,则需要两个步骤:第一步在辅助索引B+树中检索Name,到达其叶子节点获取对应的主键。第二步使用主键在主索引B+树种再执行一次B+树检索操作,最终到达叶子节点即可获取整行数据。

非聚簇索引(MyISM储存引擎)

MyISM使用的是非聚簇索引,非聚簇索引的两棵B+树看上去没什么不同,节点的结构完全一致只是存储的内容不同而已,主键索引B+树的节点存储了主键,辅助键索引B+树存储了辅助键。表数据存储在独立的地方,这两颗B+树的叶子节点都使用一个地址指向真正的表数据,对于表数据来说,这两个键没有任何差别。由于索引树是独立的,通过辅助键检索无需访问主键的索引树。

Page结构

Page是整个InnoDB存储的最基本构件,也是InnoDB磁盘管理的最小单位,与数据库相关的所有内容都存储在这种Page结构里。Page分为几种类型,常见的页类型有数据页(B-tree Node)Undo页(Undo Log Page)系统页(System Page) 事务数据页(Transaction System Page)等。单个Page的大小是16K(编译宏UNIV_PAGE_SIZE控制),每个Page使用一个32位的int值来唯一标识,这也正好对应InnoDB最大64TB的存储容量(16Kib * 2^32 = 64Tib)。

Page的头部保存了两个指针,分别指向前一个Page和后一个Page,头部还有Page的类型信息和用来唯一标识Page的编号。根据这两个指针我们很容易想象出Page链接起来就是一个双向链表的结构,page的结构如下图所示:

行数据和索引的存储,他们都位于Page的User Records部分,User Records占据Page的大部分空间,User Records由一条一条的Record组成,每条记录代表索引树上的一个节点(非叶子节点和叶子节点)。在一个Page内部,单链表的头尾由固定内容的两条记录来表示,字符串形式的"Infimum"代表开头,"Supremum"代表结尾。这两个用来代表开头结尾的Record存储在System Records的段里,这个System Records和User Records是两个平行的段。InnoDB存在4种不同的Record,它们分别是主键索引树非叶节点 、主键索引树叶子节点 、辅助键索引树非叶节点 、辅助键索引树叶子节点。这4种节点的Record格式有一些差异,但是它们都存储着Next指针指向下一个Record。

User Record在Page内以单链表的形式存在,最初数据是按照插入的先后顺序排列的,但是随着新数据的插入和旧数据的删除,数据物理顺序会变得混乱,但他们依然保持着逻辑上的先后顺序。

把User Record的组织形式和若干Page组合起来,就看到了稍微完整的形式。

现在看下如何定位一个Record:

  1 通过根节点开始遍历一个索引的B+树,通过各层非叶子节点最终到达一个Page,这个Page里存放的都是叶子节点。

  2 在Page内从"Infimum"节点开始遍历单链表(这种遍历往往会被优化),如果找到该键则成功返回。如果记录到达了"supremum",说明当前Page里没有合适的键,这时要借助Page的Next Page指针,跳转到下一个Page继续从"Infimum"开始逐个查找。

根据B+树节点的不同,User Record可以被分成四种格式:

 主索引树非叶节点(绿色):

1 子节点存储的主键里最小的值(Min Cluster Key on Child),这是B+树必须的,作用是在一个Page里定位到具体的记录的位置。

2 最小的值所在的Page的编号(Child Page Number),作用是定位Record。

 主索引树叶子节点(黄色):

1 主键(Cluster Key Fields),B+树必须的,也是数据行的一部分 。

2 除去主键以外的所有列(Non-Key Fields),这是数据行的除去主键的其他所有列的集合。 这里的1和2两部分加起来就是一个完整的数据行。

辅助索引树非叶节点非(蓝色) :

1 子节点里存储的辅助键值里的最小的值(Min Secondary-Key on Child),这是B+树必须的,作用是在一个Page里定位到具体的记录的位置。

2 主键值(Cluster Key Fields),非叶子节点为什么要存储主键呢?因为辅助索引是可以不唯一的,但是B+树要求键的值必须唯一,所以这里把辅助键的值和主键的值合并起来作为在B+树中的真正键值,保证了唯一性。但是这也导致在辅助索引B+树中非叶节点反而比叶子节点多了4个字节。(即下图中蓝色节点反而比红色多了4字节)

3 最小的值所在的Page的编号(Child Page Number),作用是定位Record。

辅助索引树叶子节点(红色) :

1 辅助索引键值(Secondary Key Fields),这是B+树必须的。

2 主键值(Cluster Key Fields),用来在主索引树里再做一次B+树检索来找到整条记录。

结合B+树的结构和前面介绍的4种Record的内容,我们终于可以画出一幅全景图。由于辅助索引的B+树与主键索引有相似的结构,这里只画出了主键索引树的结构图,只包含了"主键非叶节点"和"主键叶子节点"两种节点:

注:Page和B+树节点之间并没有一 一对应的关系,Page只是作为一个Record的保存容器,它存在的目的是便于对磁盘空间进行批量管理。

Mysql索引原理整理相关推荐

  1. mysql json匹配key为数值_干货篇:一篇文章让你——《深入解析MySQL索引原理》

    概述 最近一段时间重新深入研究了一遍MySQL的内容,今天主要分享分析MySQL索引原理,后续会输出一些关于MySQL方面的干货,希望各位小伙伴喜欢. 一.什么是索引.为什么要建立索引? 关于索引的理 ...

  2. 转:MySQL索引原理及慢查询优化

    来自:http://tech.meituan.com/mysql-index.html MySQL凭借着出色的性能.低廉的成本.丰富的资源,已经成为绝大多数互联网公司的首选关系型数据库.虽然性能出色, ...

  3. (转)MySQL索引原理及慢查询优化

    转自美团技术博客,原文地址:http://tech.meituan.com/mysql-index.html 建索引的一些原则: 1.最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到 ...

  4. 干货:MySQL 索引原理及慢查询优化

    转载自 http://blog.csdn.net/kaka1121/article/details/51815368 MySQL凭借着出色的性能.低廉的成本.丰富的资源,已经成为绝大多数互联网公司的首 ...

  5. 知识点:Mysql 索引原理完全手册(1)

    知识点:Mysql 索引原理完全手册(1) 知识点:Mysql 索引原理完全手册(2) 知识点:Mysql 索引优化实战(3) 知识点:Mysql 数据库索引优化实战(4) Mysql-索引原理完全手 ...

  6. MySQL索引原理及慢查询优化

    背景 MySQL凭借着出色的性能.低廉的成本.丰富的资源,已经成为绝大多数互联网公司的首选关系型数据库.虽然性能出色,但所谓"好马配好鞍",如何能够更好的使用它,已经成为开发工程师 ...

  7. mysql索引原理及用法

    MySQL索引原理及慢查询优化 Mysql explain用法和性能分析 MySQL 索引优化全攻略 1.索引作用 在索引列上,除了上面提到的有序查找之外,数据库利用各种各样的快速定位技术,能够大大提 ...

  8. MySQL索引原理及慢查询优化,了解一下?

    MySQL凭借着出色的性能.低廉的成本.丰富的资源,已经成为绝大多数互联网公司的首选关系型数据库. 虽然性能出色,但所谓"好马配好鞍",如何能够更好的使用它,已经成为开发工程师的必 ...

  9. 比较好的MySQL索引原理

    MySQL索引原理及慢查询优化 - 美团技术团队 https://tech.meituan.com/2014/06/30/mysql-index.html 转载于:https://www.cnblog ...

最新文章

  1. python评语生成_如何评价生成模型框架 ZhuSuan?
  2. java 网络序_Java使用网络字节序进行数据传输
  3. 如何自定义IHttpHandler
  4. Arcengine 10 ecp
  5. mongoose日期 时间 范围查询
  6. 我的世界服务器虚拟菜单插件,[综合|娱乐|管理][比赛]VirtualMenu——支持多种类型GUI的虚拟菜单[1.7.10~1.14]...
  7. P1287 盒子与球(python3实现)
  8. python 添加绝对路径时用反斜杠和正斜杠的区别
  9. 开发人员的测试报告,要包含各平台
  10. 西南交通大学计算机应用基础,西南交通大学计算机应用基础作业-客观部分
  11. 多种云资源管理用什么软件好?你知道吗?
  12. jQuery学习笔记之选取选定复选框的同行某列元素
  13. 【计算机图形学】几何
  14. A goal-driven tree-structured neural model for math word problems论文阅读
  15. 2020年的触动心灵的鸡汤
  16. 2018 再见,你好 2019
  17. 封神台靶场-尤里的复仇-第二章
  18. win7计算机组策略打不开,Win7系统组策略打不开怎么办?组策略被锁住了怎么处理?...
  19. 【图像】搜索相同,或者相似照片
  20. 树莓派挂载和卸载U盘或移动硬盘

热门文章

  1. 微信公众号图卡片息发送【模拟发送一个小程序信息】
  2. ESP32_esp-adf环境搭建
  3. 如何通过rancher部署k8s集群
  4. 软件测试需要学什么,这几点一定要知道
  5. 关于sql中like操作符的使用及效率优化问题整理
  6. STM32下的LED灯闪烁
  7. AVFoundation学习记录
  8. unity有限状态机和模糊状态机(怪物AI、自动寻路)
  9. 【Python CUDA版】河北工业大学计算机图像处理实验四:频域平滑与锐化
  10. input 输入框下划线