MySQL之创建高性能的索引(十二)

ops/2024/10/9 15:20:34/

创建高性能的索引

支持多种过滤条件

这些索引将满足大部分最常见的搜索查询,但是如何为一些生僻的搜索条件(比如has_pictures、eye_color、hair_colr和education)来设计索引呢?这些列的选择性搞,使用也不频繁,可以选择忽略它们,让MySQL多扫描一些额外的行即可。另一个可选的方法是在age列的前面加上这些列,在查询时使用前面提到过的IN()基数来处理搜索时没有指定这些列的场景。你可能已经注意到了,我们一直将age列放在索引的最后面。age列有什么特殊的地方吗?为什么要放在索引的最后?我们总是尽可能让MySQL使用更多的索引列,因为查询只能使用索引的最左前缀,直到遇到第一个范围条件列。前面提到的列在WHERE子句中都是等于条件,但age列则多半是范围查询(例如查找年龄在18~25岁之间的人)。当然,也可以使用IN()来代替范围查询,例如年龄条件改写为IN(18,19,20,21,22,23,24,25)但不是所有的范围查询都可以转换。这里描述的基本原则是,尽可能将需要做范围查询的列放到索引的后面,以便优化器能使用尽可能的索引列。前面提到可以在索引中假如更多的列,并通过IN()方式覆盖那些不在WHERE子句中的列。但这种技巧也不能滥用,否则可能会带来麻烦。因为每额外增加一个IN()条件,优化器需要做的组合都将以指数形式增加,最终可能会极大地降低查询性能。考虑下面地子句:

WHERE eye_color IN ('brown', 'blue','hazel') AND hair_color IN ('black','red','blonde','brown') AND sex IN('M','F')

优化器则会转换成4x3x2=24种组合,执行计划需要检查WHERE子句中所有地24种组合。对于MySQL来说,24种组合并不是很夸张,但如果组合数达到上千个则需要特别小心。老版本的MySQL在IN()组合条件过多的时候会有很多问题。查询优化可能需要花很多时间,并消耗大量的内存。新版本的MySQL在组合数超过一定数量后就不再进行执行计划评估了,这可能会导致MySQL不能很好地利用索引。

避免多个范围条件

假设我们有一个last_online列并系统通过下面的查询显示在过去几周上线过的用户:

WHERE eye_color IN ('brown','blue','hazel')
AND hair_color IN('blackj','red','blonde','brown')
AND sex IN ('M','F')
AND last_online > DATE_SUB(NOW, INTERVAL 7 DAY)
AND age BETWEEN 18 AND 25

什么是范围条件?
从EXPLAIN的输出很难区分MySQL是要查询范围值还是查询列表值.EXPLAIN使用同样的词"range"来描述这两种情况.例如,从type列来看,MySQL会把下下面这种查询当作是"range"类型:

mysql> EXPLAIN SELECT actor_id FROM sakila.actor WHERE actor_id > 45\G
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: actorpartitions: NULLtype: range
possible_keys: PRIMARYkey: PRIMARYkey_len: 2ref: NULLrows: 155filtered: 100.00Extra: Using where; Using index
1 row in set, 1 warning (0.00 sec)

但是下面这条查询呢?

mysql> EXPLAIN SELECT actor_id FROM sakila.actor WHERE actor_id IN(1,4,99)\G
*************************** 1. row ***************************id: 1select_type: SIMPLEtable: actorpartitions: NULLtype: range
possible_keys: PRIMARYkey: PRIMARYkey_len: 2ref: NULLrows: 3filtered: 100.00Extra: Using where; Using index
1 row in set, 1 warning (0.00 sec)

从EXPLAIN的结果是无法区分这两者的,但可以从值的范围和多个等于条件来得出不同。在我们看来,第二个查询就是多个等值条件查询的。我们不是挑剔:这两种访问效率是不同的。对于范围条件查询,MySQL无法再使用范围列后面的其他索引列了,但是对于"多个等值条件查询"则没有这个限制

这个查询有一个问题:它有两个范围条件,last_online列和agelie,MySQL可以使用last_online列索引或者age列索引,但无法同时使用它们。如果条件中只有last_online而没有age,那么我们可能考虑再索引的后面加上last_online列。这里考虑如果我们无法把age字段转换为一个IN()的列表,并且仍要求对于同时有last_online和age这两个维度的范围查询的速度很快,那该怎么办?答案是,很遗憾没有一个直接的办法能够解决这个问题。但是我们能够将其中的一个范围查询转换为一个简单的等值比较。为了实现这一点,我们需要事先计算好一个active列,这个字段由定时任务来维护。当用户每次登录时,将杜英值设置为1,并且将过去连续七天未曾登录的用户的值设置为0.
这个方法可以让MySQL使用(active,sex,country,age)索引。active列并不是完全精确的,但是对于这类查询来说,对精度的要求也没有那么高,如果需要精确数据,可以把last_online列放到WHERE子句,但不加入到索引中。这和起那么通过计算URL哈希值来实现URL的快速查找类似。所以这个查询条件没法使用任何索引,但因为这个条件的过滤性不高,即使在索引中加入该列也没有太大的帮助,换个角度来说,缺乏合适的索引对该查询的影响也不明显。
到目前为止,我们可以看到:如果用户希望同时看到活跃和不活跃的用户,可以在查询中使用IN()列表。我们已经加入了很多这样的列表,但另外一个可选的方案就只能是为不同的组合创建单独的索引。至少需要建立如下的索引:(active,sex.country,age),(active,country,age),(sex,country,age)和(country,age)。这些索引对某个具体的查询来说可能都是更优化的了,但是考虑到索引的维护和额外的空间占用的代价,这个可选方案就不是一个好策略了。
在这个案例中,优化器的特性是影响索引策略的一个很重要的因素。如果未来版本的MySQL能够实现松散索引扫描,就能在一个索引上使用多个范围条件,那也就不需要为上面考虑的这类查询使用IN()列表了

