数据库索引原理及优化

摘要: 本文内容主要来源于互联网上主流文章,只是按照我的理解稍做整合,后面附有参考连接。html

本文内容主要来源于互联网上主流文章,只是按照我的理解稍做整合,后面附有参考连接。node

1、摘要

本文以MySQL数据库为研究对象,讨论与数据库索引相关的一些话题。特别须要说明的是,MySQL支持诸多存储引擎,而各类存储引擎对索引的支持也各不相同,所以MySQL数据库支持多种索引类型,如BTree索引,哈希索引,全文索引等等。为了不混乱,本文将只关注于BTree索引,由于这是日常使用MySQL时主要打交道的索引,至于哈希索引和全文索引本文暂不讨论。mysql

2、常见的查询算法及数据结构

为何这里要讲查询算法和数据结构呢?由于之因此要创建索引,其实就是为了构建一种数据结构,能够在上面应用一种高效的查询算法,最终提升数据的查询速度。正则表达式

2.1 索引的本质

MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。提取句子主干,就能够获得索引的本质:索引是数据结构。算法

2.2 常见的查询算法

咱们知道,数据库查询是数据库的最主要功能之一。咱们都但愿查询数据的速度能尽量的快,所以数据库系统的设计者会从查询算法的角度进行优化。那么有哪些查询算法可使查询速度变得更快呢?sql

2.2.1 顺序查找(linear search )

最基本的查询算法固然是顺序查找(linear search),也就是对比每一个元素的方法,不过这种算法在数据量很大时效率是极低的。 
数据结构:有序或无序队列 
复杂度:O(n) 
实例代码:数据库

//顺序查找
int SequenceSearch(int a[], int value, int n)
{
    int i;
    for(i=0; i<n; i++)
        if(a[i]==value)
            return i;
    return -1;
}

2.2.2 二分查找(binary search)

比顺序查找更快的查询方法应该就是二分查找了,二分查找的原理是查找过程从数组的中间元素开始,若是中间元素正好是要查找的元素,则搜素过程结束;若是某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,并且跟开始同样从中间元素开始比较。若是在某一步骤数组为空,则表明找不到。 
数据结构:有序数组 
复杂度:O(logn) 
实例代码:数组

//二分查找,递归版本
int BinarySearch2(int a[], int value, int low, int high)
{
    int mid = low+(high-low)/2;
    if(a[mid]==value)
        return mid;
    if(a[mid]>value)
        return BinarySearch2(a, value, low, mid-1);
    if(a[mid]<value)
        return BinarySearch2(a, value, mid+1, high);
}

2.2.3 二叉排序树查找

二叉排序树的特色是:markdown

  1. 若它的左子树不空,则左子树上全部结点的值均小于它的根结点的值;
  2. 若它的右子树不空,则右子树上全部结点的值均大于它的根结点的值;
  3. 它的左、右子树也分别为二叉排序树。

搜索的原理:数据结构

  1. 若b是空树,则搜索失败,不然:
  2. 若x等于b的根节点的数据域之值,则查找成功;不然:
  3. 若x小于b的根节点的数据域之值,则搜索左子树;不然:
  4. 查找右子树。

数据结构:二叉排序树 
时间复杂度: O(log2N)

2.2.4 哈希散列法(哈希表)

其原理是首先根据key值和哈希函数建立一个哈希表(散列表),燃耗根据键值,经过散列函数,定位数据元素位置。

数据结构:哈希表 
时间复杂度:几乎是O(1),取决于产生冲突的多少。

2.2.5 分块查找

分块查找又称索引顺序查找,它是顺序查找的一种改进方法。其算法思想是将n个数据元素”按块有序”划分为m块(m ≤ n)。每一块中的结点没必要有序,但块与块之间必须”按块有序”;即第1块中任一元素的关键字都必须小于第2块中任一元素的关键字;而第2块中任一元素又都必须小于第3块中的任一元素,依次类推。 
   
算法流程:

  1. 先选取各块中的最大关键字构成一个索引表;
  2. 查找分两个部分:先对索引表进行二分查找或顺序查找,以肯定待查记录在哪一块中;而后,在已肯定的块中用顺序法进行查找。

