文章目录

  • 1. 索引的作用?
  • 2. 索引有哪些数据结构
    • ①:Hash
    • ①:二叉树
    • ②:红黑树
    • ③:B-Tree
    • ④:B+Tree
  • 3. B+Tree与B-Tree的区别
  • 4. InnoDB 和 MyISAM 存储引擎
    • ①:MyISAM存储引擎
    • ②:InnoDB存储引擎
    • ③:InnoDB与MyISAM的区别
  • 5. 索引回表 和 MRR回表优化
  • 6. 复合索引的最左前缀原则

1. 索引的作用?

索引就像数据的目录,是帮助MySQL高效获取数据的排好序数据结构,那么他有什么作用呢?


以上图为例,如果我们要查询col2 = 89 的数据,sql如下:

select * from table where Col2 = '89';

如果没有索引,要经过6次磁盘IO,才能找到col2 = 89 的数据,我们知道磁盘IO对系统性能是会有一定影响的。如果数据量小还好说,但当数据量起来了,达到百万、千万级别,那查询就会很慢了。比如要查询第1000w条记录,如果没有索引,需要从第一行一直查到第1000w行,时间可能有几秒钟或者几十秒,这显然是不合理的,所以需要使用索引进行优化查询!注意:索引也是存储在磁盘上的,查索引也需要磁盘IO,只不过通过索引查某条数据比全表查的磁盘IO次数要少一些!

图中使用的是二叉树对col2 这一列数据做的索引,把数据库中的col2 这一列的数据用二叉树保存起来,在使用索引列查询的时候,直接去二叉树上找,只需要两次磁盘IO就可以找到col2 = 89 的数据,查询效率得到了提升,但二叉树并不是目前mysql使用的数据结构。因为数据结构的选择决定着查询效率,二叉树虽然可以提升查询效率,但还有很多缺陷,所以mysql选择了B+Tree作为数据结构去使用!

2. 索引有哪些数据结构

①:Hash


hash表的特点如下:

  • 对索引的key进行一次hash计算就可以定位出数据存储的位置,很多时候Hash索引要比B+ 树索引更高效
  • 仅能满足 “=”,“IN”,不支持范围查询
  • hash冲突问题

由于Hash的不支持范围查找,mysql常用的索引不是Hash索引结构,但可以选择hash索引。
注意:mysql表中可选数据结构为:hashBTree

①:二叉树

二叉树在特定条件下是可以提升查询效率的,但如果数据列中都是顺序数据,那么二叉树就无法提升查询效率了,比如查询select * from table where Col1 = '6';,可以看到Col1中的数据都是顺序的,在对Col1这一列使用二叉树生成索引时,其形状如下:

所以二叉树对这种情况无法做效率提升!不适合作为mysql的数据结构

②:红黑树


        可以看到红黑树针对二叉树做了优化调整,当单边数据太多时,会做一个平衡,自动调整树结构,所以只需要3次磁盘IO即可查到col1 = 6的数据!但mysql为什么依然没有使用红黑树作为数据结构呢?

因为红黑树的高度问题,我们知道在树中的查找效率与树的高度是密切相关的。当数据量很大,达到几百上千万条时,红黑树的高度也将变得很大很大,因为红黑树每个节点只存一个数据,当查询第1000w条数据时,就算通过索引去查,也需要经过很多次磁盘IO才能查到这条数据。所以红黑树的树高问题,也不适合作为mysql的索引数据结构!

③:B-Tree


        红黑树每个节点上只存一个数据,导致大数据量时高度太高,B-Tree为了优化数的高度,如图所示:每一层树高上存储多个节点,节点中的数据索引从左到右递增排列,这样每个节点区间(数据页)内又可以向下延伸新的节点区间(数据页)。这样每一层都可以放更多的索引元素,有效的降低了树的高度,B-Tree具有以下特点

  • 节点中的数据索引从左到右递增排列
  • 所有索引节点上都存储数据,所有索引节点不重复

④:B+Tree

B+Tree 作为 B-Tree 的变种,有以下特点

  • 非叶子节点不存储data,只存储索引(冗余),可以放更多的索引
  • 叶子节点包含所有索引字段
  • 节点中的数据索引从左到右递增排列,叶子节点用指针连接,提高区间访问的性能