优化排序

最后要介绍的是排序。使用文件排序对小数据集是很快的,但如果一个查询匹配的结果有上百万行的话会怎样?例如如果WHERE子句只有sex列,如何排序?对于那些选择性非常低的列,可以增加一些特殊的索引来做排序。例如,可以创建(sex,rating)索引用于下面的查询:

mysql SELECT <cols>  FROM profiles WHERE sex= 'M'  ORDER BY rating LIMIT 10;

这个查询同时使用了ORDER BY 和LIMIT,如果没有索引的话会很慢。即使有索引,如果用户界面山需要翻页,并且翻页翻到比较靠后时查询也可能非常慢。下面这个查询就通过ORDER BY 和LIMIT偏移量的组合翻页到很后面的时候:

mysql> SELECT <cols> FROM profiles WHERE sex='M' ORDER BY rating LIMIT 100000010

无论如何创建索引,这种查询都是个严重的问题,因为随者偏移量的增加,MySQL需要花费大量的时间来扫描需要丢弃的数据。反范式化、预先计算和缓存可能是解决这类查询的仅有策略。一个更好的办法是限制yoghurt能够翻页的数量,实际上这对用户体验的影响不大,因为用户很少会真正在乎搜索结果的第10 000页。优化这类索引的另一个比较好的策略是使用延迟关联,通过使用覆盖索引查询返回需要的主键,再根据这些主键关联源表获得需要的行。这可以减少MySQL扫那些需要丢弃的行数。下面这个查询显示了如何高效地使用(sex,rating)索引进行排序和分页:

mysql> SELECT <cols> FROM profiles INNER JOIN(SELECT <primary key cols> FROM profiles WHERE x.sex ='M' ORDER BY rating LIMIT 1000000,10) AS x USING(<primary key cols>);

http://www.ppmy.cn/ops/46267.html

相关文章

C语言编程数学:探索、挑战与深度应用

C语言编程数学&#xff1a;探索、挑战与深度应用 C语言&#xff0c;作为计算机编程的基石之一&#xff0c;不仅广泛应用于系统级编程&#xff0c;还在数学计算领域发挥着重要作用。本文将围绕C语言在数学编程中的四个方面、五个方面、六个方面和七个方面展开探讨&#xff0c;带…

如何恢复 Android 设备上丢失的照片

由于我们的大量数据和日常生活都存储在一台设备上&#xff0c;因此有时将所有照片本地存储在 Android 智能手机或平板电脑上可能是一种冒险行为。无论是由于意外&#xff08;损坏、无意删除&#xff09;&#xff0c;还是您认识的人翻看您的设备并故意删除了您想要保留的照片&am…

针对硅基氮化镓高电子迁移率晶体管(GaN-HEMT)的准物理等效电路模型,包含基板中射频漏电流的温度依赖性

来源&#xff1a;Quasi-Physical Equivalent Circuit Model of RF Leakage Current in Substrate Including Temperature Dependence for GaN-HEMT on Si&#xff08;TMTT 23年&#xff09; 摘要 该文章提出了一种针对硅基氮化镓高电子迁移率晶体管&#xff08;GaN-HEMT&…

MySQL性能分析工具——EXPLAIN

性能分析工具——EXPLAIN 1、概述 定位了查询慢的SQL之后&#xff0c;我们就可以使用EXPLAIN或DESCRIBE工具做针对性的分析查询语句 。 DESCRIBE语句的使用方法与EXPLAIN语句是一样的&#xff0c;并且分析结果也是一样的。 MySQL中有专门负责优化SELECT语句的优化器模块&…

使用 Ubuntu + Docker + Vaultwarden + Tailscale 自建密码管理器

使用 Ubuntu Docker Vaultwarden Tailscale 自建密码管理器 先决条件 一台运行 Ubuntu 系统的服务器。可以是云提供商的 VPS、家庭网络中的树莓派、或者 Windows 电脑上的虚拟机等等 一个 Tailscale 账户。如果还没有 Tailscale 账户&#xff0c;可以通过此链接迅速创建一个…

社交媒体数据恢复:Voxer

一、Voxer数据恢复教程 了解Voxer应用 Voxer是一款专门为iPhone和Android智能手机设计的免费对讲机应用&#xff0c;为用户提供即时的语音、文本、照片等信息发送和接收服务。该应用有点类似短信服务&#xff0c;但用声音代替文本。当你下载之后&#xff0c;如果不邀请朋友&a…

SpringBoot校园疫情管理系统-计算机毕业设计源码81164

摘 要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;校园当然也不例外。校园疫情管理系统是以实际运用为开发背景&#xff0c;运用软件工程原理和开发方法&#xff0c;采用J…

【C++】6-8 你好,自定义类型的输入输出

6-8 你好&#xff0c;自定义类型的输入输出 分数 15 全屏浏览 切换布局 作者 向训文 单位 惠州学院 完善程序&#xff0c;使程序正确运行&#xff1a; Student类为Person类的派生类 裁判测试程序样例&#xff1a; #include <iostream> #include <string> us…