这种搜索算法每一次比较都使搜索范围缩小一半。它们的查询速度就有了很大的提高,复杂度为。若是稍微分析一下会发现,每种查找算法都只能应用于特定的数据结构之上,例如二分查找要求被检索数据有序,而二叉树查找只能应用于二叉查找树上,可是数据自己的组织结构不可能彻底知足各类数据结构(例如,理论上不可能同时将两列都按顺序进行组织),因此,在数据以外,数据库系统还维护着知足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就能够在这些数据结构上实现高级查找算法。这种数据结构,就是索引。

2.3 平衡多路搜索树B树(B-tree)

上面讲到了二叉树,它的搜索时间复杂度为O(log2N),因此它的搜索效率和树的深度有关,若是要提升查询速度,那么就要下降树的深度。要下降树的深度,很天然的方法就是采用多叉树,再结合平衡二叉树的思想,咱们能够构建一个平衡多叉树结构,而后就能够在上面构建平衡多路查找算法,提升大数据量下的搜索效率。

2.3.1 B Tree

B树(Balance Tree)又叫作B- 树(其实B-是由B-tree翻译过来,因此B-树和B树是一个概念) ,它就是一种平衡多路查找树。下图就是一个典型的B树: 
这里写图片描述

从上图中咱们能够大体看到B树的一些特色,为了更好的描述B树,咱们定义记录为一个二元组[key, data],key为记录的键值,data表示其它数据(上图中只有key,没有画出data数据 )。下面是对B树的一个详细定义:

1. 有一个根节点,根节点只有一个记录和两个孩子或者根节点为空;
2. 每一个节点记录中的key和指针相互间隔,指针指向孩子节点;
3. d是表示树的宽度,除叶子节点以外,其它每一个节点有[d/2,d-1]条记录,而且些记录中的key都是从左到右按大小排列的,有[d/2+1,d]个孩子;
4. 在一个节点中,第n个子树中的全部key,小于这个节点中第n个key,大于第n-1个key,好比上图中B节点的第2个子节点E中的全部key都小于B中的第2个key 9,大于第1个key 3;
5. 全部的叶子节点必须在同一层次,也就是它们具备相同的深度;

因为B-Tree的特性,在B-Tree中按key检索数据的算法很是直观:首先从根节点进行二分查找,若是找到则返回对应节点的data,不然对相应区间的指针指向的节点递归进行查找,直到找到节点或找到null指针,前者查找成功,后者查找失败。B-Tree上查找算法的伪代码以下:

BTree_Search(node, key) {
     if(node == null) return null;
     foreach(node.key){
          if(node.key[i] == key) return node.data[i];
          if(node.key[i] > key) return BTree_Search(point[i]->node);
      }
     return BTree_Search(point[i+1]->node);
  }
data = BTree_Search(root, my_key);

关于B-Tree有一系列有趣的性质,例如一个度为d的B-Tree,设其索引N个key,则其树高h的上限为logd((N+1)/2),检索一个key,其查找节点个数的渐进复杂度为O(logdN)。从这点能够看出,B-Tree是一个很是有效率的索引数据结构。

另外,因为插入删除新的数据记录会破坏B-Tree的性质,所以在插入删除时,须要对树进行一个分裂、合并、转移等操做以保持B-Tree性质,本文不打算完整讨论B-Tree这些内容,由于已经有许多资料详细说明了B-Tree的数学性质及插入删除算法,有兴趣的朋友能够查阅其它文献进行详细研究。

2.3.2 B+Tree

其实B-Tree有许多变种,其中最多见的是B+Tree,好比MySQL就广泛使用B+Tree实现其索引结构。B-Tree相比,B+Tree有如下不一样点:

  • 每一个节点的指针上限为2d而不是2d+1;
  • 内节点不存储data,只存储key;
  • 叶子节点不存储指针;

下面是一个简单的B+Tree示意。 
这里写图片描述

