Mysql 索引

索引是一种数据结构,能够帮助我们快速的检索数据库中的数据,常见的MySQL主要有两种结构:Hash索引和B+ Tree索引,我们使用的是InnoDB引擎,默认的是B+树。

B+ Tree索引和Hash索引区别

  • 哈希表是一种以key-value存储数据的结构,所以多个数据在存储关系上是完全没有任何顺序关系的,所以,对于区间查询是无法直接通过索引查询的,就需要全表扫描。所以,哈希索引只适用于等值查询的场景。哈希索引适合等值查询,但是不无法进行范围查询
  • 而B+ Tree是一种多路平衡查询树,所以他的节点是天然有序的(左子节点小于父节点、父节点小于右子节点),所以对于范围查询的时候不需要做全表扫描。

  • 哈希索引不支持多列联合索引的最左匹配规则, 如果有大量重复键值得情况下,哈希索引的效率会很低,因为存在哈希碰撞问题

最左前缀匹配

在创建多列索引时,根据业务需求,where子句中使用最频繁的一列放在最左边,因为MySQL索引查询会遵循最左前缀匹配的原则,即最左优先,在检索数据时从联合索引的最左边开始匹配。所以当我们创建一个联合索引的时候,如(key1,key2,key3),相当于创建了(key1)、(key1,key2)和(key1,key2,key3)三个索引,这就是最左匹配原则。

索引优化

explain查看sql语句的执行计划,通过执行计划来分析索引使用情况

  1. where后条件匹配的索引列越多扫描的数据将越少,比如组合索引(a,b,c),最好在where后面能同时用到索引上的a,b,c这三列
  2. 避免再次排序
    简单来说,就是排序字段尽量使用索引字段,因为索引默认是排好序的,使用索引字段排序可以避免再次排序
  3. 索引行包含查询语句中所有的列,即覆盖索引
    基于这一点,我们应该少用select*来查询,以增加覆盖索引的可能性
    如果你的索引能集齐上述三颗星,则说明你的索引是最优的索引!

覆盖索引举例

覆盖索引(Covering Index)是数据库优化中的一个概念,指的是一个索引包含了数据库查询中需要的所有数据。换句话说,查询可以仅通过访问索引来获取所需的数据,而无需再去访问表中的数据行。这通常可以显著提高查询性能。

举个例子,假设我们有一个简单的用户表 users,它包含以下列:

  • id(用户的唯一标识)
  • username(用户名)
  • email(电子邮件地址)
  • last_login(最后登录时间)

现在,如果我们经常执行以下查询来获取用户的 usernameemail

SELECT username, email FROM users WHERE username = 'johndoe';

如果没有适当的索引,数据库可能需要扫描整个 users 表来查找匹配 username = 'johndoe' 的行,然后返回 usernameemail 字段的值。

为了优化这个查询,我们可以创建一个覆盖索引,包含查询中涉及的所有列(在这个例子中是 usernameemail)。例如:

CREATE INDEX idx_username_email ON users (username, email);

这个索引 idx_username_email 包含了 usernameemail 这两列。当我们执行上面的查询时,数据库可以直接使用这个索引来找到 johndoeusernameemail,而不需要再去访问表中的数据行。因为索引通常比完整的表数据结构更小,所以它可以更快地加载到内存中,加速查询速度。

在这个例子中,索引 idx_username_email 就是一个覆盖索引,因为它"覆盖"了查询中所需的所有列。

实际的sql优化例子

-- 用户表
CREATE TABLE `t_user`  (
  `id` bigint(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `group_id` bigint(20) DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `idx_name`(`name`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1240277101395107842 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic;

-- 分组表

CREATE TABLE `t_group`  (
  `id` bigint(20) NOT NULL,
  `group_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic;

insert INTO t_group values(1, '小组1'),(2, '小组2');

update t_group set group_name = '小组2' where id = 2;

insert INTO t_user values(1, '小三',18,1),(2, '小四',18,2),(3, '小四',19,2),(4, '刘四',40,2),(5, '其其',32,1),(6, '刘能',30,1);

Order by 字段应该在where中,否则出现Using filesort

explain select * from t_user where age = 18 and group_id = 1 order by name;
-- Using filesort . 当order by 无法利用索引完成排序时,优化器不得不选择合适的算法从内存或者磁盘进行排序

image

explain select * from t_user where age = 18 and group_id = 1 order by group_id;
image

在你提供的两个查询中,都包含了相同的过滤条件(age = 18group_id = 1),但排序条件不同。第一个查询按 name 排序,而第二个查询按 group_id 排序。为了优化这两个查询,我们可以考虑在 t_user 表上创建索引。

对于第一个查询:

SELECT * FROM t_user WHERE age = 18 AND group_id = 1 ORDER BY name;

为了使这个查询高效,我们可以创建一个复合索引,首先包含过滤条件中的列 agegroup_id,然后是排序条件中的列 name

CREATE INDEX idx_age_group_id_name ON t_user (age, group_id, name);

这个索引将允许数据库快速定位 age 为 18 并且 group_id 为 1 的所有行,并且因为 name 也包含在索引中,所以可以直接按照 name 进行排序,而无需额外的排序操作。
创建索引后,Using index
image

组合索引跨列导致索引不用

image

参考文档:

posted @ 2024-04-29 11:30  肥梁  阅读(19)  评论(0)    收藏  举报