MySql 索引必知必会的12个知识点

文章内容来自个人学习总结整理笔记、有不对或错误的地方欢迎指正,部分插图引用来自其他大佬的博客,感谢贡献!

1、索引是什么?

索引是帮助Mysql高效获取数据的排好序的数据结构,类似一本书的目录,通过索引可以快速找到对应的数据。

常见的索引数据结构有二叉树、红黑树、Hash、B-Tree、B+Tree,为什么MySQL数据库索引选择使用B+树???

不妨先来看看这些结构是什么样的

红黑树:红黑树是平衡二叉树的一种实现,会自动做平衡处理,即单边数量超过2个以上再插入的时候会做一次节点的平衡;可以看出当数据量特别大的时候,可能会造成树的层级特别高,也就意味着 IO 的次数越多,所以不是最优方案。

红黑色插入数据示例图

二叉树:左小右大,如果数据一直在右侧,则二叉树会失去效果,变成链表的结构。如下图所示,当数据量特别大或者极端情况下,索引的意义也不大,也会有扫描全表的可能性。


二叉树

Hash:其检索效率非常高,索引的检索可以一次定位,不像B-Tree 索引需要从根节点到枝节点,最后才能访问到页节点这样多次的IO访问,所以 Hash 索引的查询效率要远高于 B-Tree 索引,但是Hash不能进行范围查询和排序等操作,所以只在一些固定的情况下会用该索引算法。

B树和B+树:在B树中,你可以将键和值存放在内部节点和叶子节点;但在B+树中,内部节点都是键,没有值,叶子节点同时存放键和值。B+树的叶子节点有一条链相连,而B树的叶子节点各自独立。

同时树中每个结点可以有多个孩子,意味着可以放更多的索引;(当容量大于15/16时会做一次平衡)

B树


B+树

再来看问题,为什么用B/B+树这种结构来实现索引呢?

其实不难看出红黑树等结构也可以用来实现索引,但是文件系统及数据库系统普遍使用B/B+树结构来实现索引。mysql是基于磁盘的数据库,索引是以索引文件的形式存在于磁盘中的,索引的查找过程就会涉及到磁盘IO(为什么涉及到磁盘IO请看文章后面的附加理解部分)消耗,磁盘IO的消耗相比较于内存IO的消耗要高好几个数量级,所以索引的组织结构要设计得在查找关键字时要尽量减少磁盘IO的次数。

2、MyISAM索引与InnoDB索引的区别?

InnoDB索引是聚簇索引,MyISAM索引是非聚簇索引。

InnoDB的主键索引的叶子节点存储着行数据,因此主键索引非常高效。

MyISAM索引的叶子节点存储的是行数据地址,需要再寻址一次才能得到数据。

InnoDB非主键索引的叶子节点存储的是主键和其他带索引的列数据,因此查询时做到覆盖索引会非常高效.

InnoDB支持hash索引,不支持全文索引,MyISAM则相反。

InnoDB支持事务、外键、MyISAM不支持

InnoDB可以支持行级锁、表级锁,MyISAM只支持表级锁。

3、MyISAM和InnoDB存储引擎的数据结构

这个问题不妨先看看这2种引擎的文件在磁盘上的表现形式。

使用ISAM存储引擎和InnoDB存储引擎的表在磁盘上具有不同的扩展名,其所表示的意义也不一样,ISAM存储引擎的表在磁盘上会有3个不同的扩展文件,.MYD、.MYI、.frm;以表名叫student的表为例,他们说代表的意义如下:

student.frm:代表framework,存储的是表结构

student.MYD:D是Data的简写,存储的是数据行的记录

student.MYI:I就是Index,存储的是表里面的索引字段

如果要查找的字段是索引,则会通过MYI找到MYD,根据MYI存储的文件磁盘地址,直接定位到MYD;即ISAM引擎是将索引和数据分开存在2个文件。

MyISAM


非主键索引

Innodb存储引擎的表在磁盘上只有一个文件.idb;以表名叫teacher存储引擎为InnoDB的表为例

teacher.idb:i是index的缩写,db是Data base的简写,存储的是索引和数据行的记录;可以看出innodb是将索引和数据存在一个文件中的。


InnoDB主键索引