B+Tree在查询数据时,也是从上往下查询的,首先第一次磁盘IO把B+Tree的第一层数据加载到内存中,然后通过算法找到找个要查的这个数据位于第一层的哪个区间(数据页),然后再进行一次磁盘IO把这个区间加载到内存,到这个区间中去找,以此类推。。。最终可找到想要的数据!mysql正是使用了B+Tree的数据结构,才可以支撑千万级的数据。

比如在上图中查询30,第一次磁盘IO把第一层数据加载到内存,然后在内存中通过算法发现30位于【15,56】之间,然后再次进行磁盘IO把【15,26】之间的数据加载到内存,发现30位于【20,49】之间,然后第三次磁盘IO把【20,30】加载到内存,这一共就经历了3次磁盘IO就找到了30这个数据!

为什么说mysql使用了B+Tree就可以支撑千万级的数据呢?

mysql默认的每一个数据区间(数据页)的大小大概在16kb,可以通过以下命令查看:

show GLOBAL STATUS LIKE 'innodb_page_size';

结果如下:

通过上面对B+Tree数据结构的了解,我们可以估算一下,三层高的树可以存储多少数据?

以索引列为bigint为例,每个数据占用8个字节(其实大部分索引列用不到bigint,在这里就往大的去算),如下图所示:如果这颗B+Tree全部被占满的话,可存储21902400 条数据,这仅仅3层树高就可存储这么多数据!!如果是不是用索引,要进行几千万次磁盘IO,使用B+Tree只需要3次磁盘IO即可,所以说B+Tree极大的提高了查询效率

3. B+Tree与B-Tree的区别

①:树高不同

mysql为什么选择B+Tree作为索引的数据结构呢?我们已经知道查询效率与树的高度有关,树的高度越低,磁盘IO越少,查询效率越快。B+Tree在应对千万级的数据量时,只需要两三次磁盘IO就可以查到,接下来看B-Tree的表现如何:

        可以看到由于B-Tree的非叶子节点上也存储着数据(数据是某一行所有数据,比较占内存),导致每一层的缓存页16KB很快被占满,只能存储很少的数据量。而B+Tree非叶子节点只存储索引,不存储数据,16KB的缓存页可以存储很多索引,在计算存储量时又是一个次方的关系,所以随着树高的增长,B+Tree的存储量远高于B-Tree。.

②:范围查找效率不同

  • B+Tree的叶子节点使用双向链表把数据维护起来,在使用范围查找时,直接通过指针就可以快速查找范围数据
  • B-Tree的叶子节点没有使用双向指针维护数据顺序,在做范围查找时,需要不断的根节点出发查找下一个符合条件的数据,会很慢

4. InnoDB 和 MyISAM 存储引擎

首先不管是InnoDB 还是 MyISAM 存储引擎,他们对数据的存储位置都是一样的,都存储在mysql安装包的/data目录下,例如:E:\tools\MySQL\data\local,但不同的存储引擎有着不同的存储结构

①:MyISAM存储引擎


可以看到一个表如果使用的是MyISAM的存储结构,那么在磁盘中会存储三种文件,以不同的后缀结尾:

  • .frm:存储test_myisam表的表结构信息
  • .MYD:存储test_myisam表的数据信息
  • .MYI:存储test_myisam表的索引信息

MyISAM无论是主键索引还是非主键索引都是非聚集索引(叶子节点不包含所有数据记录),索引文件和数据文件是分离的,跨文件查询速度较慢


通过索引查找数据时:

从B+Tree的顶端,自上而下进行查询,叶子节点存储的data是该条索引对应的全量数据的磁盘地址,根据这个地址查找到该条数据!

②:InnoDB存储引擎


可以看到一个表如果使用的是InnoDB的存储结构,那么在磁盘中会存储两种文件,以不同的后缀结尾:

  • .frm:存储test_innodb_lock表的表结构信息
  • .ibd:存储test_innodb_lock表的索引和数据信息,使用的B+Tree来组织的!

聚集与非聚集索引

InnoDB主键索引就是聚集索引(叶子节点包含了完整的数据记录,查询速度较快),聚集索引在innoDB表中只有一个!

InnoDB非主键索引就是非聚集索引(叶子节点存储的是主键的id,用于回表查询,回表查询导致速度较慢)

