当数据库表的数据量不断增长时,查询效率会变慢,以前查询数据 100 毫秒内就返回,现在可能需要几秒钟。
索引作为数据库优化查询的核心手段之一,能够大幅提升数据检索速度,就像书籍的目录一样,帮助数据库快速定位目标数据,避免全表扫描带来的性能损耗。
本文将从索引的基础概念出发,详细讲解SQL索引的分类、创建方法、管理技巧。
索引是数据库系统在表的基础上构建的一种有序数据结构,它将表中一个或多个列的值(索引键)与对应数据行的物理存储地址关联起来。当执行查询语句时,数据库引擎可以通过索引快速查找符合条件的索引键,进而定位到具体的数据行,而非遍历表中的所有记录。
注意:索引并非 “万能优化工具”,它能提升查询效率,但会增加数据插入、更新、删除(DML操作)的开销 —— 因为修改数据时,不仅要更新表数据,还需同步维护索引结构。因此,索引的设计需在查询性能与写入性能之间找到平衡。
(1) 提升查询速度
这是索引最核心的作用。对于百万级、千万级数据量的表,无索引查询可能需要几秒甚至几分钟,而合理的索引能将查询时间缩短至毫秒级。
(2)优化排序操作
当查询语句中包含 ORDER BY、GROUP BY 等排序/分组逻辑时,若索引键与排序/分组字段一致,数据库可直接利用索引的有序性完成排序,避免额外的排序运算。
(3)强制数据唯一性
通过唯一索引(如主键索引),可以约束索引键对应的列值不重复,保障数据的完整性。
(1)单列索引
指仅基于表中的一个列创建的索引,适用于查询条件中仅涉及该列的场景。例如,为用户表(user)的 “手机号” 列(phone)创建索引,可快速查询指定手机号的用户信息。
(2)复合索引
指基于表中的多个列组合创建的索引,索引键为多个列的值的组合。复合索引遵循 “最左前缀匹配原则” —— 即查询条件需从索引的第一个列开始匹配,否则无法有效利用索引。例如,为订单表(order)的 “用户ID”(user_id)和 “订单时间”(order_time)创建复合索引,查询条件为 “user_id = 1001 AND order_time > '2024-01-01'” 可利用索引,而仅以 “order_time > '2024-01-01'” 为条件则无法利用该索引。
(1)普通索引(INDEX)
最基础的索引类型,无任何约束限制,仅用于提升查询效率,一个表可以创建多个普通索引。
(2)唯一索引(UNIQUE)
索引键的值必须唯一(允许NULL值,且NULL值可出现多次,因NULL不等于任何值)。用于保障列数据的唯一性,同时提升查询效率。例如,为用户表的 “邮箱” 列创建唯一索引,避免重复注册。
(3)主键索引(PRIMARY KEY)
特殊的唯一索引,用于唯一标识表中的每一行数据。主键索引不允许 NULL 值,一个表只能有一个主键索引。创建主键时,数据库会自动创建对应的主键索引(如 MySQL 的 InnoDB 引擎中,主键索引为聚簇索引)。
(1)聚簇索引(Clustered Index)
索引结构与数据行本身存储在一起,索引的叶子节点就是数据行。MySQL 中, InnoDB 引擎的主键索引默认是聚簇索引;若表无主键,则会选择唯一索引作为聚簇索引;若既无主键也无唯一索引,InnoDB 会自动创建一个隐藏的聚簇索引。聚簇索引查询速度极快,因为找到索引就找到了数据。
(2)非聚簇索引(Non-Clustered Index)
索引结构与数据行分开存储,索引的叶子节点存储的是数据行的物理地址(或主键值),而非数据本身。查询时,需先通过非聚簇索引找到地址,再通过地址查找数据(即 “回表查询”)。普通索引、唯一索引(非主键)通常为非聚簇索引。
索引可以在创建数据表时进行创建,也可以在表创建成功后进行创建。
(1)创建表时创建索引,如下:
-- 示例:创建用户表(users),同步创建主键索引、唯一索引、普通索引 CREATE TABLE `users` ( `id` INT NOT NULL AUTO_INCREMENT COMMENT '用户ID', `phone` VARCHAR(20) NOT NULL COMMENT '手机号', `email` VARCHAR(50) NOT NULL COMMENT '邮箱', `username` VARCHAR(30) NOT NULL COMMENT '用户名', `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', -- 主键索引(自动创建) PRIMARY KEY (`id`), -- 唯一索引:保障邮箱唯一 UNIQUE INDEX idx_user_email (`email`), -- 普通索引:提升手机号查询效率 INDEX idx_user_phone (`phone`), -- 复合索引:提升用户ID+创建时间的查询效率 INDEX idx_user_id_createtime (`id`, `create_time`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
(2)表创建后单独创建索引,语法如下:
CREATE [索引类型] INDEX [索引名] ON [表名] ([列名1], [列名2], ...);
示例:
-- 1. 创建普通索引:为user表的username列创建索引 CREATE INDEX idx_user_username ON `users` (`username`); -- 2. 创建唯一索引:为user表的phone列创建唯一索引(若已存在普通索引,需先删除) CREATE UNIQUE INDEX idx_user_phone_unique ON `users` (`phone`); -- 3. 创建复合索引:为order表的user_id和order_time列创建复合索引 CREATE INDEX idx_order_userid_ordertime ON `orders` (`user_id`, `order_time`);
删除索引需要使用 DROP INDEX 语句,语法如下:
DROP INDEX [索引名] ON [表名];
示例:
-- 删除user表的idx_user_username索引 DROP INDEX idx_user_username ON `users`; -- 删除order表的复合索引idx_order_userid_ordertime DROP INDEX idx_order_userid_ordertime ON `order`;