MySQL索引原理剖析及优化建议

索引是什么?

  MySQL中的索引也是一种为了便于查找数据而在数据之外创建的一种数据结构,通过这种数据结构我们可以方便得根据条件查找我们想要得数据,索引的目的是加快数据的查找速度,从而提升查询的效率。索引的作用就类似于新画字典中的拼音目录,我们通过拼音可以快速的查找到指定的字。换句话说,如果给你一部没有拼音目录的字典,那么我们只能在字典中一页一页的去翻(当然,如果字典中的每个字都是按拼音排序,咱也可以通过二分查找来实现),总而言之就是查找效率会非常低下了。因此我们总结一下,索引有以下几个特点:

优点
  1.加快数据查询的速度和效率。(相当重要!!!)

缺点
  1.建立索引需要消耗额外的存储空间。因为索引也是一种按照某种数据结构组织的数据,通过这些数据我们可以快速地定位到我们想要查找的数据。
  2.建立和维护索引需要消耗额外的时间。虽然索引可以提升数据查询的效率,但是大量的索引的创建和维护的时间成本并不低。
  3.当对表中的数据进行增加、删除、修改时,除了维护原始数据,索引也需要同时进行动态的维护,降低了数据的维护速度

创建索引的建议

  上面我们对索引的优缺点进行了简单的分析,那么什么情况下需要创建索引?什么情况下又不适合创建索引呢?我们给出以下几点建议:
  1. 对于数据体量较大且查询频率远高于变更频率的列,建议创建索引。
  2. 对于数据区分度不大的列不建议创建索引。比如用户的性别,枚举范围较小的列。
  3. 对于需要频繁变更(修改和删除)的列,不建议创建索引。因为在频繁变更的列上创建索引,会导致变更操作的效率变低。
  4. 数据体量不大或者查询需求不高的的情况下,不建议创建索引。
  5. 索引需要根据查询需求来确定,并且尽可能以最小范围的方式进行创建,不建议对所有列创建索引。对于业务已经变更,需求不大的索引,需要及时drop。

MySQL存储引擎概览

  在详细介绍MySQL索引原理之前,我们需要先简单介绍一下MySQL的存储引擎。存储引擎是处理不同表类型的SQL操作的MySQL组件,目前InnoDB已经成为MySQL默认的存储引擎,也是目前使用范围最广的存储引擎,除此之外,我们可以使用SHOW ENGINES命令来查看当前版本的MySQL所支持的存储引擎,示例如下:

mysql> SHOW ENGINES
*************************** 1. row ***************************
      Engine: ARCHIVE
     Support: YES
     Comment: Archive storage engine
Transactions: NO
          XA: NO
  Savepoints: NO
*************************** 2. row ***************************
      Engine: BLACKHOLE
     Support: YES
     Comment: /dev/null storage engine (anything you write to it disappears)
Transactions: NO
          XA: NO
  Savepoints: NO
*************************** 3. row ***************************
      Engine: MRG_MYISAM
     Support: YES
     Comment: Collection of identical MyISAM tables
Transactions: NO
          XA: NO
  Savepoints: NO
*************************** 4. row ***************************
      Engine: FEDERATED
     Support: NO
     Comment: Federated MySQL storage engine
Transactions: NULL
          XA: NULL
  Savepoints: NULL
*************************** 5. row ***************************
      Engine: MyISAM
     Support: YES
     Comment: MyISAM storage engine
Transactions: NO
          XA: NO
  Savepoints: NO
*************************** 6. row ***************************
      Engine: PERFORMANCE_SCHEMA
     Support: YES
     Comment: Performance Schema
Transactions: NO
          XA: NO
  Savepoints: NO
*************************** 7. row ***************************
      Engine: InnoDB
     Support: DEFAULT
     Comment: Supports transactions, row-level locking, and foreign keys
Transactions: YES
          XA: YES
  Savepoints: YES
*************************** 8. row ***************************
      Engine: MEMORY
     Support: YES
     Comment: Hash based, stored in memory, useful for temporary tables
Transactions: NO
          XA: NO
  Savepoints: NO
*************************** 9. row ***************************
      Engine: CSV
     Support: YES
     Comment: CSV storage engine