因为并非全部节点都具备相同的域,所以B+Tree中叶节点和内节点通常大小不一样。这点与B-Tree不一样,虽然B-Tree中不一样节点存放的key和指针可能数量不一致,可是每一个节点的域和上限是一致的,因此在实现中B-Tree每每对每一个节点申请同等大小的空间。通常来讲,B+Tree比B-Tree更适合实现外存储索引结构,具体缘由与外存储器原理及计算机存取原理有关,将在下面讨论。

2.3.3 带有顺序访问指针的B+Tree

通常在数据库系统或文件系统中使用的B+Tree结构都在经典B+Tree的基础上进行了优化,增长了顺序访问指针。 
这里写图片描述

如图所示,在B+Tree的每一个叶子节点增长一个指向相邻叶子节点的指针,就造成了带有顺序访问指针的B+Tree。作这个优化的目的是为了提升区间访问的性能,例如图4中若是要查询key为从18到49的全部数据记录,当找到18后,只需顺着节点和指针顺序遍历就能够一次性访问到全部数据节点,极大提到了区间查询效率。

这一节对B-Tree和B+Tree进行了一个简单的介绍,下一节结合存储器存取原理介绍为何目前B+Tree是数据库系统实现索引的首选数据结构。

3、索引数据结构设相关的计算机原理

上文说过,二叉树、红黑树等数据结构也能够用来实现索引,可是文件系统及数据库系统广泛采用B-/+Tree做为索引结构,这一节将结合计算机组成原理相关知识讨论B-/+Tree做为索引的理论基础。

3.1 两种类型的存储

在计算机系统中通常包含两种类型的存储,计算机主存(RAM)和外部存储器(如硬盘、CD、SSD等)。在设计索引算法和存储结构时,咱们必需要考虑到这两种类型的存储特色。主存的读取速度快,相对于主存,外部磁盘的数据读取速率要比主从慢好几个数量级,具体它们之间的差异后面会详细介绍。 上面讲的全部查询算法都是假设数据存储在计算机主存中的,计算机主存通常比较小,实际数据库中数据都是存储到外部存储器的。

通常来讲,索引自己也很大,不可能所有存储在内存中,所以索引每每以索引文件的形式存储的磁盘上。这样的话,索引查找过程当中就要产生磁盘I/O消耗,相对于内存存取,I/O存取的消耗要高几个数量级,因此评价一个数据结构做为索引的优劣最重要的指标就是在查找过程当中磁盘I/O操做次数的渐进复杂度。换句话说,索引的结构组织要尽可能减小查找过程当中磁盘I/O的存取次数。下面详细介绍内存和磁盘存取原理,而后再结合这些原理分析B-/+Tree做为索引的效率。

3.2 主存存取原理

目前计算机使用的主存基本都是随机读写存储器(RAM),现代RAM的结构和存取原理比较复杂,这里本文抛却具体差异,抽象出一个十分简单的存取模型来讲明RAM的工做原理。 
这里写图片描述

从抽象角度看,主存是一系列的存储单元组成的矩阵,每一个存储单元存储固定大小的数据。每一个存储单元有惟一的地址,现代主存的编址规则比较复杂,这里将其简化成一个二维地址:经过一个行地址和一个列地址能够惟必定位到一个存储单元。上图展现了一个4 x 4的主存模型。

主存的存取过程以下:

当系统须要读取主存时,则将地址信号放到地址总线上传给主存,主存读到地址信号后,解析信号并定位到指定存储单元,而后将此存储单元数据放到数据总线上,供其它部件读取。写主存的过程相似,系统将要写入单元地址和数据分别放在地址总线和数据总线上,主存读取两个总线的内容,作相应的写操做。

这里能够看出,主存存取的时间仅与存取次数呈线性关系,由于不存在机械操做,两次存取的数据的“距离”不会对时间有任何影响,例如,先取A0再取A1和先取A0再取D3的时间消耗是同样的。

3.3 磁盘存取原理

上文说过,索引通常以文件形式存储在磁盘上,索引检索须要磁盘I/O操做。与主存不一样,磁盘I/O存在机械运动耗费,所以磁盘I/O的时间消耗是巨大的。

