高效利用mysql索引指南

前言

mysql 相信大部分人都用过,索引肯定也是用过的,但是你知道如何创建恰当的索引吗?在数据量小的时候,不合适的索引对性能并不会有太大的影响,但是当数据逐渐增大时,性能便会急剧的下降。

本篇是对 mysql 索引的一个归纳总结,如果有错误的地方,记得评论指出哦。

索引基础

我们都有都知道查字典的步骤,是先在索引页中找到这个字的页码,然后再到对应的页码中查看这个字的信息。mysql 的索引方法也是和这个类似的,先在索引中找到对应值,然后根据匹配的索引记录找到对应的数据行。假如有下面的 sql 语句:

select * from student where code='2333'

加入 code 列上建立有索引,mysql 将使用该索引找到值为'2333'的数据行,然后读取数据行的所有数据返回。

索引类型

B-Tree 索引

(不是 B 减树,就是 B 树),绝大多数的索引类型都是 B-Tree 的(或者是 B-Tree 的变体),通常我们使用的也是这类索引。Mysql 中 MyISAM 存储引擎使用的是 B-tree,InnoDB 使用的是 B+Tree,B 树和 B+树的区别自行百度。

树结构的索引能够加快访问数据的速度,存储引擎不再需要全表扫描来获取所需的数据,取而代之的是从树的根节点来进行二分搜索,总所周知二分搜索的速度是相当快的,因此我们能够利用索引来极大的提高查询速度。B-Tree 支持以下几种类型的查询:

假设再 student 表中仅有:name,age,weight 这样一个多列索引,下面的查询都能利用到此索引

  • 全值匹配

和索引列中的所有列进行匹配。比如查询name='abc' and age=12,这里用到了第一列和第二列

  • 匹配最左前列

只是用索引的开头部分,比如查询name='ggg'只使用索引的第一列,查询name='ggg' and age=12是用索引的第一、二列。

  • 匹配列前缀

也可以只匹配某一列的开头部分,比如查询name lik 'g%',查询 name 以 g 开头的记录。这里用到了第一列

  • 匹配范围值

可用于匹配范围值,比如查询name > 'abc' and name < 'bcd'

  • 精确匹配某一列并范围匹配另外一列

用于匹配多列,比如查询name='abc' and age > 12。

总的来看,可以发现 B-Tree 索引适用于根据最左前缀的查找,也就是查询字段字段顺序要和索引字段顺序一样,且以第一个索引字段开头。比如查询name,name and age,name and age and weight都能使用索引,但是查询age,age and name不能使用索引。

哈希索引

hash 索引基于 hash 表实现,只有精确匹配索引所有列才会生效。MySQL 中只有 Memory 引擎显示支持哈希索引,同时也是其默认索引。

InnoDB 无法创建 hash 索引,但是它有一个功能叫自适应hash索引,当某些索引值使用非常频繁时,引擎会在内存中基于 B-Tree 索引之上再创建一个 hash 索引,这样就让 B-Tree 索引也有了一点 hash 索引的优点。这个功能是一个完全自动的、内部的行为,也就是无法手动控制或配置。

高性能索引策略

下面是一些常见的索引策略。

独立的列

这个很简单,如果查询中的列不是独立,便无法使用索引,比如:

select * from student where age+1=12

即使 age 列有索引,上面的查询语句也是无法利用索引的。

前缀索引和索引选择性

如果需要索引很长的字符串列,直接创建索引,会让索引占用更多的空间且速度较慢。一个优化策略是模拟 hash 索引:给列计算一个 hash 值,并在 hash 值列建立索引。

另外一个办法就是建立前缀索引。只索引这个字段开始的部分字符,这样可以极大的解决空间占用,索引建立速度也会快很多。但是这样也有如下弊端:

  • 降低了索引选择性,如果多个字符串前缀相同便无法区分,还需要进行字符串对比。
  • 不支持order by,group by,原因显而易见,只索引了部分字符,无法完全区分。

这里的关键是确定索引多少个字符合适。既要避免长度过大,还要有足够的索引选择性。有以下两种办法来帮助确定索引字符数:

索引字段前缀数据分布均匀。也就是以索引字符开头的字符串数目分布均匀,比如索引 name 字段的前 3 个字符,下面的结果是比较合理(只取排名前 8 的):

数目 索引前三个字符
500 abc
465 asd
455 acd
431 zaf
430 aaa
420 vvv
411 asv
512 pdf

如果每一列的数据都比较大,说明区分度还不高需要增大索引字符数,直到这个前缀的选择性接近完整列的索引性,也就是前面的数据要尽可能的小。

计算完整列的选择性,并使前缀的选择性接近于完整列的选择性。下面语句用户计算完整列选择性:

-- 不同字符串的数目/总的数目就是完整列选择性
select count(distinct name)/count(*) from person;

下面语句计算索引前 3 个字段选择性:

-- 前3个字符不同的字符串数据/总的数据
select count(distincy left(city,3))/count(*) from person

不断增大索引字符数目,直到选择性接近完整列选择性且继续增大数据选择性提升幅度不大的时候。

创建方法

-- 假设最佳长度为4
alter table person add key (name(4));

多列索引

不少人有这样的误解,如果一个查询用有多个字段 ‘and'查询,那么给每个字段都建立索引不就能最大化提高效率了?事实并不是如此,mysql 只会选择其中一个字段来进行索引查找。这种情况下应该建立多列索引(又叫联合索引),就能利用多个索引字段了,注意索引列顺序要和查询的顺序一致。

在 5.0 及以上版本中引入了“索引合并”的策略。一定程度上也可以使用多个单列索引,比如下面的查询:

-- mysql会分别使用name和age索引查出数据然后合并
-- 如果使and则查出数据后再对比取交集
select * from person where name = "bob" or age=12

但是不推荐这么做,and 或 or条件过多会耗费大量的 CPU 和内存在算法的缓存、排序和合并操作上。

选择合适的索引列顺序

在一个多列 B-Tree 索引中,索引列的顺序意味着索引首先是按照最左列进行排序,然后是第二列…索引一个良好的多列索引应该是将选择性最高的索引放在最前面,然后依次降低,这样才能更好的利于索引。选择性计算方发见:前缀索引 小节。

聚族索引

聚族索引不是一种单独的索引类型,而是一种数据存储方法,具体的细节依赖其实现方式。

InnoDB 的聚族索引实际是在同一个结构中保存索引值和数据行。因为不能同时将数据行放在两个不同的地方,所以一个表只能有一个聚族索引。InnoDB 的聚族索引列为“主键列”。

如果没有定义主键,InnoDB 会选择一个唯一的非空索引代替。如果这样的索引也没有,InnoDB 会隐式定义一个主键来作为聚族索引。

聚族索引的主要优点是:可以把相关数据保存在一起,减少磁盘 IO,提高查询效率。但是也有缺点:

  • 插入顺序严重依赖于插入顺序。按照主键的顺序插入是速度最快的方式,否则可能会导致页分裂的问题出现,会占用更多的磁盘空间,扫描速度也会变慢。可通过OPTIMIZE TABLE重新组织表。
  • 更新聚族索引列代价很高,因为索引值变了,行数据也会跟着索引移动到新的位置上。
  • 二级索引(非聚族索引)访问行数据需要两次索引查找,因为二级索引叶子节点存储的并不是行数据的物理位置,而是行的主键值,再通过主键值到聚族索引中取行数据。

覆盖索引

简单来说就是一个索引覆盖了需要查询的列字段,这样就不需要再到聚族索引中利用主键进行二次查找,在一个二级索引中就能取到所需的数据。

InnoDB 的索引会在叶子节点中保存索引值,因此如果要查询的字段全部包含在某个索引中,且这个索引被使用了,那么就能极大的提高查询速度。比如如下查询语句:

-- name有索引的情况下,直接从索引的叶子节点中取name值返回,无需二次查找
select name from person where name = 'abc'
-- 如果存在`name,age`聚合索引,也会直接返回数据,无需二次查找
select name,age from person where name='abc' and age=12