Transactions: NO
          XA: NO
  Savepoints: NO

  由于很多存储引擎只针对特定的场景,很多人可能终其一生也不会用到,下面就从官网扒拉来一份几种常见的MySQL特性对比,如下表所示:

Feature MyISAM Memory InnoDB Archive NDB
B-tree indexes Yes Yes Yes No No
Backup/point-in-time recovery Yes Yes Yes Yes Yes
Cluster database support No No No No Yes
Clustered indexes No No Yes No No
Compressed data Yes No Yes Yes No
Data caches No N/A Yes No Yes
Encrypted data Yes Yes Yes Yes Yes
Foreign key support No No Yes No Yes
Full-text search indexes Yes No Yes No No
Geospatial data type support Yes No Yes Yes Yes
Geospatial indexing support Yes No Yes No No
Hash indexes No Yes No No Yes
Index caches Yes N/A Yes No Yes
Locking granularity Table Table Row Row Row
MVCC No No Yes No No
Replication support Yes Limited Yes Yes Yes
Storage limits 256TB RAM 64TB None 384EB
T-tree indexes No No No No Yes
Transactions No No Yes No Yes
Update statistics for data dictionary Yes Yes Yes Yes Yes

备注:
  1.在服务器中实现,而不是在存储引擎中。
  2.只有在使用压缩行格式时,才支持压缩的MyISAM表。使用MyISAM压缩行格式的表是只读  的。
  3.在服务器端通过加密功能来实现。
  4.在服务器端通过加密功能来实现;MySQL 5.7及以上版本支持静态数据加密
  5.在MySQL集群NDB 7.3及更高版本中支持使用外键。
  6.在MySQL 5.6及更高版本中支持FULLTEXT索引可用。
  7.在MySQL 5.7及更高版本中支持geospatial 索引可用。
  8.InnoDB内部利用哈希索引实现自适应哈希索引特性。

MySQL索引的分类

  MySQL中的索引,从作用范围上可以将其分成分单列索引和组合索引。单列索引即一个索引只包含单个列,一个表可以有多个单列索引。组合索引即一个索引包含多个列。这里我们不对单列索引和组合索引做过多介绍。我们主要从不同的用途出发归纳MySQL的索引分类,MySQL中的索引根据其具体的用途在逻辑上主要分成分为以下 3 类:

1.常规索引

  常规索引是 MySQL 中最基本的索引类型,它没有任何特殊限制限制,其主要目的就是提升数据的查询效率。常规索引又可以根据其约束和限制的严苛程度分为3种,分别是:普通索引、唯一索引和主键索引。

普通索引:普通索引允许在索引的列中插入重复值和空值。
唯一索引:唯一索引与普通索引的不同的是创建唯一性索引的目的不仅仅是为了提高访问速度,同时还要避免数据出现重复。唯一索引的列值必须唯一,但是允许有空值。如果是组合索引,则这些列值的组合的值必须唯一(组合索引不要某个列的值必须唯一)。
主键索引:主键索引是一种特殊的唯一索引,该索引是专门为主键字段创建的索引,不允许值重复或者为空值。

2. 全文索引

  首先全文检索并不是MySQL最擅长的任务之一,如果有巨大的全文检索需求,建议使用专门的全文检索引擎,如Solr和ES等。但是MySQL使用全文索引提供了对全文检索的支持,全文索引主要用来查找文本中的关键字,只能在 CHAR、VARCHAR 或 TEXT 类型的列上创建。全文索引允许在索引列中插入重复值和空值。

3. 空间索引

  与全文索引类似,MySQL中的空间索引也是一种针对特定类型的数据和查询需求创建的索引, 空间索引主要是对空间数据类型的字段建立的索引,如GIS中常用的坐标数据(经度、维度),创建空间索引的列必须将其声明为 NOT NULL,空间索引主要用于地理空间数据类型 GEOMETRY,对于没有这种空间数据处理任务的人来说,这类索引很少会用到。我们当初在一个时空网络的项目中使用到了空间索引,在针对GPS数据查询和坐标点距离排序这块,空间索引有着常规索引无法比拟的优势。