磁盘读取数据靠的是机械运动,当须要从磁盘读取数据时,系统会将数据逻辑地址传给磁盘,磁盘的控制电路按照寻址逻辑将逻辑地址翻译成物理地址,即肯定要读的数据在哪一个磁道,哪一个扇区。为了读取这个扇区的数据,须要将磁头放到这个扇区上方,为了实现这一点,磁头须要移动对准相应磁道,这个过程叫作寻道,所耗费时间叫作寻道时间,而后磁盘旋转将目标扇区旋转到磁头下,这个过程耗费的时间叫作旋转时间,最后即是对读取数据的传输。 因此每次读取数据花费的时间能够分为寻道时间、旋转延迟、传输时间三个部分。其中:

  • 寻道时间是磁臂移动到指定磁道所须要的时间,主流磁盘通常在5ms如下。
  • 旋转延迟就是咱们常常据说的磁盘转速,好比一个磁盘7200转,表示每分钟能转7200次,也就是说1秒钟能转120次,旋转延迟就是1/120/2 = 4.17ms。
  • 传输时间指的是从磁盘读出或将数据写入磁盘的时间,通常在零点几毫秒,相对于前两个时间能够忽略不计。

那么访问一次磁盘的时间,即一次磁盘IO的时间约等于5+4.17 = 9ms左右,听起来还挺不错的,但要知道一台500 -MIPS的机器每秒能够执行5亿条指令,由于指令依靠的是电的性质,换句话说执行一次IO的时间能够执行40万条指令,数据库动辄十万百万乃至千万级数据,每次9毫秒的时间,显然是个灾难。

3.4 局部性原理与磁盘预读

因为存储介质的特性,磁盘自己存取就比主存慢不少,再加上机械运动耗费,磁盘的存取速度每每是主存的几百分分之一,所以为了提升效率,要尽可能减小磁盘I/O。为了达到这个目的,磁盘每每不是严格按需读取,而是每次都会预读,即便只须要一个字节,磁盘也会从这个位置开始,顺序向后读取必定长度的数据放入内存。这样作的理论依据是计算机科学中著名的局部性原理:当一个数据被用到时,其附近的数据也一般会立刻被使用。程序运行期间所须要的数据一般比较集中。

因为磁盘顺序读取的效率很高(不须要寻道时间,只需不多的旋转时间),所以对于具备局部性的程序来讲,预读能够提升I/O效率。预读的长度通常为页(page)的整倍数。页是计算机管理存储器的逻辑块,硬件及操做系统每每将主存和磁盘存储区分割为连续的大小相等的块,每一个存储块称为一页(在许多操做系统中,页得大小一般为4k),主存和磁盘以页为单位交换数据。当程序要读取的数据不在主存中时,会触发一个缺页异常,此时系统会向磁盘发出读盘信号,磁盘会找到数据的起始位置并向后连续读取一页或几页载入内存中,而后异常返回,程序继续运行。

4、数据库索引所采用的数据结构B-/+Tree及其性能分析

到这里终于能够分析为什么数据库索引采用B-/+Tree存储结构了。上文说过数据库索引是存储到磁盘的而咱们又通常以使用磁盘I/O次数来评价索引结构的优劣。先从B-Tree分析,根据B-Tree的定义,可知检索一次最多须要访问h-1个节点(根节点常驻内存)。数据库系统的设计者巧妙利用了磁盘预读原理,将一个节点的大小设为等于一个页,这样每一个节点只须要一次I/O就能够彻底载入。为了达到这个目的,在实际实现B-Tree还须要使用以下技巧:每次新建节点时,直接申请一个页的空间,这样就保证一个节点物理上也存储在一个页里,加之计算机存储分配都是按页对齐的,就实现了一个node只需一次I/O。

B-Tree中一次检索最多须要h-1次I/O(根节点常驻内存),渐进复杂度为O(h)=O(logdN)。通常实际应用中,出度d是很是大的数字,一般超过100,所以h很是小(一般不超过3)。

综上所述,若是咱们采用B-Tree存储结构,搜索时I/O次数通常不会超过3次,因此用B-Tree做为索引结构效率是很是高的。

4.1 B+树性能分析