使用索引进行排序

mysql 的排序操作也是可以利用索引的,只有当索引的列顺序和ORDER BY的顺序完全一致,并且所有列的排序方法(正序或者倒序)也一样时,才能够使用索引来进行排序。注意:排序的字段可以比对应的索引字段少,但是顺序必须一致。如下:

-- 假设有:(name,age,sex)联合索引
-- 可使用索引排序
select ... order by name desc,age desc
select ... order by name desc,age desc,sex desc
-- 不可使用排序
select ... order by name desc,sex desc
select ... order by name desc,age asc

结束

本篇基于 mysql 5.5 的版本,更新的版本可能会有不一样的策略。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 由不同的索引更新解决MySQL死锁套路

    前几篇文章介绍了用源码的方式来调试锁相关的信息,这里同样用这个工具来解决一个线上实际的死锁案例,也是我们介绍的第一个两条 SQL 就造成死锁的情况.因为线上的表结构比较复杂,做了一些简化以后如下 CREATE TABLE `t3` ( `id` int(11) NOT NULL AUTO_INCREMENT, `a` varchar(5), `b` varchar(5), PRIMARY KEY (`id`), UNIQUE KEY `uk_a` (`a`), KEY `idx_b` (`b`)

  • Mysql中的索引精讲

    前言 开门见山,直接上图,下面的思维导图即是现在要讲的内容,可以先有个印象- 常见索引类型(实现层面) 索引种类(应用层面) 聚簇索引与非聚簇索引 覆盖索引 最佳索引使用策略 1.常见索引类型(实现层面) 首先不谈Mysql怎么实现索引的,先马后炮一下,如果让我们来设计数据库的索引,该怎么设计? 我们首先思考一下索引到底想达到什么效果?其实就是想能够实现快速查找数据的策略,所以索引的实现本质上就是一个查找算法. 但是跟普通的查找有所不同,因为我们的数据有一下特征: 1.存储的数据是非常非常多的

  • MySQL学习(七):Innodb存储引擎索引的实现原理详解

    概述 在数据库当中,索引就跟树的目录一样用来加快数据的查找速度,对于一个SQL查询操作,根据索引快速过滤掉不符合要求的数据并定位到符合要求的数据,从而不需要扫描整个表来获取所需的数据. 在innodb存储引擎中,主要是基于B+树来实现索引,在非叶子节点存放索引关键字,在叶子节点存放数据记录或者主键索引(或者说是聚簇索引)中的主键值,所有的数据记录都在同一层,叶子节点,即数据记录直接之间通过指针相连,构成一个双向链表,从而可以方便地遍历到所有的或者某一范围的数据记录. B树,B+树 B树和B+树都

  • 使用shell脚本来给mysql加索引的方法

    用shell脚本来给mysql加索引 刚好用到, mark一下: #! /bin/bash tb_base=tb_student_ arr=("0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "a" "b" "c" &q

  • 新手学习MySQL索引

    前言 由于MySQL的索引中最重要的数据结构就是B+树,所以前面我们先大概讲讲B+树的原理 B+ Tree 原理 1. 数据结构 B Tree 指的是 Balance Tree,也就是平衡树.平衡树是一颗查找树,并且所有叶子节点位于同一层. B+ Tree 是基于 B Tree 和叶子节点顺序访问指针进行实现,它具有 B Tree 的平衡性,并且通过顺序访问指针来提高区间查询的性能. 在 B+ Tree 中,一个节点中的 key 从左到右非递减排列,如果某个指针的左右相邻 key 分别是 key

  • MySQL 死锁套路:唯一索引 S 锁与 X 锁的爱恨情仇

    在初学者从源码理解MySQL死锁问题中介绍了使用调试 MySQL  源码的方式来查看死锁的过程,这篇文章来讲讲一个常见的案例. 毫不夸张的说,有一半以上的死锁问题由唯一索引贡献,后面介绍的很多死锁的问题都跟唯一索引有关.这次我们讲一段唯一索引 S 锁与 X 锁的爱恨情仇 我们来看一个简化过的例子 # 构造数据 CREATE TABLE `t1` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(10), `level` int(11),

  • 简单谈谈Mysql索引与redis跳表

    摘要 面试时,交流有关mysql索引问题时,发现有些人能够涛涛不绝的说出B+树和B树,平衡二叉树的区别,却说不出B+树和hash索引的区别.这种一看就知道是死记硬背,没有理解索引的本质.本文旨在剖析这背后的原理,欢迎留言探讨 问题 如果对以下问题感到困惑或一知半解,请继续看下去,相信本文一定会对你有帮助 mysql 索引如何实现 mysql 索引结构B+树与hash有何区别.分别适用于什么场景 数据库的索引还能有其他实现吗 redis跳表是如何实现的 跳表和B+树,LSM树有和区别呢 解析 首先

  • 分享几道关于MySQL索引的重点面试题

    前言 索引是对数据库中一或多个列值的排序,帮助数据库高效获取数据的数据结构 假如我们用类比的方法,数据库中的索引就相当于书籍中的目录一样,当我们想找到书中的摸个知识点,我们可以直接去目录中找而不是在书中每页的找,但是这也抛出了索引的一个缺点,在对数据库修改的时候要修改索引到导致时间变多. 但MySQL 索引你真的懂吗?这几道题带你了解索引的几个重要知识点 1. 什么是最左前缀原则? 以下回答全部是基于MySQL的InnoDB引擎 例如对于下面这一张表 如果我们按照 name 字段来建立索引的话,

  • MySQL批量插入和唯一索引问题的解决方法

    MySQL批量插入问题 在开发项目时,因为有一些旧系统的基础数据需要提前导入,所以我在导入时做了批量导入操作 ,但是因为MySQL中的一次可接受的SQL语句大小受限制所以我每次批量虽然只有500条,但依然无法插入,这个时候代码报错如下: nested exception is com.mysql.jdbc.PacketTooBigException: Packet for query is too large (5677854 > 1048576). You can change this va

  • 高效利用mysql索引指南

    前言 mysql 相信大部分人都用过,索引肯定也是用过的,但是你知道如何创建恰当的索引吗?在数据量小的时候,不合适的索引对性能并不会有太大的影响,但是当数据逐渐增大时,性能便会急剧的下降. 本篇是对 mysql 索引的一个归纳总结,如果有错误的地方,记得评论指出哦. 索引基础 我们都有都知道查字典的步骤,是先在索引页中找到这个字的页码,然后再到对应的页码中查看这个字的信息.mysql 的索引方法也是和这个类似的,先在索引中找到对应值,然后根据匹配的索引记录找到对应的数据行.假如有下面的 sql

  • MySQL索引背后的数据结构及算法原理详解

    摘要 本文以MySQL数据库为研究对象,讨论与数据库索引相关的一些话题.特别需要说明的是,MySQL支持诸多存储引擎,而各种存储引擎对索引的支持也各不相同,因此MySQL数据库支持多种索引类型,如BTree索引,哈希索引,全文索引等等.为了避免混乱,本文将只关注于BTree索引,因为这是平常使用MySQL时主要打交道的索引,至于哈希索引和全文索引本文暂不讨论. 文章主要内容分为三个部分. 第一部分主要从数据结构及算法理论层面讨论MySQL数据库索引的数理基础. 第二部分结合MySQL数据库中My

  • 如何利用MySQL添加联合唯一索引

    目录 联合唯一索引 扩展延伸: 附:mysql中如何用命令创建联合索引 总结 联合唯一索引 项目中需要用到联合唯一索引: 例如:有以下需求:每个人每一天只有可能产生一条记录:处了程序约定之外,数据库本身也可以设定: 例如:t_aa 表中有aa,bb两个字段,如果不希望有2条一模一样的记录(即:aa字段的值可以重复: bb字段的值也可以重复,但是一条记录(aa,bb)组合值不允许重复),需要给 t_aa 表添加多个字段的联合唯一索引: alter table t_aa add unique ind

  • mysql利用覆盖索引避免回表优化查询

    前言 说到覆盖索引之前,先要了解它的数据结构:B+树. 先建个表演示(为了简单,id按顺序建): id name 1 aa 3 kl 5 op 8 aa 10 kk 11 kl 14 jk 16 ml 17 mn 18 kl 19 kl 22 hj 24 io 25 vg 29 jk 31 jk 33 rt 34 ty 35 yu 37 rt 39 rt 41 ty 45 qt 47 ty 53 qi 57 gh 61 dh 以主键以外的列值作为键值构建的 B+ 树索引,我们称之为非聚集索引.

  • mysql 索引分类以及用途分析

    一. MySQL: 索引以B树格式保存 Memory存储引擎可以选择Hash或BTree索引,Hash索引只能用于=或<=>的等式比较. 1.普通索引:create index on Tablename(列的列表) alter table TableName add index (列的列表) create table TableName([...], index [IndexName] (列的列表) 2.唯一性索引:create unique index alter ... add uniqu

  • 快速学习MySQL索引的入门超级教程

    所谓索引就是为特定的mysql字段进行一些特定的算法排序,比如二叉树的算法和哈希算法,哈希算法是通过建立特征值,然后根据特征值来快速查找.而用的最多,并且是mysql默认的就是二叉树算法 BTREE,通过BTREE算法建立索引的字段,比如扫描20行就能得到未使用BTREE前扫描了2^20行的结果,具体的实现方式后续本博客会出一个算法专题里面会有具体的分析讨论; Explain优化查询检测 EXPLAIN可以帮助开发人员分析SQL问题,explain显示了mysql如何使用索引来处理select语

  • 理解MySQL——索引与优化总结

    写在前面:索引对查询的速度有着至关重要的影响,理解索引也是进行数据库性能调优的起点.考虑如下情况,假设数据库中一个表有10^6条记录,DBMS的页面大小为4K,并存储100条记录.如果没有索引,查询将对整个表进行扫描,最坏的情况下,如果所有数据页都不在内存,需要读取10^4个页面,如果这10^4个页面在磁盘上随机分布,需要进行10^4次I/O,假设磁盘每次I/O时间为10ms(忽略数据传输时间),则总共需要100s(但实际上要好很多很多).如果对之建立B-Tree索引,则只需要进行log100(

  • MySQL安全性指南 (2)(转)

    正在看的ORACLE教程是:MySQL安全性指南 (2)(转). MySQL安全性指南(2) 作 者: 晏子 2.1.3 数据库和表权限下列权限运用于数据库和表上的操作. ALTER允许你使用ALTER TABLE语句,这其实是一个简单的第一级权限,你必须由其他权限,这看你想对数据库实施什么操作. CREATE允许你创建数据库和表,但不允许创建索引. DELETE允许你从表中删除现有记录. DROP允许你删除(抛弃)数据库和表,但不允许删除索引. INDEX允许你创建并删除索引. REFEREN

  • 关于MySQL索引的几点值得注意的事项

    在数据库中,对性能影响最大的包括数据库的锁策略.缓存策略.索引策略.存储策略.执行计划优化策略. 索引策略决定数据库快速定位数据的效率,存储策略决定数据持久化的效率. 1.索引不存储null值. 更准确的说,单列索引不存储null值,复合索引不存储全为null的值.索引不能存储Null,所以对这列采用is null条件时,因为索引上根本 没Null值,不能利用到索引,只能全表扫描. 为什么索引列不能存Null值? 将索引列值进行建树,其中必然涉及到诸多的比较操作.Null值的特殊性就在于参与的运

  • 怎样正确创建MySQL索引的方法详解

    索引类似大学图书馆建书目索引,可以提高数据检索的效率,降低数据库的IO成本.MySQL在300万条记录左右性能开始逐渐下降,虽然官方文档说500~800w记录,所以大数据量建立索引是非常有必要的.MySQL提供了Explain,用于显示SQL执行的详细信息,可以进行索引的优化. 什么是索引? MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构.我们可以简单理解为:快速查找排好序的一种数据结构.Mysql索引主要有两种结构:B+Tree索引和Hash索引.我们平

随机推荐