热门标签 | HotTags
当前位置:  开发笔记 > 后端 > 正文

高性能MySql进化论(七):正确的使用索引

数据库引擎利用索引提高查询效率,同时也针对索引增加了很多的优化策略,但是在使用索引的过程中也有很多的细节问题需要注意,如果忽略了这些问题,辛辛苦苦建立的索引可能得不到很好的应用,而且有可能还会对性能有一定的影响,下面列出了在使用索引的过程

数据库引擎利用索引提高查询效率,同时也针对索引增加了很多的优化策略,但是在使用索引的过程中也有很多的细节问题需要注意,如果忽略了这些问题,辛辛苦苦建立的索引可能得不到很好的应用,而且有可能还会对性能有一定的影响,下面列出了在使用索引的过程

数据库引擎利用索引提高查询效率,同时也针对索引增加了很多的优化策略,但是在使用索引的过程中也有很多的细节问题需要注意,如果忽略了这些问题,辛辛苦苦建立的索引可能得不到很好的应用,而且有可能还会对性能有一定的影响,下面列出了在使用索引的过程中需要遵守的原则

1 保持单纯的列

所谓的“单纯的列”指的就是在作为查询条件时,不要使用运算符,函数对字段进行处理,否则相关的索引将不能使用

下面列出两种最常见的错误情况
(1) select wordfrom dictionary where id+1=999;

不应该使用id+1的方式

(2) selectword from dictionary where to_days(CURRENT_DATE)-to_days(id)<=10;

不应该使用TO_DAYS(id)的方式

2 正确的采用“前缀索引”的前缀长度

在有些时候,需要使用长字符串作为索引,这样的索引由于占用的空间比较大,以及排序的时候&#20540;之间的比较会花费很多的时间,效率会比较低。对于这种情况可以只使用这个字段的前N个字符作为索引的&#20540;,这种策略就叫做”前缀索引”。比如,在MYSQL中如果需要使用BLOB/TEXT类型的字段作为索引的话,那么必须使用前缀索引,因为这几种类型不允许作为索引。

使用“前缀索引”会带来选择性的问题,比如某张表的长字段索引记录如下,如果采用整个字段的&#20540;作为索引的&#20540;的话虽然效率很低,但是匹配到最后应该只有一条记录与之相符,这种情况下选择性为1,是最高的。如果采用4个字符作为前缀,那么其实这个索引也就失去了价&#20540;,因为它的数量和表记录的数量是相等的,不管怎么优化,做的也都是全表扫描。所以前缀长度的选择非常的重要

ABCDEFGHIJKDDD8

ABCDEFFSKDJKJKD7

ABCDKJH65654654K

ABCDEFGHIJKKJKJG

ABCDEFGHIJKFFFFEJ

可以采取以下方式确定前缀长度,应用别的书籍上的例子,这个例子中city是索引字段

2.1 首先确定整个表索引&#20540;的选择率:

select count(distinct city)/ count(*) from City_Demo;

假设执行的结果是0.0312

2.2 算出几个候选长度的选择率

SELECT COUNT(DISTINCTLEFT(city, 3))/COUNT(*) AS sel3,

COUNT(DISTINCT LEFT(city,4))/COUNT(*) AS sel4,

COUNT(DISTINCT LEFT(city,5))/COUNT(*) AS sel5,

COUNT(DISTINCT LEFT(city,6))/COUNT(*) AS sel6,

COUNT(DISTINCT LEFT(city,7))/COUNT(*) AS sel7

FROM city_demo;

&#43;--------&#43;--------&#43;--------&#43;--------&#43;--------&#43;

| sel3 | sel4 | sel5 | sel6| sel7 |

&#43;--------&#43;--------&#43;--------&#43;--------&#43;--------&#43;

| 0.0239 | 0.0293 | 0.0305 | 0.0309 | 0.0310 |

2.3 选一个最靠近的&#20540;,从比较的结果看,应该选7作为前缀的长度