非主键索引包括 二级索引、复合索引 等等


问题一:为什么非主键索引叶子节点存储的是主键id?

  • 数据一致性:如果非主键索引叶子节点也保存全量数据,那么新插入一条数据时,就要为主键、非主键的叶子节点都维护好这一条新增数据,任何一个插入失败,都会影响数据一致性。非主键索引叶子节点只存储主键id,保证数据只有一份,降低了数据混乱的风险
  • 节省存储空间:如果主键、非主键的叶子节点都存储全量数据,那将会造成空间浪费,完全没必要!

问题二:为什么建议InnoDB表尽量建立主键,并且推荐使用整型的自增主键?

建议设置主键的原因:

InnoDB的数据和索引信息都存储在xx.idb文件中,这个xx.idb文件本身就是一棵B+Tree来组织的,要组织这棵B+Tree,就必须有一个确定的、且不可重复的列作为索引列

  • 如果数据表建立了主键,因为主键不可重复,那么InnoDB就以这个主键创建一棵B+Tree索引树,所以对于建立了主键(id)的索引,通过id查询是非常快的!
  • 如果如果数据表没有建立主键,那就遍历表中所有列,找到一个列中数据无重复的列作为索引列,建立B+Tree索引树
  • 如果找不到无重复的列,mysql会为当前表生成一个隐藏列作为索引列,隐藏列中数据不重复,这点类似于oraclerowId

mysql自己去遍历或者创建索引列,这个过程是要消耗一定时间的。所以为了避免这种情况,我们在建表时应尽量自己建立主键索引,节省mysql的工作,提高效率!

推荐使用整型的自增主键的原因:

有些公司喜欢用uuid来做主键,这里并不推荐,原因有三点:

  1. 在通过索引查找数据时,需要与数据页区间中的数据作比较,使用uuid还需要转为Ascall码后再做比较,使用整型可直接比较,效率更高
  2. uuid占用内存空间比较大,使用整型可以节省内存!
  3. uuid因为无法做到自增,当插入一个新的uuid时,B+Tree会根据这个uuidAscall码将其有序的放在树中某个位置,这中间就可能会涉及到节点的分裂、重建等等,是相当耗时的。而整型的自增主键,原本就是有序的,在插入时,直接在节点尾部添加即可,不会涉及节点的分裂、重建。

结合以上三点,在建表时尽量设置整型的自增主键!

③:InnoDB与MyISAM的区别

  1. InnoDB 支持事务,MyISAM 不支持。这是 MySQL 将默认存储引擎从 MyISAM 变成 InnoDB 的重要原因之一
  2. InnoDB 最小的锁粒度是行锁,MyISAM 最小的锁粒度是表锁。个更新语句会锁住整张表,导致其他查询和更新都会被阻塞,因此并发访问受限
  3. InnoDB 是聚集索引,MyISAM 是非聚集索引
  4. InnoDB 支持外键, MyISAM 不支持
  5. InnoDB 不保存表的具体行数,执行 select count(*) from table 时需要全表扫描。而MyISAM 用一个变量保存了整个表的行数,执行上述语句时只需要读出该变量即可,速度很快
  6. mysql5.0.3版本的InnoDB引擎开始支持XA事务规范(java中的规范是JTA),可以使用数据库的分布式事务,而MYISAM不支持!

如何选择:
7. 是否要支持事务,如果要请选择 InnoDB,如果不需要可以考虑 MyISAM
8. 如果表中绝大多数都只是读查询,可以考虑 MyISAM,如果既有读写也挺频繁,请使用InnoDB
9. 系统奔溃后,MyISAM恢复起来更困难,能否接受,不能接受就选 InnoDB,InnoDB有binlog日志;

5. 索引回表 和 MRR回表优化

非主键索引的回表操作:

非主键(辅助)索引包括:

  1. 单值索引:叶子节点存储的是主键索引的值
  2. 复合索引:叶子节点存储的也是主键索引的值

在使用单值索引或复合索引去查询数据时,先在单值索引或复合索引的B+Tree从上往下找,找到对应的主键索引的值,然后执行回表操作,根据主键索引的值再去主键索引的B+Tree从上往下找到对应的数据!

也就是说:根据辅助索引的值查询一条完整的 用户记录需要使用到2棵B+树:一次辅助索引树,一次聚集(主键)索引树。

