MySQL联合索引遇到范围查询

MySQL联合索引遇到范围查询

1、环境准备

复制代码
-- 查看MySQL版本
SELECT VERSION();
​
-- MySQL 5.7 联合索引范围查询测试脚本
-- 联合索引字段顺序:tenant_id, created_at, status
-- created_at 位于联合索引中间,用于 BETWEEN、>、<、>=、<= 等范围查询。
CREATE TABLE IF NOT EXISTS order_index_test (
    id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键ID',
    order_no VARCHAR(32) NOT NULL COMMENT '订单号',
    tenant_id BIGINT UNSIGNED NOT NULL COMMENT '租户ID',
    created_at DATETIME NOT NULL COMMENT '创建时间',
    status TINYINT UNSIGNED NOT NULL COMMENT '状态:1待支付,2已支付,3已关闭',
    amount DECIMAL(10, 2) NOT NULL COMMENT '订单金额',
    PRIMARY KEY (id),
    UNIQUE KEY uk_order_no (order_no),
    KEY idx_tenant_created_status (tenant_id, created_at, status)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4
  COLLATE = utf8mb4_general_ci
  COMMENT = '联合索引范围查询测试表';
​
-- 插入50条测试数据。
INSERT INTO order_index_test
    (order_no, tenant_id, created_at, status, amount)
VALUES
    ('ORD202607010001', 1001, '2026-07-01 09:10:00', 1,  99.00),
    ('ORD202607010002', 1001, '2026-07-01 10:20:00', 2, 128.50),
    ('ORD202607020003', 1001, '2026-07-02 08:30:00', 1,  59.90),
    ('ORD202607020004', 1001, '2026-07-02 11:40:00', 3, 399.00),
    ('ORD202607030005', 1001, '2026-07-03 09:15:00', 2, 219.80),
    ('ORD202607030006', 1001, '2026-07-03 14:25:00', 1,  35.00),
    ('ORD202607040007', 1001, '2026-07-04 07:50:00', 2, 499.99),
    ('ORD202607040008', 1001, '2026-07-04 16:05:00', 3,  88.80),
    ('ORD202607050009', 1001, '2026-07-05 10:10:00', 1, 129.00),
    ('ORD202607050010', 1001, '2026-07-05 18:30:00', 2, 268.00),
    ('ORD202607060011', 1001, '2026-07-06 08:45:00', 1,  45.60),
    ('ORD202607060012', 1001, '2026-07-06 13:15:00', 3, 688.00),
    ('ORD202607070013', 1001, '2026-07-07 09:35:00', 2, 178.90),
    ('ORD202607070014', 1001, '2026-07-07 15:55:00', 1,  76.20),
    ('ORD202607080015', 1001, '2026-07-08 10:05:00', 2, 340.00),
    ('ORD202607080016', 1001, '2026-07-08 19:20:00', 3,  29.90),
    ('ORD202607090017', 1001, '2026-07-09 08:10:00', 1, 108.00),
    ('ORD202607090018', 1001, '2026-07-09 12:40:00', 2, 508.50),
    ('ORD202607100019', 1001, '2026-07-10 09:00:00', 1,  66.60),
    ('ORD202607100020', 1001, '2026-07-10 17:10:00', 3, 199.99),
    ('ORD202607110021', 1001, '2026-07-11 08:20:00', 2, 320.00),
    ('ORD202607110022', 1001, '2026-07-11 14:45:00', 1,  55.50),
    ('ORD202607120023', 1001, '2026-07-12 10:30:00', 2, 780.00),
    ('ORD202607130024', 1001, '2026-07-13 16:20:00', 3,  89.00),
    ('ORD202607140025', 1001, '2026-07-14 11:15:00', 1, 145.80),
​
    ('ORD202607010026', 1002, '2026-07-01 09:25:00', 2, 210.00),
    ('ORD202607020027', 1002, '2026-07-02 13:35:00', 1,  75.00),
    ('ORD202607030028', 1002, '2026-07-03 08:55:00', 3, 430.00),
    ('ORD202607040029', 1002, '2026-07-04 12:10:00', 2,  98.90),
    ('ORD202607050030', 1002, '2026-07-05 17:45:00', 1, 156.00),
    ('ORD202607060031', 1002, '2026-07-06 10:20:00', 2, 620.50),
    ('ORD202607070032', 1002, '2026-07-07 14:30:00', 3,  40.00),
    ('ORD202607080033', 1002, '2026-07-08 09:40:00', 1, 188.80),
    ('ORD202607090034', 1002, '2026-07-09 18:05:00', 2, 275.00),
    ('ORD202607100035', 1002, '2026-07-10 07:35:00', 1,  68.00),
    ('ORD202607110036', 1002, '2026-07-11 11:50:00', 3, 399.90),
    ('ORD202607120037', 1002, '2026-07-12 15:10:00', 2, 125.00),
    ('ORD202607130038', 1002, '2026-07-13 09:45:00', 1,  49.90),
    ('ORD202607140039', 1002, '2026-07-14 13:25:00', 2, 580.00),
    ('ORD202607150040', 1002, '2026-07-15 16:40:00', 3, 230.00),
​
    ('ORD202607010041', 1003, '2026-07-01 08:15:00', 1,  39.90),
    ('ORD202607020042', 1003, '2026-07-02 12:25:00', 2, 149.00),
    ('ORD202607030043', 1003, '2026-07-03 16:35:00', 3, 299.00),
    ('ORD202607040044', 1003, '2026-07-04 10:45:00', 1,  79.80),
    ('ORD202607050045', 1003, '2026-07-05 14:55:00', 2, 450.00),
    ('ORD202607060046', 1003, '2026-07-06 19:05:00', 1,  90.00),
    ('ORD202607070047', 1003, '2026-07-07 11:15:00', 3, 520.00),
    ('ORD202607080048', 1003, '2026-07-08 15:25:00', 2, 170.50),
    ('ORD202607090049', 1003, '2026-07-09 09:35:00', 1,  65.00),
    ('ORD202607100050', 1003, '2026-07-10 13:45:00', 2, 310.00);
    
    
-- 验证数据总数,应返回50。
SELECT COUNT(*) AS total_rows
FROM order_index_test;
​
-- 查看索引定义。
SHOW INDEX FROM order_index_test;

2、执行计划

2.1 等值查询

复制代码
EXPLAIN
SELECT id, order_no, tenant_id, created_at, status, amount
FROM order_index_test
WHERE tenant_id = 1001
  AND created_at = '2026-07-03 00:00:00'            
  AND status = 1;

执行计划:

关键字段:

key: idx_tenant_created_status

key_len: 14

Extra: Using index condition

2.2 > < 左开右开查询

复制代码
EXPLAIN 
SELECT id, order_no, tenant_id, created_at, status, amount
FROM order_index_test
WHERE tenant_id = 1001
  AND created_at > '2026-07-03 00:00:00'
  AND created_at <  '2026-07-11 00:00:00'
  AND status = 1;

执行计划:

关键字段:

key: idx_tenant_created_status

key_len: 13

Extra: Using index condition

2.3 >, <= 左开右闭查询

复制代码
EXPLAIN 
SELECT id, order_no, tenant_id, created_at, status, amount
FROM order_index_test
WHERE tenant_id = 1001
  AND created_at > '2026-07-03 00:00:00'
  AND created_at <=  '2026-07-11 00:00:00'
  AND status = 1;

执行计划:

关键字段:

key: idx_tenant_created_status

key_len: 14

Extra: Using index condition

2.4 >= , < 左闭右开查询

复制代码
EXPLAIN
SELECT id, order_no, tenant_id, created_at, status, amount
FROM order_index_test
WHERE tenant_id = 1001
  AND created_at >= '2026-07-03 00:00:00'
  AND created_at <  '2026-07-11 00:00:00'
  AND status = 1;

执行计划:

关键字段:

key: idx_tenant_created_status

key_len: 14

Extra: Using index condition

2.5 >= , <= 左闭右闭查询

复制代码
EXPLAIN 
SELECT id, order_no, tenant_id, created_at, status, amount
FROM order_index_test
WHERE tenant_id = 1001
  AND created_at >= '2026-07-03 00:00:00'
  AND created_at <=  '2026-07-11 00:00:00'
  AND status = 1;

执行计划:

关键字段:

key: idx_tenant_created_status

key_len: 14

Extra: Using index condition

2.6 between and 查询

复制代码
EXPLAIN
SELECT id, order_no, tenant_id, created_at, status, amount
FROM order_index_test
WHERE tenant_id = 1001
  AND created_at BETWEEN '2026-07-03 00:00:00' AND '2026-07-11 00:00:00'
  AND status = 1;

执行计划:

关键字段:

key: idx_tenant_created_status

key_len: 14

Extra: Using index condition

3、总结

  1. BETWEEN><>=<= 都是范围条件,都可以使用索引。

  2. 联合索引遇到中间列的范围查询后,范围列本身仍然使用索引。

  3. 范围列后面的字段通常不能像前置等值字段一样缩小整个连续扫描区间。

  4. 后续字段仍可能参与范围边界计算,并通过 ICP 在索引层过滤,所以不能简单地说"范围查询后面的索引全部失效"。

  5. key_len=14 不代表 status 对范围内所有记录都完成了精准定位,还需要结合 Using index condition 和扫描行数判断。

范围查询不会让联合索引后面的列完全失效,但会限制后续列继续缩小连续扫描范围;后续列可能参与边界计算和 ICP 过滤,是否真正高效需要结合数据量和执行计划判断。

相关推荐
敲个大西瓜6 小时前
Redis一百道核心面试题
数据库·redis·缓存
無a伟9 小时前
Redis持久化详解:RDB与AOF原理、配置、优缺点
数据库
小罗水10 小时前
附录A 各微服务完整 application.yml 配置汇总
数据库·elasticsearch·微服务
muddjsv10 小时前
SQLite 外键进阶:CASCADE / SET NULL / 级联更新与完整性校验
数据库·sqlite
APItesterCris10 小时前
Open Claw 实战教程:5 分钟搭建京东商品自动化监控与数据分析系统
大数据·运维·数据库·数据仓库·自动化
Nturmoils11 小时前
查库存的 SQL 时灵时不灵,最后发现是 WHERE 里两个函数在打架
数据库
不想纳尼的青春12 小时前
where = 的作用?会影响性能吗?count(*) 和 count()哪个快?
数据库·oracle
倔强的石头_12 小时前
Spring Boot 接入金仓数据库:配置分层、启动自检与常见错误
数据库
杨云龙UP12 小时前
生产环境Oracle表空间扩容实战:使用Toad for Oracle图形界面新增Datafile(ASM+OMF)
linux·运维·数据库·oracle·表空间·asm 存储管理·toad