本文介绍一些优化 MySQL 索引设计和查询的建议。在进行优化工作前,请务必了解MySQL EXPLAIN命令: 查看执行计划

索引

索引在逻辑上是指从索引列(关键字)到数据的映射,通过索引可以快速的由关键字查找到数据记录。顺序查找复杂度为O(n), 树状索引查找复杂度为O(logn), 哈希索引为O(1)。

MySQL中的索引一般是指BTree索引, InnoDB存储引擎使用B+树来实现BTree索引。

BTree索引保持数据之间的顺序,可以极大的加快精确搜索(=, in)、范围搜索(<,>), 去重(DISTINCT), 排序(ORDER BY) 和 聚合(GROUP BY)。

总结来说使用索引有三个优点:

  • 极大减少了要扫描的数据量
  • 减少排序和临时表
  • 将随机IO变为顺序IO

因为写入数据时需要为新行建立索引,所以索引会减慢写入速度。请尽量避免创建无用的索引

索引只能用于独立的列

示例:

SELECT * FROM `user` WHERE `id`=5; -- 可以使用索引
SELECT * FROM `user` WHERE `id` + 1 = 5; -- 索引列作为表达式一部分时无法使用索引
SELECT * FROM `user` WHERE MD5(first_name)='MD5'; -- 索引列作为函数参数时无法使用索引

最左匹配原则

BTree索引具有最左匹配性质, 即只能按照索引列的顺序自左向右搜索,不能跳过索引列

联合索引中存在范围查询(<, >, like, between) 会导致后面的索引列失效

定义表和索引:

CREATE TABLE `user` (
`id` INT,
`first_name` VARCHAR(16),
`middle_name` VARCHAR(16),
`last_name` VARCHAR(16),
PRIMARY KEY (`id`),
KEY `idx_name` (`first_name`, `middle_name`, `last_name`)
);

示例:

SELECT * FROM `user` WHERE `first_name`='a'; -- 可以使用 idx_name 索引
SELECT * FROM `user` WHERE `first_name`='a' AND `middle_name`='b'; -- 可以使用 idx_name 索引
SELECT * FROM `user` WHERE `first_name`='a' AND `middle_name`='b' AND `last_name`='c'; -- 可以使用 idx_name 索引
SELECT * FROM `user` WHERE `middle_name`='b'; -- 不能使用 idx_name 索引
SELECT * FROM `user` WHERE `middle_name`='b' AND `last_name`='c'; -- 不能使用 idx_name 索引
SELECT * FROM `user` WHERE `first_name`='a' AND `last_name`='c'; -- 不能使用 idx_name 索引

上文中说的"可以使用索引"是指可以用ref,eq_refrange方式进行查询。

使用 EXPLAIN 命令查看3个不能使用索引示例的执行计划,可以发现 type 字段为 index, 这是在索引树上进行顺序查找。虽然性能优于全表扫描, 但比 ref 和 range 查询来说要慢很多。

索引列为字符串等类型时, 可以使用索引列的前缀字符串进行模糊查询

select * from user where first_name = 'abc' AND middle_name like 'de%';

这条语句的类型的为 range, 即在索引列上进行范围查询。

将联合索引理解为: 将索引列(关键字)按顺序拼接, 把拼接后的关键字与数据建立映射。最左匹配即是使用关键字前缀缩小搜索范围。

联合索引

在进行多列搜索时有一条经验法则: 首先使用选择性高的列进行搜索

我们可以将选择性定义为 count(distinct ) / count(*), 也就是说满足条件的数据越少,则条件的选择性越高。

假设用户名name比性别gender选择性高, 那么查询应该写作WHERE name='finley' AND gender='M'而不是WHERE gender='M' AND name='finley'

实际上两条语句是等效的, 当存在多个查询条件时 MySQL 优化器会根据索引和选择性决定最优的过滤顺序

为每一个列单独建立索引,并不能有效支持多列查询

CREATE TABLE `user` (
`id` INT,
`first_name` VARCHAR(16),
`middle_name` VARCHAR(16),
`last_name` VARCHAR(16),
PRIMARY KEY (`id`),
KEY `idx_first_name` (`first_name`),
KEY `idx_middle_name` (`middle_name`)
);