那么为什么还需要一次回表操作呢?直接把完整的用户记录放到辅助索引的叶子节点不就好了么?原因是:

  • 浪费空间:每建立一棵B+树都需要把所有的用户记录再都拷贝一遍
  • 修改性能低下:每次对数据的变化要在所有包含数据的索引中全部都修改一 次

因为回表操作会扫描多颗b+树,所以需要回表的记录越多,使用二级索引的性能就越低,甚至让某些查询宁愿使用全表扫描也不使用二级索引。 那什么时候采用全表扫描的方式,什么时候使用采用二级索引 + 回表的方式去执行查询呢?

这个就是查询优化器做的工作,查询优化器会事先对表中的记录计算一些统计数据,然后再利用这些统计数据根据查询的条件来计算一下需要回表的记录数,需要回表的记录数越多,就越倾向于使用全表扫描,反之倾向于使用二级索引 + 回表的方式。

回表优化 – MRR多范围读取

通过辅助索引查到的主键id一般都是随机的,如果每一个随机id都进行一次回表,发生一次磁盘io。那么查找的数据量很大时,回表操作就会很多,io开销很大。Mysql中提出了一个名为Disk-Sweep Multi-Range Read (MRR,多范围读取)的优化措施,即先读取一部分二级索引记录,将它们的主键值排好序之后再统一执行回表操作。因为mysql读取数据是按页读取的(一页16kb),一次磁盘扫描就可以把整页数据读取到内存中,而这一页数据也是连续的,因为读取的是主键索引b+树。

那么刚好这一页被读到内存中的数据 就与 回表时的一批有序id匹配到了,这样的话,这一批有序id只进行了一次回表操作即可。而如果不使用MRR,在辅助索引上读取到的id都是随机的,在回表时很难享受到页缓存中已经读取到的id,这样就会进行非常多的回表操作。 所以MRR在一定程度上提高了索引回表的效率,其本质也是利用了 磁盘顺序IO比随机IO速度快 的原理

6. 复合索引的最左前缀原则

上图的索引是一个联合主键,联合主键即是主键索引也是复合索引,这里着重分析一下复合索引,复合索引也是一种排好序的B+Tree数据结构。只不过与单值索引的排序方式有些区别:

  • 单值索引:只有一个值,按照这个值去排序即可
  • 复合索引:有多个值,则会从左到右按值排序,比如上图的nameageposition组成的复合索引,构建B+Tree时的排序方式如下:
    • 从左开始,先根据name的大小,数据从小到大排列
    • 如果name相等,再看age的大小,数据从小到大排列
    • 如果age相等,再看position的大小,数据从小到大排列
    • 如果nameageposition都相等,则这组数据放在一起,通过回表的主键id做区分!

查询时,想要使用已经排好序的复合索引,需要遵循最左前缀原则:如下案例所示

//会走索引,只走name
explain select * from employees where name = 'aa' //会走索引,只走name,age
explain select * from employees where name = 'aa'  and age = 3  //会走索引,只走name,因为中间跳过了age后,position变得无序
explain select * from employees where name = 'aa'  and position = '5' //不会走索引
explain select * from employees where position = '5'//会走索引,>=全部走到,会走name,age,position
explain select * from employees where name = 'aa'  and age >= 3 and position = '5'   //会走索引,>只会走name,age 不会走position
explain select * from employees where name = 'aa'  and age > 3 and position = '5'  //会走索引,!=只会走name,age 不会走position
explain select * from employees where name = 'aa'  and age != 3 and position = '5'