从上面介绍咱们知道,B树的搜索复杂度为O(h)=O(logdN),因此树的出度d越大,深度h就越小,I/O的次数就越少。B+Tree偏偏能够增长出度d的宽度,由于每一个节点大小为一个页大小,因此出度的上限取决于节点内key和data的大小:

dmax=floor(pagesize/(keysize+datasize+pointsize))//floor表示向下取整

因为B+Tree内节点去掉了data域,所以能够拥有更大的出度,从而拥有更好的性能。

4.2 B+树查找过程

这里写图片描述
B-树和B+树查找过程基本一致。如上图所示,若是要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中用二分查找肯定29在17和35之间,锁定磁盘块1的P2指针,内存时间由于很是短(相比磁盘的IO)能够忽略不计,经过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO,29在26和30之间,锁定磁盘块3的P2指针,经过指针加载磁盘块8到内存,发生第三次IO,同时内存中作二分查找找到29,结束查询,总计三次IO。真实的状况是,3层的b+树能够表示上百万的数据,若是上百万的数据查找只须要三次IO,性能提升将是巨大的,若是没有索引,每一个数据项都要发生一次IO,那么总共须要百万次的IO,显然成本很是很是高。

这一章从理论角度讨论了与索引相关的数据结构与算法问题,下一章将讨论B+Tree是如何具体实现为MySQL中索引,同时将结合MyISAM和InnDB存储引擎介绍非汇集索引和汇集索引两种不一样的索引实现形式。

5、MySQL索引实现

在MySQL中,索引属于存储引擎级别的概念,不一样存储引擎对索引的实现方式是不一样的,本文主要讨论MyISAM和InnoDB两个存储引擎的索引实现方式。

5.1 MyISAM索引实现

MyISAM引擎使用B+Tree做为索引结构,叶节点的data域存放的是数据记录的地址。下图是MyISAM索引的原理图: 
这里写图片描述

这里设表一共有三列,假设咱们以Col1为主键,则上图是一个MyISAM表的主索引(Primary key)示意。能够看出MyISAM的索引文件仅仅保存数据记录的地址。在MyISAM中,主索引和辅助索引(Secondary key)在结构上没有任何区别,只是主索引要求key是惟一的,而辅助索引的key能够重复。若是咱们在Col2上创建一个辅助索引,则此索引的结构以下图所示: 
这里写图片描述

一样也是一颗B+Tree,data域保存数据记录的地址。所以,MyISAM中索引检索的算法为首先按照B+Tree搜索算法搜索索引,若是指定的Key存在,则取出其data域的值,而后以data域的值为地址,读取相应数据记录。 
MyISAM的索引方式也叫作“非汇集”的,之因此这么称呼是为了与InnoDB的汇集索引区分。

5.2 InnoDB索引实现

虽然InnoDB也使用B+Tree做为索引结构,但具体实现方式却与MyISAM大相径庭。

第一个重大区别是InnoDB的数据文件自己就是索引文件。从上文知道,MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。而在InnoDB中,表数据文件自己就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,所以InnoDB表数据文件自己就是主索引。 
这里写图片描述

上图是InnoDB主索引(同时也是数据文件)的示意图,能够看到叶节点包含了完整的数据记录。这种索引叫作汇集索引。由于InnoDB的数据文件自己要按主键汇集,因此InnoDB要求表必须有主键(MyISAM能够没有),若是没有显式指定,则MySQL系统会自动选择一个能够惟一标识数据记录的列做为主键,若是不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段做为主键,这个字段长度为6个字节,类型为长整形。

第二个与MyISAM索引的不一样是InnoDB的辅助索引data域存储相应记录主键的值而不是地址。换句话说,InnoDB的全部辅助索引都引用主键做为data域。例如,下图为定义在Col3上的一个辅助索引: 
这里写图片描述

这里以英文字符的ASCII码做为比较准则。汇集索引这种实现方式使得按主键的搜索十分高效,可是辅助索引搜索须要检索两遍索引:首先检索辅助索引得到主键,而后用主键到主索引中检索得到记录。

