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高性能索引策略 的相关文章

  • 我应该定义索引(A)和索引(B),还是索引(A,B),或者两者都定义?

    在我的表中 我有两个密切相关的列 A 和 B 我应该考虑哪些因素来决定是否创建 索引 A 和索引 B 索引 A B 以上两者 如果我 仅使用类似的查询where A 5 and B 10 并且从不喜欢where A 5 也可以使用类似的查询
  • R:ifelse 中的字符串列表

    我正在寻找与 MySQL 中的 where var in 语句类似的东西 我的代码如下 data lt data frame id 10001 10030 cc1 rep c a b c 10 attach data data new lt
  • PHP,文本从数据库中回显,没有换行,全部一体

    我的数据库中有一个长文本 从 php mayadmin 来看它看起来很好 但是当我将它回显到页面时 它会丢失所有格式 即没有新行 全部都在一个块中 有任何想法吗 Thanks 可能是因为换行符是 n 并且 html 想要 br 所以使用nl
  • 重置 MySQL root 密码不起作用

    我花了很多时间阅读并尝试了数十种重置 root 密码的方法 但我一无所获 我发现 并尝试过 的最完整的说明如下 顺便说一句 我在 Win7 32 位上运行 MySQL 5 5 我创建了一个文件 c mysqlinit txt 其中包含两行
  • 为什么我收到“无法进行二进制日志记录”的信息。在我的 MySQL 服务器上?

    当我今天启动 MySQL 服务器并尝试使用以下命令进行一些更改时用于 MySQL 的 Toad http www quest com toad for mysql 我收到此消息 MySQL 数据库错误 无法进行二进制日志记录 消息 交易级别
  • 维护/更新mysql中的记录顺序

    我在 mySql 中有一个记录表 我需要按照用户指定的方式维护它们的订单 所以我添加了一个 位置 列 当我移动特定记录时更新所有记录的 SQL 语句是什么 我有类似的东西 UPDATE items SET position 2 WHERE
  • 删除表的重复项

    In my activity logs 它包含列 material name user id mod result 这标志着测试是否通过 失败 cert links 不知何故 用户生成了两倍的条目material name与cert lin
  • Magento --“SQLSTATE[23000]:违反完整性约束..”客户更新

    迁移服务器后 每次尝试更新客户信息时都会出现错误 我正在使用一个客户激活插件 http www magentocommerce com magento connect vinai extension 489 customer activat
  • 如何将 mysql 转换为 mysqli? [复制]

    这个问题在这里已经有答案了 我厌倦了将 mysql 转换为 mysqli 但似乎收到了很多错误和警告 连接到数据库没有问题 但其余代码似乎错误 我做错了什么 sql
  • 计算 mysql 数据库行数的最佳方法

    在遇到 mysql 查询加载时间慢的问题后 我现在正在寻找计算行数的最佳方法 我曾经愚蠢地使用过mysql num rows 函数来做到这一点 现在意识到这是最糟糕的方法 我实际上正在制作一个分页来用 PHP 制作页面 我找到了几种计算行数
  • 当复选框条件更改时,如何使用ajax更新mysql数据库?

    我有一个在客户端按行显示的文章表 每篇文章都有一个唯一的 ID 并包含一个复选框以指示该文章是否被选中为收藏夹 如果它是最喜欢的 则该复选框已被选中 如果没有 则未选中 现在 如果特定于每一行的复选框条件发生变化 我需要 js 或 jque
  • 何时在 mysql 中使用 Union [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 对于 Union 您会在什么现实情况下使用它 因为对我来说 对具有不同列用途 含义的两个表中的两个选择查询使用联合是没有意义的 例如
  • Laravel 5.4 升级 - 违反完整性约束 - 列不能为空

    奇怪的是 所有这些都在 5 2 中工作 但我不知道可以改变什么来实现这一点 下面是错误和正在插入的数组 SQLSTATE 23000 Integrity constraint violation 1048 Column gender can
  • 如何将行变成列?

    我有一个数据库 其中存储分组到项目中的关键字以及与每个关键字相关的数据 然后我显示每个项目的数据网格 每个关键字一行和几列 全部从同一个表 数据 中检索 我有 4 个表 关键字 项目 group keywords 和数据 keywords
  • PHP使用auto_increment生成短唯一ID?

    我想生成一个简短的 唯一的 ID 而不必检查冲突 我目前正在做类似的事情 但是我当前生成的 ID 是随机的 并且在循环中检查冲突很烦人 并且如果记录数量显着增加 将会变得昂贵 通常担心冲突不是问题 但我想要生成的唯一 ID 是一个由 5 8
  • 从 Grib 天气模型中提取数据

    我已经下载了grib1模型数据来自GFS http en wikipedia org wiki Global Forecast System 我使用的是 Mac OS X 并且能够构建wgrib2文件来自NOAA http en wikip
  • MySQL - 多个结果集

    我正在使用 NET Connector 连接到 MySQL 在我的应用程序中 很少有线程使用相同的连接 因此如果 MySQLDataReader 尚未关闭并且某个线程正在尝试执行查询 则会出现该错误 已经有一个打开的 DataReader
  • MySQL 将表从 Latin1 转换为 utf8

    我需要将包含大量数据的表从 Latin1 转换为 utf8 以便它可以接受韩语字符 如何更改该表而不损坏其中的数据 我的 SQL 语句是什么 最好的方法是什么 ALTER TABLE database name table name CON
  • 使用来自另一个数据库的选择查询更新 mysql 表

    我有两个数据库 我想用另一个数据库表中的值更新一个表 我正在使用以下查询 但它不起作用 UPDATE database1 table1 SET field2 database2 table1 field2 WHERE database1 t
  • 更改mysql数据库表中的日期格式

    大家早上好 只是一个简单的问题 在我现有的 MySql 数据库中 我几乎没有包含日期 的列 目前这些是年 月 日格式 但现在我需要将其全部更改为年 月 日格式 我试过了select date format curdate d m Y 但它不

随机推荐

  • 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高性能索引策略

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