mysql索引为什么使用B+tree,InnoDB与MyISAM 的区别相关推荐

  1. mysql索引innodb和myisam的区别

    引用 引用 区别 InnoDB支持事务,MyISAM不支持,对于InnoDB每一条SQL语言都默认封装成事务,自动提交,这样会影响速度,所以最好把多条SQL语言放在begin和commit之间,组成一 ...

  2. 常见面试题:为什么MySQL索引要用B+Tree呢?(看完你就能和面试官笑谈人生了)

    title: 常见面试题:为什么MySQL索引要用B+Tree呢?(看完你就能和面试官笑谈人生了) tags: 面试常见题 常见面试题:为什么MySQL索引要用B+Tree呢?(看完你就能和面试官笑谈 ...

  3. mysql存储引擎中INNODB和MyISAM的区别

    切记:存储引擎是基于表的,而不是数据库. 存储引擎概念: MySQL中的数据用各种不同的技术存储在文件(或者内存)中.这些技术中的每一种技术都使用不同的存储机制.索引技巧.锁定水平并且最终提供广泛的不 ...

  4. MySQL存储引擎InnoDB和MyISAM的区别

    文章目录 前言 一.MySQL 引擎 二.MyISAM和InnoDB特点 三.InnoDB和MyISAM的区别 总结 前言 面试经常会问到MySQL存储引擎的相关内容.常见的MySQL存储引擎有MyI ...

  5. mysql存储引擎InnoDB与MyISAM的区别

    mysql存储引擎InnoDB与MyISAM的区别 众所周知,mysql之前的存储引擎是MyISAM,在5.6版本之后默认的存储引擎是InnoDB,那么两个存储引擎有什么区别? 一.MyISAM 存储 ...

  6. MySql InnoDB与MyISAM的区别

    一.InnoDB与MyISAM的区别? 存储引擎是对底层物理数据执行实际操作的组件,为Server服务层提供各种操作数据的API.常用的存储引擎有InnoDB.MyISAM.Memory.这里我们主要 ...

  7. InnoDB和MyISAM的区别与选择

    MyISAM 性能(适合小项目,读快速)MyISAM 是MySQL中默认的存储引擎,比如适合新闻系统,读为主. InnoDB 事务或外键支持(适合大项目,高并发读写)活跃用户20多万时候,也能很轻松应 ...

  8. 数据表的类型(INNODB与MYISAM 的区别)

    数据表的类型(INNODB与MYISAM 的区别) SHOW CREATE DATABASE school; -- 查看创建数据库的语句 SHOW CREATE TABLE student; -- 查 ...

  9. innodb和myisam的区别

    innodb和myisam的区别: (1)事务处理: MyISAM是非事务安全型的,而InnoDB是事务安全型的(支持事务处理等高级处理): (2)锁机制不同: MyISAM是表级锁,而InnoDB是 ...

最新文章

  1. 为 ActionScript 导出库元件
  2. 使用JQuery筛选sharepoint日历里的超链接
  3. linux发行版本在这里。
  4. Microstation研发
  5. Linux字符驱动开发学习总结
  6. 密钥登陆Linux服务器
  7. 题解【黑匣子_NOI导刊2010提高(06)】(洛谷P1801)
  8. java之socket的OOBInline和UrgentData和发送心跳包研究
  9. 高级转录组分析和R数据可视化第十一期(报名线上课还可免费参加线下课)
  10. 20150905-Y1506401-19+benz2015+at、crontab等使用方法
  11. 【ES6】什么是Promise?解析Promise的基本用法
  12. 2020款iPhone SE最快下周发布:价格3000以内
  13. 实现系统菜单的两种方式
  14. 求一篇计算机word文档作业,计算机应用基础作业3:Word2003
  15. 打破国外垄断,开发中国人自己的编程语言(1):编写解析表达式的计算器
  16. java 去停用词_Lucene学习之——停用词
  17. 【009】Excel宏编程相关封装模块(边框细线、边框粗线、列宽、行高)_002_#VBA
  18. push declined due to email privacy restrictions (GH007 error code) 解决方法
  19. python字典相乘_python集合、元组、字典
  20. 双十一海量数据下EagleEye的使命和挑战

热门文章

  1. c++ class struct同名_如何把C++的源代码改写成C代码?而C改C++只需一步!
  2. java异常应用_Java异常处理机制 —— 深入理解与开发应用
  3. leetcode mysql 排名_GitHub - nimphy/leetcode-Mysql
  4. shell的建立与执行实验报告_实验七 Shell脚本运行的优化
  5. 超级终端软件哪个好_同城配送软件哪个好?如何选择配送软件?
  6. python ide是什么意思_初学Python使用什么IDE会更好?
  7. [Leetcode]字符串转换整数 (ATOI)
  8. 图形用户界面和交互输入方法---图形用户界面的设计
  9. 六大举措深耕光通信市场
  10. 巴西政府考虑用微软产品替换开源软件