了解不一样存储引擎的索引实现方式对于正确使用和优化索引都很是有帮助,例如知道了InnoDB的索引实现后,就很容易明白为何不建议使用过长的字段做为主键,由于全部辅助索引都引用主索引,过长的主索引会令辅助索引变得过大。再例如,用非单调的字段做为主键在InnoDB中不是个好主意,由于InnoDB数据文件自己是一颗B+Tree,非单调的主键会形成在插入新记录时数据文件为了维持B+Tree的特性而频繁的分裂调整,十分低效,而使用自增字段做为主键则是一个很好的选择。

下一章将具体讨论这些与索引有关的优化策略。

6、索引使用策略及优化

MySQL的优化主要分为结构优化(Scheme optimization)和查询优化(Query optimization)。本章讨论的高性能索引策略主要属于结构优化范畴。本章的内容彻底基于上文的理论基础,实际上一旦理解了索引背后的机制,那么选择高性能的策略就变成了纯粹的推理,而且能够理解这些策略背后的逻辑。

6.1 联合索引及最左前缀原理

联合索引(复合索引)

首先介绍一下联合索引。联合索引其实很简单,相对于通常索引只有一个字段,联合索引能够为多个字段建立一个索引。它的原理也很简单,好比,咱们在(a,b,c)字段上建立一个联合索引,则索引记录会首先按照A字段排序,而后再按照B字段排序而后再是C字段,所以,联合索引的特色就是:

  • 第一个字段必定是有序的
  • 当第一个字段值相等的时候,第二个字段又是有序的,好比下表中当A=2时全部B的值是有序排列的,依次类推,当同一个B值得全部C字段是有序排列的

    | A | B | C | 
    | 1 | 2 | 3 | 
    | 1 | 4 | 2 | 
    | 1 | 1 | 4 | 
    | 2 | 3 | 5 | 
    | 2 | 4 | 4 | 
    | 2 | 4 | 6 | 
    | 2 | 5 | 5 |

其实联合索引的查找就跟查字典是同样的,先根据第一个字母查,而后再根据第二个字母查,或者只根据第一个字母查,可是不能跳过第一个字母从第二个字母开始查。这就是所谓的最左前缀原理。

最左前缀原理

咱们再来详细介绍一下联合索引的查询。仍是上面例子,咱们在(a,b,c)字段上建了一个联合索引,因此这个索引是先按a 再按b 再按c进行排列的,因此:

如下的查询方式均可以用到索引

select * from table where a=1;
select * from table where a=1 and b=2;
select * from table where a=1 and b=2 and c=3;

上面三个查询按照 (a ), (a,b ),(a,b,c )的顺序均可以利用到索引,这就是最左前缀匹配。

若是查询语句是:

select * from table where a=1 and c=3; 那么只会用到索引a。

若是查询语句是:

select * from table where b=2 and c=3; 由于没有用到最左前缀a,因此这个查询是用户到索引的。

若是用到了最左前缀,可是顺序颠倒会用到索引码?

好比:

select * from table where b=2 and a=1;
select * from table where b=2 and a=1 and c=3;

若是用到了最左前缀而只是颠倒了顺序,也是能够用到索引的,由于mysql查询优化器会判断纠正这条sql语句该以什么样的顺序执行效率最高,最后才生成真正的执行计划。但咱们仍是最好按照索引顺序来查询,这样查询优化器就不用从新编译了。

前缀索引

除了联合索引以外,对mysql来讲其实还有一种前缀索引。前缀索引就是用列的前缀代替整个列做为索引key,当前缀长度合适时,能够作到既使得前缀索引的选择性接近全列索引,同时由于索引key变短而减小了索引文件的大小和维护开销。

