MySQL高性能索引策略

2023-10-30

高性能索引策略

正确地创建和使用索引是实现高性能查询的基础。高效地选择和使用索引有很多种方式,其中有些是针对特殊案例的优化方法,有些则是针对特定行为的优化。

独立的列

我们通常会看到一些查询不当地使用索引,或者使得 MySQL 无法使用已有的索引。如果查询的列不是独立的,则 MySQL 就不会使用索引。独立的列是指索引列不能是表达式的一部分,也不能是函数的参数。

例如,下面查询无法使用索引

select actor_id from where sakila.actor where actor_id + 1 =5

前缀索引

有时候需要索引很长的字符列,这会让所有编的大且慢,通常可以索引开始的部分字符,这样可以大大节约索引空间,从而提高索引效率,但这样也会降低索引的选择性。索引的选择性是指,不重复的索引值(也称基数 cardinality) 和数据表的记录总数(# T)的比值,范围从 1/# 到 1 之间。索引的选择性越高则查询效率越高,因为选择性高的索引可以让 MySQL 在查找时过滤掉更多的行,唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的。

一般情况下某个列前缀的选择性也是足够高的,足以满足查询性 能。对于 blob 、text 或者很长的 varchar 类型的列,必须使用前缀索 引,因为MySQL不允许索引这些列的完整长度

例如现在有一个地区表 sys_area

area code
上海市 1
北京市 2
张家口市 3
n
SELECT * FROM sys_area WHERE name = '北京市'

创建前缀索引

alter table sys_area add index(name(2))

在这里插入图片描述
在这里插入图片描述

前缀索引是一种能使索引更小、更快的有效办法,但另一方面也有 其缺点:MySQL无法使用前缀索引做ORDER BY 和GROUP BY ,也无法使 用前缀索引做覆盖扫描。

多列索引

在我们开发中一个常见的错误就是,为每个列创建独立的索引,或者按照错误的顺序创建多列索引

CREATE TABLE `test`  (
  `c1` int NULL DEFAULT NULL,
  `c2` int NULL DEFAULT NULL,
  `c3` int NULL DEFAULT NULL,
  INDEX `idx_c1`(`c1`) USING BTREE,
  INDEX `idx_c2`(`c2`) USING BTREE,
  INDEX `idx_c3`(`c3`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

这种索引策略,一般都是把 where 条件里面的列都建立索引,这样以来最好的情况也只能使用1个索引。

在多个列上建立独立的单列索引大部分情况下并不能提高MySQL 的查询性能。MySQL 5.0和更新版本引入了一种叫“索引合并”(index merge)的策略,一定程度上可以使用表上的多个单列索引来定位指定 的行。更早版本的MySQL只能使用其中某一个单列索引,然而这种情 况下没有哪一个独立的单列索引是非常有效的

在 MySQL 5.0更新版本中,查询能够同时使用这两个单列索引进行扫描,并将结果进行合并,下面的查询就是使用了两个索引扫描的联合,通过 EXPLAIN 的 Extra 可以看出

mysql> EXPLAIN SELECT *  FROM test WHERE  c1 = 1 or c2 = 2;

***********************************************************

+----+-------------+-------+------------+-------------+---------------+---------------+---------+------+------+----------+-----------------------------------------+
| id | select_type | table | partitions | type        | possible_keys | key           | key_len | ref  | rows | filtered | Extra                                   |
+----+-------------+-------+------------+-------------+---------------+---------------+---------+------+------+----------+-----------------------------------------+
|  1 | SIMPLE      | test  | NULL       | index_merge | idx_c1,idx_c2 | idx_c1,idx_c2 | 5,5     | NULL |    2 |   100.00 | Using union(idx_c1,idx_c2); Using where |
+----+-------------+-------+------------+-------------+---------------+---------------+---------+------+------+----------+-----------------------------------------+
1 row in set (0.03 sec)

MySQL 会使用这类技术优化复杂查询,所以在某些语句的 Extra 列中还可以看到嵌套操作。

索引合并策略有时候是一种优化的结果,但实际上更多时候说明了表上的索引建的很糟糕:

  • 当出现服务器多个索引做相交操作时(and) 通常意味着需要一个包含所有相关列的多列索引,而不是多个独立的单列索引。
  • 当服务器需要对多个索引做联合操作(or),通常需要耗费大量的cpu和内存资源在算法的缓冲,排序和合并的操作上。特别是当其中有些索引的选择性不高。需要合并扫描返回大量数据的时候
  • 更重要的是,优化器不会把这些计算到 “查询成本” (cost)中,优化器只关心随机页面读取。这会使得查询的成本被 “低估” ,导致该执行计划还不如全表扫描,这样不但会消耗更多的 cpu 和内存资源,还可能会影响查询的并发性,但如果是单独允许这样的查询则往往会忽略堆并发性的影响。

如果在 Explain 语句中看到索引合并,应该好好检查一下查询和表的结构。也可以通过参数optimizer_switch来关闭索引合并功能。也可以使用IGNORE_INDEX提示让优化器忽略掉某些索引。

选择合适的索引列顺序

对于如何选择索引的列顺序有个经验: 将选择性最高的列放在索引最前列

当不考虑排序和分组时,将选择性最高的列放在前面通常是很好的,这个时候索引的作用知识用于优化 where 条件的查询。在这种情况下,这样设计的索引确实能够最快地过滤出需要的行,对于在 where 子句中只使用了索引部分前缀列的查询来说选择性也更高。性能不只是依赖于所有索引列的选择性,也和查询条件具体值有关,也就是和值的分布有关。

聚簇索引

聚簇索引并不是一种单独的索引类型,而是一种数据存储方式。InnoDB中,表数据文件本身就是按B+Tree组织的一个索引结构,聚簇索引就是按照每张表的主键构造一颗B+树,同时叶子节点中存放的就是整张表的行记录数据,也将聚集索引的叶子节点称为数据页。这个特性决定了索引组织表中数据也是索引的一部分

Innodb通过主键聚集数据,如果没有定义主键,innodb会选择非空的唯一索引代替。如果没有这样的索引,innodb会隐式的定义一个主键来作为聚簇索引。

在这里插入图片描述

聚簇索引的优点:

  • 可以把相关数据保存在一起。例如实现电子邮件时,可以根据用户 ID 来聚集数据,这样只需要从磁盘读取少数的数据页就能获取某个用户全部邮件。如果没有使用聚簇索引,可能每封邮件都可能导致一次磁盘I/O
  • 数据访问更快。因为聚簇索引将索引和数据保存在同一个B+树中,因此从聚簇索引中获取数据比非聚簇索引更快
  • 使用覆盖索引扫描的查询可以直接使用页节点的主键值

缺点:

  • 聚簇数据最大限度地提高了 I/O 密集型应用性能,但如果数据全部放在内存中,则访问顺序就没那么重要了,优势就不存在了
  • 插入速度严重依赖插入顺序,按照主键的顺序插入是最快的方式,否则将会出现页分裂,严重影响性能。因此,对于InnoDB表,我们一般都会定义一个自增的ID列为主键
  • 更新主键的代价很高,因为将会导致被更新的行移动
  • 聚簇索引可能导致全表扫描变慢,尤其是行比较稀疏的时候,由于页分裂导致数据存储不连续的时候
  • 二级索引访问需要两次索引查找,第一次找到主键值,第二次根据主键值找到行数据

覆盖索引

只需要在一棵索引树上就能获取SQL所需的所有列数据,无需回表,速度更快。

如果一个索引包含所有需要查询的字段的值,我们就称为 ”覆盖索引”

好处:

  • 索引条目通常远小于数据行大小,所有如果只需要读取索引,那么MySQL 就会极大地减少数据访问量
  • 索引按照列值存储的,所以对于 I/O 密集型的范围查询比随机从磁盘读取每一行数据少的多。
  • 由于InnoDB的聚簇索引,覆盖索引对 InnoDB 表特别有用。

如何实现覆盖索引?

在explain查询计划优化章节,即explain的输出结果Extra字段为Using index时,能够触发索引覆盖

mysql> EXPLAIN SELECT c1 FROM test WHERE  c1 = 1 ;

*************************************************

+----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key    | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | test  | NULL       | ref  | idx_c1        | idx_c1 | 5       | const |    1 |   100.00 | Using index |
+----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------------+
1 row in set (0.07 sec)

在select返回列较少或列宽较小的时候,我们可以通过建立复合索引来实现覆盖索引

mysql> EXPLAIN SELECT c1,c2 FROM test WHERE  c1 = 1 ;


*************************************************

+----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key    | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | test  | NULL       | ref  | idx_union        | idx_union | 5       | const |    1 |   100.00 | Using index |
+----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------------+
1 row in set (0.05 sec)

使用索引扫描来做排序

mysql有两种方式可以生成有序的结果:通过排序操作或者按索引顺序扫描,如果explain出来的type列的值为index,则说明mysql使用了索引扫描来做排序

扫描索引本身是很快的,因为只需要从一条索引记录移动到紧接着的下一条记录。但如果索引不能覆盖查询所需的全部列,那么就不得不每扫描一条索引记录就得回表查询一次对应的行,这基本都是随机IO,因此按索引顺序读取数据的速度通常要比顺序地全表扫描慢

mysql可以使用同一个索引即满足排序,又用于查找行,如果可能的话,设计索引时应该尽可能地同时满足这两种任务。

只有当索引的列顺序和order by子句的顺序完全一致,并且所有列的排序方式都一样时,mysql才能够使用索引来对结果进行排序,如果查询需要关联多张表,则只有当orderby子句引用的字段全部为第一张表时,才能使用索引做排序。order by子句和查找型查询的限制是一样的,需要满足索引的最左前缀的要求,否则,mysql都需要执行顺序操作,而无法利用索引排序

mysql> EXPLAIN SELECT c1,c2 FROM test  order by c1,c2 desc;


*************************************************


+----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+-----------------------------+
| id | select_type | table | partitions | type  | possible_keys | key    | key_len | ref  | rows | filtered | Extra                       |
+----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+-----------------------------+
|  1 | SIMPLE      | test  | NULL       | index | NULL          | idx_c1 | 10      | NULL |    8 |   100.00 | Using index; Using filesort |
+----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+-----------------------------+
1 row in set (0.05 sec)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MySQL高性能索引策略 的相关文章

  • MySQL用户创建的临时表已满

    我使用内存引擎创建了一个临时表 如下所示 CREATE TEMPORARY TABLE IF NOT EXISTS some text id INT DEFAULT 0 string varchar 400 DEFAULT engine m
  • C#:SQL 查询生成器类

    在哪里可以找到好的 SQL 查询构建器类 我只需要一个简单的类来构建 SQL 字符串 仅此而已 我需要它用于 C 和 MySql 我真的不需要像 Linq 或 NHibernate 这样的东西 谢谢 由于 Google 将我引导至此页面 我
  • MySQL 全文搜索不适用于某些单词,例如“house”

    我已经在 3 个字段中的一小部分记录上设置了全文索引 也尝试了 3 个字段的组合 并得到了相同的结果 有些单词返回结果很好 但某些单词如 house 和 澳大利亚 不这样做 有趣的是 澳大利亚 和 家乡 这样做 这似乎是奇怪的行为 如果我添
  • 如何以最少的查询次数获取帖子列表和关联标签

    我的表格结构如下 标签 更多的是一个类别 id 标签名称 描述 slug POSTS ID 标题 网址 邮戳 id idPost idTag USERS ID 用户名 userSlug VOTES id idPost idUser 每个帖子
  • 社交应用程序的数据库设计和优化注意事项

    通常的情况 我有一个简单的应用程序 允许人们上传照片并关注其他人 因此 每个用户都会有类似 墙 或 活动源 的东西 他或她可以在其中看到他 她的朋友 他或她关注的人 上传的最新照片 大多数功能都很容易实现 然而 当涉及到这个历史活动源时 由
  • 当“修复表”查询在 mysql 中不起作用时该怎么办?

    我收到此错误 表的存储引擎不支持修复 当我尝试使用查询修复表时repair table tbl college master 表是 innodb 类型 但我不知道我收到此错误 See 手册 http dev mysql com doc re
  • 比较表中的行以了解字段之间的差异

    我有一个包含 20 多列的表 客户端 其中大部分是历史数据 就像是 id clientID field1 field2 etc updateDate 如果我的数据如下所示 10 12 A A 2009 03 01 11 12 A B 200
  • PDO SQLSRV 和 PDO MySQL 在获取 int 或 float 时返回字符串

    当您获取时 PDO MS SQL Server 和 PDO MySQL 都会返回一个字符串数组 即使列的 SQL 类型本应是数字类型 例如 int 或 float 我设法解决了这个问题 但我想了解为什么它们一开始就这样设计 是不是因为PDO
  • PHP 绑定“bigint”数据类型(MySQLi 准备好的语句)

    studentId 57004542323382 companyOfferId 7 sql INSERT INTO studentPlacement companyOfferId studentId VALUES if stmt db gt
  • 当sql连接中存在两个同名列时,如何从一个表列中获取值

    当我连接两个具有相同名称列的表时 我目前面临着尝试获取值的问题 例如 table1 date和table2 date 每个表中的日期不同 我将如何获取 日期 本例中的表1 我目前正在跑步 while row mysqliquery gt f
  • 为什么这会返回资源 id #2? [复制]

    这个问题在这里已经有答案了 可能的重复 我如何从 PHP 中的 MySql 响应中 回显 资源 id 6 https stackoverflow com questions 4290108 how do i echo a resource
  • MySQL 使用 ALTER IGNORE TABLE 出现重复错误

    我的 MySQL 中有一个有重复项的表 我尝试删除重复项并保留一项 我没有主键 我可以通过以下方式找到重复项 select user id server id count as NumDuplicates from user server
  • MYSQL 区分大小写的 utf8 搜索(使用 hibernate)

    我的登录表具有 utf8 字符集和 utf8 排序规则 当我想要检查用户名并检索该特定用户名的其他信息时 hql 查询会为我提供小写和大写相同的结果 我应该如何处理适用于案例的 HQL 查询 我使用 Mysql 5 和 java hiber
  • 如何正确转义mysql?

    我刚刚发现如果我写 select from tbl where name like foo 然后添加 foo 作为参数及其值 a 用户数据 它不会正确转义 我勒个去 它想要 a 即使我使用参数 我还是忍不住觉得我对 sql 注入持开放态度
  • MySQL MIN/MAX 所有行

    我有桌子Races与行ID Name and TotalCP 我选择分钟 TotalCP FROM Races 但是我想选择具有最小值的整行 我如何在单个查询中做到这一点 从聚合值获取整行的一般形式是 SELECT FROM Races W
  • 忽略重复条目并在 EF Core 中的 DbContext.SaveChanges() 上提交成功条目

    我有一个 ASP Net Core 2 2 Web API 在我的一个控制器操作中 我向 MySQL 数据库表添加了一堆行 我使用的是 Pomelo 例如 dbContext AddRange entities dbContext Save
  • 将古吉拉特语文本插入 MySQL 表会产生垃圾字符和不可读的文本

    我有三个 MySQL 表 我正在向其中插入古吉拉特语内容 当我插入两个表时 它们插入得很好并且可读 但在一个表中 它显示垃圾字符 不可读的文本 我怎样才能解决这个问题 MySQL 有每个表的字符集设置 http dev mysql com
  • MySQL连接字符集问题

    我在 Mac 上使用带有 MySQL 的 velosurf 没有任何编码问题 但是当我切换到 Linux 计算机时 从 velosurf 获得的值未正确编码 我发现这可能是默认连接字符集的问题 在 Mac 上我得到 mysql gt sho
  • Mysql 将 int 转换为 MAC

    我有一些数据可以转换 其中有 2 列 其中一列有 IP 它包含整数值 我在 mysql 查询中使用了以下函数 是否有一个函数可以用来转换我的 mac 列 其中包含整数和数据类型是bigint to MAC地址 SELECT INET NTO
  • MySQL 查询到 CSV [重复]

    这个问题在这里已经有答案了 有没有一种简单的方法来运行MySQL查询来自linux命令行并以csv格式输出结果 这就是我现在正在做的事情 mysql u uid ppwd D dbname lt lt EOQ sed e s g tee l

随机推荐

  • Canvas和SVG有什么区别

    在项目开发中也许会涉及到图形 经常用到的就是svg和canvas两种画图方式 下面就让我们看一看他们两者的区别 svg绘制出来的每一个图形的元素都是独立的DOM节点 能够方便的绑定事件或用来修改 canvas输出的是一整幅画布 svg输出的
  • RabbitMQ消息可靠性(二)-- 消费者消息确认

    一 消费者消息确认是什么 在这种机制下 消费者在接收到消息后 需要向 RabbitMQ 发送确认信息 告知 RabbitMQ 已经接收到该消息 并已经处理完毕 如果 RabbitMQ 没有接收到确认信息 则会将该消息重新加入队列 等待其他消
  • supervisor系列:2、运行supervisor

    supervisor系列 2 运行supervisor 文章目录 supervisor系列 2 运行supervisor 1 添加一个程序 2 运行supervisord 2 1 supervisord命令行配置 3 运行superviso
  • 【剑指Offer题解:java】二叉树的镜像

    文章目录 题目 分析 代码 题目 操作给定的二叉树 将其变换为源二叉树的镜像 二叉树的镜像定义 源二叉树 8 6 10 5 7 9 11 镜像二叉树 8 10 6 11 9 7 5 分析 递归交换左右子树即可 1 root null直接返回
  • 【环境配置】基于Docker配置Chisel-Bootcamp环境

    文章目录 Chisel是什么 Chisel Bootcamp是什么 基于Docker配置Chisel Bootcamp 官网下载Docker安装包 Docker换源 启动Bootcamp镜像 常用docker命令 可能产生的问题 Chise
  • Mysql获取数据库的所有表以及表所有字段信息

    mysql获取所有表以及表所有字段信息 SELECT TB TABLE SCHEMA 模式 TB TABLE NAME 表名 TB TABLE COMMENT 表名注释 COL COLUMN NAME 字段名 COL COLUMN TYPE
  • 风投与IT

    风投即风险投资 广义的风险投资泛指一切具有高风险 高潜在收益的投资 狭义的风险投资是指以高新技术为基础 生产与经营技术密集型产品的投资 根据美国全美风险投资协会的定义 风险投资是由职业金融家投入到新兴的 迅速发展的 具有巨大竞争潜力的企业中
  • vue设置不出现滚动条的全屏背景100%

    1 想在登录页面设置页面背景占比100 而且不出现滚动 首先给你所需要的元素设置好css 2 如果没生效 看下App vue中是否有定义 app的宽高 将其设置成100 3 如果综上两部设置完成还未生效 那就需要在index html文件中
  • kubernetes集群实战——资源限制(内存、CPU、NameSpace)

    1 k8s容器资源限制 Kubernetes采用request和limit两种限制类型来对资源进行分配 request 资源需求 即运行Pod的节点必须满足运行Pod的最基本需求才能 运行Pod limit 资源限额 即运行Pod期间 可能
  • MySQL必知必会 学习笔记 第一章 了解SQL

    数据库是保存有组织的数据的容器 通常是一个或一组文件 数据库软件称为DBMS 数据库管理系统 数据库是被DBMS创建和操纵的容器 数据库究竟是文件或其他东西并不重要 因为你不会直接访问数据库 而是间接通过DBMS替你访问数据库 表是某种特定
  • BootstrapTable checkbox默认选中

    BootstrapTable 在Web后台管理项目中对表格展示数据使用居多 主要是表格的多条件查询 分页 排序等功能 我们的项目中前端框架是Bootstrap 所以很多插件都是支持Bootstrap的 bootstrap table是一款非
  • 关于List的subList原理分析

    今天在看Java开发手册的时候看到这么一句话 如果需要对list某个范围内的元素进行操作 可以使用subList 任何对子列表的操作最终都会反映到原列表中 例如list subList 0 2 clear 这样的操作便会对原列表进行修改 修
  • SARScape中用sentinel-1数据做SBAS-InSAR完整流程(1/2)

    SARScape中用sentinel 1数据做SBAS InSAR完整流程 1 SABA InSAR原理简述 2 数据采集和预设 2 1 SAR数据采集 2 2 DEM数据下载与放置 2 3 精密轨道数据下载与放置 2 4 制作研究区范围矢
  • 三分钟教你小程序实现无感刷新!

    无感刷新 无感刷新对于前端来说是一项非常实用的技术 其本质是为了优化用户体验 让用户感受不到token已经过期 本质上就是登录时 储存token和refresh token 当token过期或错误时不需要用户跳回登录页重新登录 而是在响应拦
  • python读取文件名存到list_python读取文件名称生成list的方法

    下面为大家分享一篇python读取文件名称生成list的方法 具有很好的参考价值 希望对大家有所帮助 一起过来看看吧 经常需要读取某个文件夹下所有的图像文件 我使用python写了个简单的代码 读取某个文件夹下某个后缀的文件 将文件名生成为
  • 力扣 删除链表的节点

    给定单向链表的头指针和一个要删除的节点的值 定义一个函数删除该节点 返回删除后的链表的头节点 注意 此题对比原题有改动 示例 1 输入 head 4 5 1 9 val 5 输出 4 1 9 解释 给定你链表中值为 5 的第二个节点 那么在
  • 手动实现Spring IOC 跟 AOP 的雏形

    关注后回复 进群 拉你进程序员交流群 作者丨sowhat1412 来源丨sowhat1412 Spring Spring make java more simpleSpring make java more modernSpring mak
  • 在Linux下用C语言写贪吃蛇;

    项目思路 ncurses上下左右键的获得 gt 贪吃蛇地图的实现 gt 显示贪吃蛇的完整身子 gt 贪吃蛇向右移动 gt 贪吃蛇撞墙找死 gt 贪吃蛇自行向右行走与页面一起刷新 利用线程解决 gt 贪吃蛇四个方向的自由走位 gt 贪吃蛇吃饭
  • 教程:群体演化方法分析玉米的驯化与改良

    一般文章在筛选 正选择区间 时 大多 不考虑 群体的 演化历史 即不考虑 群体大小 的变化 只进行亚群之间各种群体遗传参数的对比 这可能会产生大量的假阳性 另一方面 研究一般也 不考虑 遗传信息的 迁移 所以作者希望将群体演化历史及遗传信息
  • MySQL高性能索引策略

    文章目录 高性能索引策略 独立的列 前缀索引 多列索引 选择合适的索引列顺序 聚簇索引 覆盖索引 使用索引扫描来做排序 高性能索引策略 正确地创建和使用索引是实现高性能查询的基础 高效地选择和使用索引有很多种方式 其中有些是针对特殊案例的优