Mysql 索引该如何设计与优化

作者:0xBoo 时间:2024-01-21 19:22:19 

目录
  • 什么是索引?

  • 最左前缀匹配原则

  • 如何计算 key_len

  • 索引优化

  • 创建索引规范

什么是索引?

数据库索引是一种数据结构,它以额外的写入和存储空间为代价来提高数据库表上数据检索操作的速度。通俗来说,索引类似于书的目录,根据其中记录的页码可以快速找到所需的内容。—— *
常见索引有哪些?

  • 普通索引:最基本的索引,没有任何限制

  • 唯一索引:与”普通索引“类似,不同的就是:索引列的值必须是唯一,但允许有空值

  • 主键索引:它是一种特殊的索引,不允许有空值

  • 全文索引:仅可用于 MyISAM 表,针对较大的数据,生成全文索引很耗时占空间

  • 组合索引:为了提高多条件查询效率,可建立组合索引,遵循"最左前缀匹配原则"

这里以相对复杂的组合为例,介绍如何优化。

最左前缀匹配原则

首先我们要知道什么是最左前缀匹配原则。

最左前缀匹配原则是指在使用 B+Tree 联合索引进行数据检索时,MySQL 优化器会读取谓词(过滤条件)并按照联合索引字段创建顺序一直向右匹配直到遇到范围查询或非等值查询后停止匹配,此字段之后的索引列不会被使用,这时计算 key_len 可以分析出联合索引实际使用了哪些索引列。

如何计算 key_len

通过 key_len 计算也帮助我们了解索引的最左前缀匹配原则。

key_len 表示得到结果集所使用的选择索引的长度[字节数],不包括 order by,也就是说如果 order by 也使用了索引则 key_len 不计算在内。

在计算 key_len 之前,先来温习一下基本数据类型(以UTF8 编码为例):

类型所占空间不允许为NULL额外占用
char一个字符三个字节一个字节
varchar一个字符三个字节一个字节
int四个字节一个字节
tinyint一个字节一个字节

测试数据表如下:


CREATE TABLE `test_table` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`a` int(11) DEFAULT NOT NULL,
`b` int(11) DEFAULT NOT NULL,
`c` int(11) DEFAULT NOT NULL,
PRIMARY KEY (`id`),
KEY `test_table_a_b_c_index` (`a`,`b`,`c`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

命中索引:


mysql> explain select * from test_table where a = 1 and b = 2 and c = 3;
+----+-------------+------------+------------+------+------------------------+------------------------+---------+-------------------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys     | key          | key_len | ref        | rows | filtered | Extra    |
+----+-------------+------------+------------+------+------------------------+------------------------+---------+-------------------+------+----------+-------------+
| 1 | SIMPLE   | test_table | NULL    | ref | test_table_a_b_c_index | test_table_a_b_c_index | 12   | const,const,const |  1 |  100.00 | Using index |
+----+-------------+------------+------------+------+------------------------+------------------------+---------+-------------------+------+----------+-------------+

可以看到 key_len = 12,这是如何计算的呢?
因为字符集是 UTF8,一个字段占用四个字节,三个字段就是 4 * 3 = 12 字节。

是否允许为 NULL,如果允许为 NULL,则需要用额外的字节来标记该字段,不同的数据类型所需的字节大小不同。


mysql> ALTER TABLE `test_table` CHANGE `a` `a` INT(11) NULL;
mysql> ALTER TABLE `test_table` CHANGE `c` `c` INT(11) NULL;
mysql> ALTER TABLE `test_table` CHANGE `b` `b` INT(11) NULL;
mysql> explain select * from test_table where a = 1 and b = 2 and c = 3;
+----+-------------+------------+------------+------+------------------------+------------------------+---------+-------------------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys     | key          | key_len | ref        | rows | filtered | Extra    |
+----+-------------+------------+------------+------+------------------------+------------------------+---------+-------------------+------+----------+-------------+
| 1 | SIMPLE   | test_table | NULL    | ref | test_table_a_b_c_index | test_table_a_b_c_index | 15   | const,const,const |  1 |  100.00 | Using index |
+----+-------------+------------+------------+------+------------------------+------------------------+---------+-------------------+------+----------+-------------+

可以看到,当字段允许为空时,这时的key_len 变成了15 = 4 3 + 1 3(INT 类型为空时,额外占用一个字节)。

索引优化

有了这些基础知识之后,再来根据实际的SQL 判断索性性能好坏。

还是以上面那张数据表为例,为 a、b、c 三个字段创建联合索引。

SQL 语句是否索引
explain select * from test_table where a = 1 and b = 2 and c = 3;Extra:Using index key_len: 15
explain select * from test_table where a = 1 and b = 2 and c = 3 order by c;Extra:Using index key_len: 15
explain select * from test_table where b = 2 and c = 3;Extra:Using where; Using index key_len: 15
explain select * from test_table where a = 1 order by c;Extra:Using where; Using index; Using filesort key_len: 5
explain select * from test_table order by a, b, c;Extra:Using index key_len: 15
explain select * from test_table order by a, b, c desc;Extra:Using index; Using filesort key_len:15
explain select * from test_table where a in (1,2) and b in (1,2,3) and c = 1;Extra:Using where; Using index key_len: 15

通常在查看执行计划时, Extra 列为 Using index 则表示优化器使用了覆盖索引。

  • SQL1 可以使用覆盖索引,性能好

  • SQL2 可以使用覆盖索引,同时避免排序,性能好

  • SQL3 可以使用覆盖索引,但是需要根据 where 字句进行过滤

  • SQL4 可以使用部分索引 a,但无法避免排序,性能差

  • SQL5 可以完全使用覆盖索引,同时可以避免排序,性能好

  • SQL6 可以使用覆盖索引,但无法避免排序,(这是因为 MySQL InnoDB 创建索引时默认asc升序,索引无法自动倒序排序)

  • SQL7 可以使用覆盖索引,但是需要根据 where 子句进行过滤(非定值查询)

创建索引规范

  • 考虑到索引维护的成本,单张表的索引数量不超过 5 个,单个索引中的字段数不超过 5 个

  • 不在低基数列上建⽴索引,例如“性别”。 在低基数列上创建的索引查询相比全表扫描不一定有性能优势,特别是当存在回表成本时。

  • 合理创建联合索引,(a,b,c) 相当于 (a) 、(a,b) 、(a,b,c)。 

  • 合理使用覆盖索引减少IO,避免排序。

来源:https://segmentfault.com/a/1190000039355574

标签:MySQL,索引,设计,优化
0
投稿

猜你喜欢

  • YOLOv5改进之添加CBAM注意力机制的方法

    2023-07-22 20:48:52
  • Vue-cli创建项目从单页面到多页面的方法

    2024-05-21 10:17:04
  • 用CSS制作兼容多浏览量器的隐藏菜单

    2007-08-30 09:05:00
  • Python 调用PIL库失败的解决方法

    2023-01-25 02:47:08
  • Python报错:对象不存在此属性的解决

    2023-05-30 20:12:56
  • 给展示性图片增加提示工具条(黑白效果)

    2007-10-20 14:21:00
  • 解析Javascript中中括号“[]”的多义性

    2023-09-03 09:59:35
  • Python3如何将源目录中的图片用MD5命名并可以设定目标目录

    2023-11-28 10:33:17
  • JS实现倒计时图文效果

    2024-04-28 09:48:28
  • 网页优化之加速图片显示(CSS Sprite)

    2007-09-29 21:39:00
  • python爬取网页内容转换为PDF文件

    2023-04-29 10:53:12
  • python 实现图片特效处理

    2021-04-20 05:34:22
  • Python远程桌面协议RDPY安装使用介绍

    2023-11-06 05:37:23
  • Python参数解析器configparser简介

    2021-04-22 02:23:31
  • Python基础之函数与控制语句

    2021-02-02 16:22:49
  • 一文教你如何用Python轻轻松松操作Excel,Word,CSV

    2021-11-20 22:47:52
  • 解析ROC曲线绘制(python+sklearn+多分类)

    2021-04-06 12:16:16
  • Python 列表(list)的常用方法

    2022-05-04 19:05:20
  • 了解WEB页面工具语言XML(二)定义

    2008-09-05 17:18:00
  • Pytorch教程内置模型源码实现

    2022-09-04 12:58:50
  • asp之家 网络编程 m.aspxhome.com