查询语句:

select * from user where first_name = 'a' AND middle_name = 'bc';

查看查询计划:

id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE user ALL idx_first_name,idx_middle_name NULL NULL NULL 4 100.00 Using where

根据最左匹配法则和优先使用高选择性列的经验法则,可以得出一条建议:

对于需要进行多列查询的表,应建立包含所有参与查询列的联合索引, 索引的顺序应按照列的选择性从强到弱排列

一些关于索引的建议

通常在使用索引检索到数据之后,需要访问磁盘上数据表文件读取所需要的列,这种操作称为"回表"。

若索引中包含查询的所有列,则不需要回表操作直接从索引文件中读取数据即可, 这种索引称为覆盖索引。

在查询时尽量减少"SELECT *"只查询需要的行, 条件允许时尽量建立覆盖索引。

《数据库索引设计与优化》一书中提出了判断最佳索引的"三星索引"概念:

  1. 1星: 可以在索引上(用 ref 或 eq_ref 方式)完成等值查询。需要取出等值谓词涉及的列作为索引开头的列以满足最左匹配原则。
  2. 2星: 可以使用索引进行排序
  3. 3星: 索引中包含要查询的所有列,不需要回表

MySQL 在索引包含 null 的列时需要额外的开销, 尽量避免允许索引列上存在 null

除非有非常严格的一致性要求,否则应避免使用外键

关于主键:

  • 避免使用字符串类型作为主键
  • 使用MD5、UUID等随机的主键可能导致更多的磁盘随机读写,但一般不会有太大的性能问题
  • auto_increment 使用锁机制实现,可能影响写入性能。

在查询较多且业务允许的情况下, 推荐使用自增主键。

不知道放哪儿好的两条建议:

  • BLOB 用于存储较大的二进制串,TEXT 用于存储较大的字符串; 它们不能被索引;
  • ip地址是32位无符号整数,使用 INT UNSIGNED 存储ip地址而不是字符串。INET_ATON(), INET_NTOA()可以转换数字和字符串两种格式

查询

一些关于查询的建议

  • 尽量避免使用 != 或 not in
  • 条件允许时避免使用 join 查询, 可以先分别查询然后在应用程序内存中关联
  • 避免在where语句中进行 is null 判断, 这可能导致MySQL放弃使用索引而进行全表扫描
  • 条件允许时使用 union all 而非 union, 避免 union 不必要的去重操作
  • 必要时使用 union (all) 代替 or 条件

小表驱动大表

MySQL在执行多表查询时可以采用Nest Loop Join算法,即选择数据集较小的一张表(数据集)作为驱动表, 遍历驱动表中所有记录并连接另一张表中符合条件的记录。

在使用 JOIN 进行查询时 MySQL 会自动选择数据集较小的一张表作为驱动表。

LEFT JOIN 强制左表作为驱动表, RIGHT JOIN 则强制选择右表作为驱动表。

MySQL 的 STRAIGHT_JOIN 结果与 INNER JOIN 相同, 但强制使用左表作为驱动表, 可用来分析选择不同驱动表的效果。

在业务允许的情况下, 让 MySQL 自行决定驱动表

在使用 IN 进行多表查询时一般会把 IN 内部的嵌套循环作为驱动表, 应尽量减少IN数据集的大小。实际上, MySQL 也会对 IN 和 EXISTS 查询进行优化, 选择最优的驱动表。