2.4 接下来创建这个前缀索引

ALTER TABLE sakila.city_demo ADD KEY (city(7));

最后要指出的是“前缀索引”的缺点:MYSQL无法使用前缀索引做orderby,group by, 以及覆盖索引(后面会提到)

3 组合索引中字段的顺序问题

在创建B-Tree的组合索引时,由于B-Tree的匹配顺序是按照存储的顺序来比较的,所以说如果前面的字段可以过滤掉更多的记录的话,后面的条件就会比较更少的记录,当然效率也就更高,用下面的组合索引做个简单的解释

NAME

AREA

如果把NAME放在第一个位置,那么在用AREA进行比较时,可能只用10条记录需要比较,如果把AREA放在第一个位置,那么在用NAME进行比较是,可能会有1000条记录需要比较,可以简单的推断出需要把NAME放在索引的第一列,这个原理决定了Hash索引是不适用该规则的。

上面的小例子,可以通过字段的选择率来确定索引的字段顺序,转换成SQL的表示方式如下,

SELECT COUNT(DISTINCT NAME)/COUNT(*) AS name_selectivity,

COUNT(DISTINCT AREA)/COUNT(*) AS area_selectivity,

COUNT(*)

FROM student\G

*************************** 1. row***************************

area_selectivity: 0.0001

name_selectivity: 0.0373

COUNT(*): 16049

结论:把结果中选择率高的字段放在组合的前面

4 聚合索引(Cluster Indexes)

首先要说明的是“聚合索引”并不是指某种具体的索引类型,而是指索引数据的存储方式。其具体的实现细节和数据库引擎实现紧密相关,基本的思想是”将索引和数据行保存在一个数据结构中,且数据行和相邻的键&#20540;存储在一起”。如果查询的结果都可以被索引覆盖的话,就不需要再回数据库中进行数据的检索。

在Oracle中,可以显示的指定哪些索引为聚合索引。在目前版本的MySQL中,各个存储引擎还不能通过“任意指定”的方式来设置聚合索引。在InnoDB中默认的情况会使用主键作为聚合索引,如果没有建立主键,会选择一个“非空且唯一”的索引来代替,如果 “非空且唯一”的索引也不存在,它会自定义一个主键作为聚合索引。

最后需要指出的是,因为这种存储方式中,相邻键&#20540;的数据行是存储在一起的,所以应该尽量采用键&#20540;递增的插入方式。

如果是使用UUID这种随机的主键,会导致大量的随机IO访问,插入效率会很低

5 覆盖索引(Converting Indexes)

覆盖索引是指:索引的字段包含了所有要查询的字段,就成为覆盖索引。

例如Student表中在(Name,Area)上建立了索引:

select name,area from student wherename=’Eric’ 这样的查询就成为是被索引覆盖的,从执行计划上看,如果EXPLAIN一条查询的Extra列包含“Using Index”就说明这条查询被索引覆盖了。

通常情况下利用索引查询到记录的时候,首先会查询到索引的Node,然后根据索引Node中存储的记录指针再到数据库的中查找记录,这种方式也就相当于进行了两次查询,而且数据库记录查询的效率往往会比较低。覆盖索引由于它本身就包含了要查询的字段&#20540;,也就避免了对数据库记录的访问,从而极大的提高了查询的效率。

有一点需要注意,因为覆盖索引用到的是索引字段的&#20540;,而“Hash/空间/全文” 索引中并没有存储索引字段的实际&#20540;,所以他们是不支持覆盖索引的。


关于覆盖索引,除了上面提出的问题外,还需要注意MYSQL的版本问题。MYSQL5.5以后的版本对该特性提供了更好的支持,所以尽可能还是使用最新的版本。

6 索引顺序扫描排序

MYSQL 可以按照索引顺序扫描(Explain 中的type为index)以及排序操作来实现排序功能。