InnoDB非主键索引

可以看到主键索引的叶子节点包含了完整的数据记录,非主键索引叶子节点存储的是主键的值,也就是说使用非主键索引查询数据会先找到该数据的主键值,然后通过主键找到对应的数据。

4、为什么InnoDB表必须要有主键,并且推荐使用整型的自增主键?

InnoDB使用聚集索引,数据记录本身被存于主索引(一颗B+Tree)的叶子节点上。这就要求同一个叶子节点内(大小为一个内存页或磁盘页)的各条数据记录按主键顺序存放,因此每当有一条新的记录插入时,MySQL会根据其主键将其插入适当的节点和位置,如果页面达到装载因子(InnoDB默认为15/16),则开辟一个新的页(节点)。如果表使用自增主键,那么每次插入新的记录,记录就会顺序添加到当前索引节点的后续位置,当一页写满,就会自动开辟一个新的页。如下图所示:


InnoDB

这样就会形成一个紧凑的索引结构,近似顺序填满。由于每次插入时也不需要移动已有数据,因此效率很高,也不会增加很多开销在维护索引上。

如果使用非自增主键(如果身份证号或学号等),由于每次插入主键的值近似于随机,因此每次新纪录都要被插到现有索引页得中间某个位置;此时MySQL不得不为了将新记录插到合适位置而移动数据,甚至目标页面可能已经被回写到磁盘上而从缓存中清掉,此时又要从磁盘上读回来,这增加了很多开销,同时频繁的移动、分页操作造成了大量的碎片,得到了不够紧凑的索引结构,后续不得不通过OPTIMIZE TABLE来重建表并优化填充页面。

因此,只要可以,请尽量在InnoDB上采用自增字段做主键。

5、主键自增带来的劣势是什么?

对于高并发工作负载,在InnoDB中按主键顺序插入可能会造成明显的争用。主键上界会成为”热点”,因为所有的插入都发生在这里,所以并发插入可能导致间隙锁竞争。另一个热点可能是AUTO_INCREMENT锁机制:如果遇到这个问题,则可能需要考虑重新设计表或者应用,或者更改innodb_autoinc_lock_mode配置。

6、为什么非主键索引结构叶子节点存储的是主键值?

InnoDB索引非主键索引存储的是主键ID,这样可以保证数据一致性和节省存储空间,可以这么理解:商城系统订单表会存储一个用户ID作为关联外键,而不推荐存储完整的用户信息,因为当我们用户表中的信息(真是名称、手机号、收货地址···)修改后,不需要再次维护订单表的用户数据,同时也节省了存储空间。(主要因为innerDB的索引和数据是在一个文件中,如果非主键索引不用主键的值,那么就意味做需要存多份数据文件)

7、为什么Mysql大多数都是用B+tree索引,而不使用Hash索引?

使用hash索引可能会出现hash冲突,且hash索引不支持范围查找;同时Hash无法被用来避免数据的排序操作,所以大多数情况下不满足业务需求,只有在确定只用到精确查询等情况下可以考虑使用Hash索引。

8、什么是最左前缀原则?什么是最左匹配原则?

最左优先,在创建多列索引时,要根据业务需求,where子句中使用最频繁的一列放在最左边。

最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。=和in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式

最左前缀法则:如果索引了多列,要遵守最左前缀原则,指的是查询从索引的最左前列开始并且不跳过索引中的列。

如下图所示,如果联合索引的列是(id,job,time)则在查询的时候要遵守最左侧开始索引,即先按照第一行10002的字段比较、然后按照第二行Staff字段比较,接着是第三行时间来比较;如果有其他条件跳过则索引效果失效。

最左前缀原理

通俗理解口诀:

   全值匹配我最爱,最左前缀要遵守;

   带头大哥不能死,中间兄弟不能断;

   索引列上少计算,范围之后全失效;

   LIKE百分写最右,覆盖索引不写星;

   不等空值还有or,索引失效要少用。

9、MySQL InnoDB一棵B+树可以存放多少行数据?

约2千万