MySQL 索引与查询优化的更多相关文章

  1. 【转】MySQL索引和查询优化

    原文链接:http://www.cnblogs.com/mailingfeng/archive/2012/09/26/2704344.html 对于任何DBMS,索引都是进行优化的最主要的因素.对于少 ...

  2. MySQL索引及查询优化总结

    欢迎大家关注腾讯云技术社区-博客园官方主页,我们将持续在博客园为大家推荐技术精品文章哦~ 作者:谢庆玲 文章<MySQL查询分析>讲述了使用MySQL慢查询和explain命令来定位mys ...

  3. MySQL索引和查询优化

    对于任何DBMS,索引都是进行优化的最主要的因素.对于少量的数据,没有合适的索引影响不是很大,但是,当随着数据量的增加,性能会急剧下降. 如果对多列进行索引(组合索引),列的顺序非常重要,MySQL仅 ...

  4. MySQL索引及查询优化总结 专题

    小结:db名与应用名相同,表名:业务名_此表的作用 ,表名表示内容,不体现数量,如果表示boolean概念,表名需要使用is_业务含义来表示,但POJO中不应该出现isXXX,因为不方便序列化,中间的 ...

  5. MySQL 索引及查询优化总结

    本文由云+社区发表 文章<MySQL查询分析>讲述了使用MySQL慢查询和explain命令来定位mysql性能瓶颈的方法,定位出性能瓶颈的sql语句后,则需要对低效的sql语句进行优化. ...

  6. mysql 索引和查询优化

    对于任何DBMS,索引都是进行优化的最主要的因素.对于少量的数据,没有合适的索引影响不是很大,但是,当随着数据量的增加,性能会急剧下降.如果对多列进行索引(组合索引),列的顺序非常重要,MySQL仅能 ...

  7. mysql 索引、查询优化

    查询计划Explain mysql查询过程中,如若想了解当前sql的执行计划,可以通过explain your_sql的方式查看,具体可以参考mysql官方解释:https://dev.mysql.c ...

  8. MySQL索引及查询优化

    mysql 索引 1.索引介绍 索引按数据结构分可分为哈希表,有序数组,搜索树,跳表: 哈希表适用于只有等值查询的场景 有序数组适用于有等值查询和范围查询的场景,但有序数组索引的更新代价很大,所以最好 ...

  9. 开发人员不得不知的MySQL索引和查询优化

    转载:https://blog.csdn.net/enmotech/article/details/88809822 本文主要总结了慢查询优化的过程中常用的以及不合理的操作,适合有 MySQL 基础的 ...

随机推荐

  1. sql批量新增,修改

    <insert id="insertExtDocList" parameterType="map"> INSERT INTO extprjdoc ( ...

  2. Paper | 量化CV任务的关联性,寻找最佳迁移策略(Taskonomy)

    目录 1. 问题 2. 方法 3. 实验设计 3.1. 解决词典内部(一组已知)任务的能力 3.2. 解决新任务(少量标记数据)的能力 4. 讨论和启发 论文:Taskonomy: Disentang ...

  3. scrapy的入门使用(一)

    1. scrapy项目实现流程 创建一个scrapy项目:scrapy startproject mySpider 生成一个爬虫:scrapy genspider  提取数据:完善spider,使用x ...

  4. mybatis框架的注意点

    1.在mapper.xml文件中 resultType:返回结果类型,只能用于单表 paramsType:接收参数类型,基本数据类型可省略 2.给实体类起别名 在mybatisConfig.xml配置 ...

  5. intentservice 内部类

    https://blog.csdn.net/u010746364/article/details/50503586

  6. 与http协作的web服务器--代理、网关、隧道

    一台服务器可以搭建多个web站点 代理: 接受客户端发送的请求,转发给其他服务器,然后接受服务器的返回结果(响应)再返回给客户端.每次经过代理服务器,就会追加写入via首部信息. 按两种基准分类.一种 ...

  7. Solidity: ParserError: Expected pragma, import directive or contract/interface/library definition.

    第一行忘记加分号 pragma solidity ^0.4.0;

  8. 不适合使用hadoop来解决的问题

    1.Hadoop能解决的问题必须是可以mapreduce的.一是问题可以拆分,二是子问题必须独立.比如斐波那契数列就不适合. 2.数据结构不满足key-value形式的.比如结构化的数据查询. 3.不 ...

  9. Git使用详细教程(5):修改提交说明

    在使用git的过程中,我们有时在提交时,注释说明会写错,那么我们该如何修改这次提交说明呢?在SVN上我们只能在代码的某个地方加一个不影响功能的空格再次提交,然后写新说明.但是在Git中我们可以吃后悔药 ...

  10. 第38节:hashCode()与toString()与equals()函数的作用,内部类和匿名内部类

    hashCode()和toString() Hash算法是把任意长度的数据通过hash算法成为散列值 hashCode() public int hashCode(){ int result = 10 ...