如果使用了索引顺序扫描以及覆盖索引可以使排序的速度大大的提高,如果索引不能包含所有要查询的列,则在取每条记录&#20540;的时候都需要根据记录指针去查找对应的记录,这有可能会带来大量的随机I/O的产生,从而使查询效率下降。所以在设计索引的时候,既要考虑到查询条件,也要考虑到排序操作

为了使索引更好的服务于排序动作,在拼写SQL的过程中有以下几个问题需要注意

(1) 使用“覆盖索引”

(2) 索引的顺序和order by的顺序一致,且所有列的排序方向一致

(3) 多表关联排序时,order by的条件字段全部都是第一个表的

(4) Order by 和where一样,也要遵守“最左原则“

(5) 如果where或者join中指定了某个列为常量,则可以弥补规则(4),

例如 INDEX(Name,Age,ID) 则 ….where name=”Eric” order by age, id 也可以使用索引顺序扫描排序

7 轻便的索引

MYSQL允许在相同的Column上创建多个索引,MYSQL需要单独维护每个重复的索引,而且查询优化器也会在这些重复的索引上花时间,从而可能导致整体性能的下降。

所以应该尽量保持索引的"轻便"

· 重复的索引

定义:在相同的列上,按照相同的顺序,创建相同类型的索引。

例如 下面的语句将在ID上创建三个索引,对于Primary KEY而言,UNIQUE以及INDEX都是重复的

Create table test(

 ID int not null PRIMARY KEY,

 UNIQUE(ID),

 INDEX(ID),

)


· 冗余的索引

这里的“冗余“指的是多个索引有着相同的类型,且功能重复。

例如对于B-Tree索引而言,如果创建了索引(A,B),再创建索引(A),则(A)就被认为是重复的,但是如果创建了(B)则不被认为是冗余的。

冗余的索引一般是发生在扩展的索引的时候,例如已经存在了一个索引(A),有人为了满足新的查询需求,创建了一个新的索引(A,B),这个时候(A)就成了冗余的索引,应该进行删除,或者是用(A)来扩展成(A,B)

有时候冗余的索引也能带来查询性能上的提升,例如当需要向索引中添加了一个超长的字符串字段,因为这个字段会导致索引存储空间的变大,导致了查询效率的降低,所以适当的使用冗余索引对性能会有帮助。