在计算机中磁盘存储数据最小单元是扇区,一个扇区的大小是512字节,而文件系统(例如XFS/EXT4)他的最小单元是块,一个块的大小是4k,而对于我们的InnoDB存储引擎也有自己的最小储存单元——页(Page),一个页的大小是16K。在mysql中新创建的一个表初始化大小默认就是16k。innodb的所有数据文件(后缀为ibd的文件),他的大小始终都是16384(16k)的整数倍。

那么现在我们需要计算出非叶子节点能存放多少指针,其实这也很好算,我们假设主键ID为bigint类型,长度为8字节,而指针大小在InnoDB源码中设置为6字节,这样一共14字节,我们一个页中能存放多少这样的单元,其实就代表有多少指针;上面已经说了一页16k,转换成字节就是16*1024=16384;那14字节可以存放的数据量即16384/14=1170。那么可以算出一棵高度为2的B+树,能存放1170*16=18720条这样的数据记录。

根据同样的原理我们可以算出一个高度为3的B+树可以存放:1170*1170*16=21902400 条这样的记录。所以在InnoDB中B+树高度一般为1-3层,它就能满足千万级的数据存储。在查找数据时一次页的查找代表一次IO,所以通过主键索引查询通常只需要1-3次IO操作即可查找到数据。

下图创建好的表默认都是16k

10、MySql中char、varchar和Text的区别

char:存储定长数据很方便,CHAR字段上的索引效率级高,必须在括号里定义长度,可以有默认值,比如定义char(10),那么不论你存储的数据是否达到了10个字节,都要占去10个字节的空间(自动用空格填充),且在检索的时候后面的空格会隐藏掉,所以检索出来的数据需要记得用什么trim之类的函数去过滤空格。

varchar:存储变长数据,但存储效率没有CHAR高,必须在括号里定义长度,可以有默认值。保存数据的时候,不进行空格自动填充,而且如果数据存在空格时,当值保存和检索时尾部的空格仍会保留。另外,varchar类型的实际长度是它的值的实际长度+1,这一个字节用于保存实际使用了多大的长度。

text:存储可变长度的非Unicode数据,最大长度为2^31-1个字符。text列不能有默认值,存储或检索过程中,不存在大小写转换,后面如果指定长度,不会报错误,但是这个长度是不起作用的,意思就是你插入数据的时候,超过你指定的长度还是可以正常插入。

char和varchar一句话总结就是:“一个是时间换空间、一个是空间换时间”

11、百万级别或以上的数据如何删除

关于索引:由于索引需要额外的维护成本,因为索引文件是单独存在的文件,所以当我们对数据的增加,修改,删除,都会产生额外的对索引文件的操作,这些操作需要消耗额外的IO,会降低增/改/删的执行效率。所以,在我们删除数据库百万级别数据的时候,查询MySQL官方手册得知删除数据的速度和创建的索引数量是成正比的。

所以我们想要删除百万数据的时候可以先删除索引(此时大概耗时三分多钟)然后删除其中无用数据(此过程需要不到两分钟)删除完成后重新创建索引(此时数据较少了)创建索引也非常快,约十分钟左右。与之前的直接删除绝对是要快速很多,更别说万一删除中断,一切删除会回滚。那更是坑了。

12、超大分页怎么处理?

超大的分页一般从两个方向上来解决.

数据库层面,这也是我们主要集中关注的(虽然收效没那么大),类似于

select * from table where age > 20 limit 1000000,10

这种查询其实也是有可以优化的余地的. 这条语句需要load1000000数据然后基本上全部丢弃,只取10条当然比较慢. 当时我们可以修改为

select * from table where id in (select id from table where age > 20 limit 1000000,10)

.这样虽然也load了一百万的数据,但是由于索引覆盖,要查询的所有字段都在索引中,所以速度会很快. 同时如果ID连续的好,我们还可以

select * from table where id > 1000000 limit 10

效率也是不错的,优化的可能性有许多种,但是核心思想都一样,就是减少load的数据.

从需求的角度减少这种请求…主要是不做类似的需求(直接跳转到几百万页之后的具体某一页.只允许逐页查看或者按照给定的路线走,这样可预测,可缓存)以及防止ID泄漏且连续被人恶意攻击.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,277评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,689评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,624评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,356评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,402评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,292评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,135评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,992评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,429评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,636评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,785评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,492评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,092评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,723评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,858评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,891评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,713评论 2 354