通常来讲如下状况可使用前缀索引:

  • 字符串列(varchar,char,text等),须要进行全字段匹配或者前匹配。也就是=‘xxx’ 或者 like ‘xxx%’
  • 字符串自己可能比较长,并且前几个字符就开始不相同。好比咱们对中国人的姓名使用前缀索引就没啥意义,由于中国人名字都很短,另外对收件地址使用前缀索引也不是很实用,由于一方面收件地址通常都是以XX省开头,也就是说前几个字符都是差很少的,并且收件地址进行检索通常都是like ’%xxx%’,不会用到前匹配。相反对外国人的姓名可使用前缀索引,由于其字符较长,并且前几个字符的选择性比较高。一样电子邮件也是一个可使用前缀索引的字段。
  • 前一半字符的索引选择性就已经接近于全字段的索引选择性。若是整个字段的长度为20,索引选择性为0.9,而咱们对前10个字符创建前缀索引其选择性也只有0.5,那么咱们须要继续加大前缀字符的长度,可是这个时候前缀索引的优点已经不明显,没有太大的建前缀索引的必要了。

一些文章中也提到:

MySQL 前缀索引能有效减少索引文件的大小,提升索引的速度。可是前缀索引也有它的坏处:MySQL 不能在 ORDER BY 或 GROUP BY 中使用前缀索引,也不能把它们用做覆盖索引(Covering Index)。

6.2 索引优化策略

  • 最左前缀匹配原则,上面讲到了
  • 主键外检必定要建索引
  • 对 where,on,group by,order by 中出现的列使用索引
  • 尽可能选择区分度高的列做为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大咱们扫描的记录数越少,惟一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0
  • 对较小的数据列使用索引,这样会使索引文件更小,同时内存中也能够装载更多的索引键
  • 索引列不能参与计算,保持列“干净”,好比from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,缘由很简单,b+树中存的都是数据表中的字段值,但进行检索时,须要把全部元素都应用函数才能比较,显然成本太大。因此语句应该写成create_time = unix_timestamp(’2014-05-29’);
  • 为较长的字符串使用前缀索引
  • 尽可能的扩展索引,不要新建索引。好比表中已经有a的索引,如今要加(a,b)的索引,那么只须要修改原来的索引便可
  • 不要过多建立索引, 权衡索引个数与DML之间关系,DML也就是插入、删除数据操做。这里须要权衡一个问题,创建索引的目的是为了提升查询效率的,但创建的索引过多,会影响插入、删除数据的速度,由于咱们修改的表数据,索引也须要进行调整重建
  • 对于like查询,”%”不要放在前面。 
    SELECT * FROMhoudunwangWHEREunameLIKE'后盾%' -- 走索引 
    SELECT * FROMhoudunwangWHEREunameLIKE "%后盾%" -- 不走索引
  • 查询where条件数据类型不匹配也没法使用索引 
    字符串与数字比较不使用索引; 
    CREATE TABLEa(achar(10)); 
    EXPLAIN SELECT * FROMaWHEREa="1" – 走索引 
    EXPLAIN SELECT * FROM a WHERE a=1 – 不走索引 
    正则表达式不使用索引,这应该很好理解,因此为何在SQL中很难看到regexp关键字的缘由

参考文章:

http://blog.csdn.net/suifeng3051/article/details/49530299?locationNum=1 
http://tech.meituan.com/mysql-index.html 
https://yq.aliyun.com/articles/39841 
http://blog.csdn.net/lovelion/article/details/8462814

 转载:https://yq.aliyun.com/articles/65126

 

建索引原则:

1.最左前缀匹配原则,很是重要的原则,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的顺序能够任意调整。2.=和in能够乱序,好比a = 1 and b = 2 and c = 3 创建(a,b,c)索引能够任意顺序,mysql的查询优化器会帮你优化成索引能够识别的形式3.尽可能选择区分度高的列做为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大咱们扫描的记录数越少,惟一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0,那可能有人会问,这个比例有什么经验值吗?使用场景不一样,这个值也很难肯定,通常须要join的字段咱们都要求是0.1以上,即平均1条扫描10条记录4.索引列不能参与计算,保持列“干净”,好比from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,缘由很简单,b+树中存的都是数据表中的字段值,但进行检索时,须要把全部元素都应用函数才能比较,显然成本太大。因此语句应该写成create_time = unix_timestamp(’2014-05-29’);5.尽可能的扩展索引,不要新建索引。好比表中已经有a的索引,如今要加(a,b)的索引,那么只须要修改原来的索引便可

相关文章
相关标签/搜索