可以通过对INFORMATION_SCHEMA中的数据来判断索引是否是重复/冗余,也可以通过专门的工具例如Percona-Toolkit的pt-duplicate-key-checker 来检测(http://www.percona.com/doc/percona-toolkit/2.1/pt-duplicate-key-checker.html)

· 未使用的索引

和前面两种情况类&#20284;,当系统中存在大量未使用的索引时,同样对查询的效率会有影响,可以通过以下两种方式来判断那些索引时未使用的

(1) 在Percona或者是MariaDB中,通过打开userstates服务器变量,然后正常运行一段时间,最后通过INFORMATION_SCHEMA.INDEX_STATISTICS变量来判断

(2) 通过专门的工具,例如Percona-Toolkit的pt-index-usage来判断,该工具不仅可以查出那些索引是没有被使用的,还可以了解查询的执行计划(http://www.percona.com/doc/percona-toolkit/2.1/pt-index-usage.html)

需要注意的是,并不是未使用的索引就是没有用的索引,例如有些索引的功能是唯一性约束,虽然该索引一直没有被使用,但是却可以避免产生重复的数据,这种类型的索引在处理的时候需要小心


推荐阅读
  • 本文探讨了如何在PHP与MySQL环境中实现高效的分页查询,包括基本的分页实现、性能优化技巧以及高级的分页策略。 ... [详细]
  • CentOS下ProFTPD的安装与配置指南
    本文详细介绍在CentOS操作系统上安装和配置ProFTPD服务的方法,包括基本配置、安全设置及高级功能的启用。 ... [详细]
  • 本文详细探讨了在Web开发中常见的UTF-8编码问题及其解决方案,包括HTML页面、PHP脚本、MySQL数据库以及JavaScript和Flash应用中的乱码问题。 ... [详细]
  • centos 7.0 lnmp成功安装过程(很乱)
    下载nginx[rootlocalhostsrc]#wgethttp:nginx.orgdownloadnginx-1.7.9.tar.gz--2015-01-2412:55:2 ... [详细]
  • 在重新安装Ubuntu并配置Django和PyCharm后,忘记测试MySQL连接,导致在后续配置过程中遇到错误:ERROR 2003 (HY000) - 无法连接到本地服务器 ‘127.0.0.1’ (111)。本文将详细介绍该错误的原因及解决步骤,帮助用户快速恢复MySQL服务的正常运行。我们将从检查网络配置、验证MySQL服务状态、配置防火墙规则等方面入手,提供全面的故障排除指南。 ... [详细]
  • 如何在Linux系统上部署MySQL 5.7.28
    本文详细介绍了在Linux系统上部署MySQL 5.7.28的具体步骤。通过官方下载页面获取最新安装包后,按照提供的指南进行配置和安装。文章内容实用性强,适合初学者和有经验的管理员参考。 ... [详细]
  • 搭建个人博客:WordPress安装详解
    计划建立个人博客来分享生活与工作的见解和经验,选择WordPress是因为它专为博客设计,功能强大且易于使用。 ... [详细]
  • H5技术实现经典游戏《贪吃蛇》
    本文将分享一个使用HTML5技术实现的经典小游戏——《贪吃蛇》。通过H5技术,我们将探讨如何构建这款游戏的两种主要玩法:积分闯关和无尽模式。 ... [详细]
  • 软件测试行业深度解析:迈向高薪的必经之路
    本文深入探讨了软件测试行业的发展现状及未来趋势,旨在帮助有志于在该领域取得高薪的技术人员明确职业方向和发展路径。 ... [详细]
  • 如何在Django框架中实现对象关系映射(ORM)
    本文介绍了Django框架中对象关系映射(ORM)的实现方式,通过ORM,开发者可以通过定义模型类来间接操作数据库表,从而简化数据库操作流程,提高开发效率。 ... [详细]
  • 从CodeIgniter中提取图像处理组件
    本指南旨在帮助开发者在未使用CodeIgniter框架的情况下,如何独立使用其强大的图像处理功能,包括图像尺寸调整、创建缩略图、裁剪、旋转及添加水印等。 ... [详细]
  • 在关系型数据库中,数据约束是指在向数据表中插入数据时必须遵循的限制条件。在MySQL和MariaDB中,常见的数据约束包括主键约束、唯一键约束、外键约束以及非空约束等。这些约束确保了数据的完整性和一致性,是数据库管理中的重要组成部分。通过合理设置和使用这些约束,可以有效防止数据冗余和错误,提升数据库的可靠性和性能。 ... [详细]
  • MySQL 8.0 MGR 自动化部署与配置:DBA 和开源工具的高效解决方案
    MySQL 8.0 MGR 自动化部署与配置:DBA 和开源工具的高效解决方案 ... [详细]
  • 如何利用Apache与Nginx高效实现动静态内容分离
    如何利用Apache与Nginx高效实现动静态内容分离 ... [详细]
  • 在使用 PHP 通过 SSL 安全连接到 MySQLi 数据库服务器时,遇到了一些技术难题。我的环境包括一个 Web 服务器和一个数据库服务器,两者均使用 OpenSSL 生成了证书。尽管证书内容一致,但在尝试从 Web 服务器使用 `mysql` 命令进行连接时,仍然遇到了问题。为了确保连接的安全性和稳定性,需要进一步检查证书配置和 PHP 的 SSL 设置,以排除潜在的配置错误或兼容性问题。 ... [详细]
author-avatar
北关仔仔_378
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有