MySQL联合索引

java 复制代码
-- 对话历史表
create table chat_history
(
    id          bigint auto_increment comment 'id' primary key,
    message     text                               not null comment '消息',
    messageType varchar(32)                        not null comment 'user/ai',
    appId       bigint                             not null comment '应用id',
    userId      bigint                             not null comment '创建用户id',
    createTime  datetime default CURRENT_TIMESTAMP not null comment '创建时间',
    updateTime  datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间',
    isDelete    tinyint  default 0                 not null comment '是否删除',
    INDEX idx_appId (appId),                       -- 提升基于应用的查询性能
    INDEX idx_createTime (createTime),             -- 提升基于时间的查询性能
    INDEX idx_appId_createTime (appId, createTime) -- 游标查询核心索引
) comment '对话历史' collate = utf8mb4_unicode_ci;

你想了解在chat_history表中,联合索引idx_appId_createTime (appId, createTime)被标注为 "游标查询核心索引" 且能 "避免回表" 的具体含义,核心是搞清楚这个联合索引如何减少 / 消除查询时的 "回表" 操作,提升性能。

先理解核心概念:回表

MySQL 的 InnoDB 存储引擎中,索引分为两类:

  1. 聚簇索引(主键索引) :叶子节点直接存储整行数据(这是 InnoDB 表的默认特性,主键就是聚簇索引)。
  2. 二级索引(非主键索引,如普通索引 / 联合索引) :叶子节点只存储索引列的值 + 主键 id(不会存整行数据)。

当你执行查询时,如果:

  • 条件用到了二级索引(比如where appId=1);
  • 但查询的字段(select后的列)不在这个二级索引的列中;

数据库会先通过二级索引找到匹配记录的主键 id ,再拿着这个 id 去聚簇索引(主键索引)中查整行数据 ------ 这个 "先找 id,再查行" 的额外步骤,就是回表。回表会增加磁盘 IO,降低查询效率。

为什么这个联合索引能避免回表?

idx_appId_createTime (appId, createTime)联合二级索引 ,它的叶子节点存储的内容是:(appId值, createTime值, 主键id)(InnoDB 的二级索引默认附带主键)。

对于 "游标查询"(比如分页加载对话记录、按时间滚动加载),这类查询的典型场景是:

sql

sql 复制代码
-- 游标查询示例:按appId筛选,按createTime排序,分页获取记录
SELECT id, message, messageType, appId, createTime 
FROM chat_history 
WHERE appId = 123 AND createTime > '2026-01-01' 
ORDER BY createTime LIMIT 100;
场景 1:完全避免回表(覆盖索引)

如果你的查询字段全部包含在联合索引 + 主键中 (比如只查id, appId, createTime):

sql

sql 复制代码
SELECT id, appId, createTime 
FROM chat_history 
WHERE appId = 123 AND createTime > '2026-01-01' 
ORDER BY createTime;

此时数据库只需要扫描idx_appId_createTime这个索引,就能从叶子节点中直接拿到appId、createTime、id不需要回表(这就是 "覆盖索引" 特性)。

场景 2:减少回表次数(核心优化)

即使你要查messagemessageType等不在索引中的字段(必须回表),这个联合索引依然能极大优化性能:

  • 没有这个联合索引时:可能需要全表扫描,或先单查appId索引,再对结果按createTime排序(文件排序),最后批量回表;
  • 有这个联合索引时:数据库先通过appId+createTime快速定位到符合条件的主键 id(索引是有序的,无需额外排序),再用这些 id 批量回表 ------ 回表次数大幅减少,且避免了文件排序,这也是 "避免回表" 的核心价值(实际是减少回表的开销)。

总结

  1. 回表是二级索引查询时,需要通过主键再查整行数据的额外操作,会降低性能;
  2. 该联合索引利用覆盖索引 特性,对appId+createTime的游标查询(分页 / 滚动加载),要么完全避免回表,要么大幅减少回表次数;
  3. 这是针对 "按应用 + 时间范围查询对话记录" 这类核心场景的性能优化,也是标注为 "游标查询核心索引" 的原因。
相关推荐
Anastasiozzzz1 小时前
MySQL深分页问题与优化思路
数据库·mysql
伯明翰java1 小时前
数据库的操作
数据库
知识分享小能手5 小时前
PostgreSQL 入门学习教程,从入门到精通,PostgreSQL 16 语法知识点与案例详解(1)
数据库·学习·postgresql
康康的AI博客5 小时前
智能情感分析与品牌策略优化:如何通过AI洞察提升企业市场响应力
大数据·数据库·人工智能
Anastasiozzzz5 小时前
阿亮随手记:MySQL移除查询缓存、子查询优化深分页、自增主键溢出、索引失效
数据库·mysql·缓存
ppp今天又没打瓦6 小时前
围达梦数据库批量插入更新性能实测:普通表、一级分区与二级分区的对决
数据库
@insist1236 小时前
软考-数据库系统工程师-计算机体系结构与流水线核心考点解析
数据库·软考·数据系统工程师
可观测性用观测云6 小时前
KES(KingbaseES)数据库监控最佳实践
数据库
新缸中之脑7 小时前
在Reddit上探索未满足的需求
数据库·oracle
安当加密7 小时前
用 SMS 凭据管理系统替代 HashiCorp Vault:中小企业的轻量级 Secrets 管理实践
服务器·数据库·安全·阿里云