MySQL索引的实现原理

  上文中我们提供过,MySQL中的索引根据用途和场景不同,主要可以分成三种:常规索引、全文索引和空间索引。不同索引的实现原理不同,且在不同的存储引擎上的实现方式也不尽相同。比如常规索引的在InnoDB上可以使用B-Tree索引的方式实现,也可以使用Hash索引的方式实现;全文索引可以使用倒排索引的方式来实现;空间索引可以基于R-Tree的方式来实现。这里我们主要针对MySQL中Hash和B-Tree索引的实现原理进行详细的介绍。

Hash索引原理

  Hash索引也称为哈希索引或散列索引。MySQL 目前在 MEMORY和NDB 存储引擎中都支持支持该种类型的索引,其中,MEMORY 存储引擎将 Hash当成默认索引。Hash索引不是基于树形的数据结构查找数据,而是根据索引列对应的哈希值的方法获取记录所在的位置。Hash索引的原理很简单,大致如下图所示,主要是维护一个Hash函数,插入数据的时候对索引列的值进行Hash,得到HashCode后将该行数据存储在指定位置;查询数据的时候,也是同样的操作,先对索引列的值进行Hash,得到HashCode后去到指定的位置读取当前行的数据。Hash索引的最大优点是访问速度快,但也存在下面的一些缺点:

  1. 使用Hash索引需要在查询和建立索引的过程中进行Hash计算,Hash计算是一个比较耗时的操作,相对于 B-树索引来说,建立哈希索引会耗费更多的时间。
  2. 由于Hash的无序性,无法使用 Hash索引进行排序和范围查找。
  3. Hash索引只支持等值比较。
  4. Hash索引不支持键的部分匹配,因为Hash值是通过整个索引值计算得到的。
  5.随着数据量的增大,Hash冲突的现象会越来越明显,此时会出现明显的性能下降现象。

Hash索引
B+Tree索引原理

B+Tree索引是目前MySQL中使用最广的索引,MySQL中的B+Tree索引根据其实现方式的不同又可以分为聚集索引和非聚集索引。其中InnoDB存储引擎中的B+Tree的实现即为聚集索引,聚集索引的B+Tree中的叶子节点存放的是整张表的行记录数据。而非聚集索引与聚集索引的主要区别在于非聚集索引的叶子节点并不包含行记录的全部数据,而是存储相应行数据的主键,MySQL中的MyISAM存储引擎中的B+树的实现就是非聚集索引。这里主要针对InnoDB存储引擎中的B+Tree实现原理进行展示,不同的存储引擎中,B+树的实现方式也不一样。B+树索引是一个典型的数据结构,其包含的组件主要有以下几个:

根节点:一个 B+树索引只有一个根节点,根节点位于树的最顶端。
分支节点:包含的条目指向索引里其他的分支节点。
叶子节点:包含的条目直接指向表里的数据行。B+树中叶子节点之间通过指针彼此相连。

  基于这种树形数据结构,表中的每一行都会在索引上有一个对应值。因此,在表中进行数据查询时,可以根据索引值一步一步定位到数据所在的行。

image.png

  B+树索引可以进行全键值、键值范围和键值前缀查询,也可以对查询结果进行 ORDER BY 排序。但 B+树索引必须遵循左边前缀原则,要考虑以下几点约束:

查询必须从索引的最左边的列开始。
查询不能跳过某一索引列,必须按照从左到右的顺序进行匹配。
存储引擎不能使用索引中范围条件右边的列。

MySQL索引优化建议

  这里给出一些MySQL的索引优化建议,仅供在使用MySQL中作为参考。

1.优先选择唯一性索引,尽量选择区分度高的列作为索引,可以更快速的通过该索引来确定某条记录。
2.为经常作为查询条件的字段建立索引,为经常需要排序、分组和联合操作的字段建立索引。
3.限制索引的数目: 越多的索引,会使更新表变得很浪费时间;尽量在当前索引的基础上扩展索引,尽量不要新建索引;删除不再使用或者很少使用的索引。
4.尽量使用字段长度较短的列做索引,如果索引的值很长,那么查询的速度会受到影响。
5.如果索引的值很长,尽量使用前缀来索引,比如like只有在‘%’不在最左边的时候索引才会生效,也就是最左匹配原则。
6.最左前缀匹配原则,非常重要的原则。
7.索引列不能参与计算,保持列“干净”:带函数的查询不参与索引。

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

推荐阅读更多精彩内容