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. 这是针对 "按应用 + 时间范围查询对话记录" 这类核心场景的性能优化,也是标注为 "游标查询核心索引" 的原因。
相关推荐
pixcarp5 小时前
知识库系统的内容资产闭环怎么设计
服务器·数据库·后端·golang
JosieBook5 小时前
【数据库】时序预测能力的分级进化:TimechoAI如何让每一类用户都能精准预见未来
java·开发语言·数据库
吴声子夜歌7 小时前
SQL经典实例——使用多张表
数据库·sql
倔强的石头_8 小时前
《Kingbase护城河》——深度解密数据库行锁冲突与等待事件架构
数据库
IT策士8 小时前
Redis 从入门到精通:性能调优与多语言客户端对比
数据库·redis·缓存
Bert.Cai9 小时前
Oracle INSTR函数详解
数据库·oracle
茉莉玫瑰花茶11 小时前
综合案例 - AI 智能租房助手 [ 5 ]
服务器·数据库·人工智能·python·ai
ywl47081208711 小时前
jwt生产token,简单版helloworld
java·数据库·spring
器灵科技11 小时前
AI视频工具实测:Seedance/可灵/HappyHorse谁最能打?
java·运维·数据库·人工智能·github
huangdong_11 小时前
京东商品图片视频批量下载与m3u8视频合并技术完整实现方案
大数据·前端·数据库