-- 单列索引示例 CREATE INDEX idx_user_email ON users(email); CREATE INDEX idx_order_date ON orders(order_date); CREATE INDEX idx_product_price ON products(price);
2. 复合索引(多列索引)
1 2 3
-- 复合索引示例 CREATE INDEX idx_user_age_city ON users(age, city); CREATE INDEX idx_order_user_date ON orders(user_id, order_date, status);
复合索引的最左前缀原则
1 2 3 4 5 6 7 8 9 10 11 12
-- 假设有复合索引:idx_abc(a, b, c)
-- 可以使用索引的查询 SELECT*FROMtableWHERE a =1; -- 使用索引 SELECT*FROMtableWHERE a =1AND b =2; -- 使用索引 SELECT*FROMtableWHERE a =1AND b =2AND c =3; -- 使用索引 SELECT*FROMtableWHERE a =1AND c =3; -- 部分使用索引(只用a)
-- 无法使用索引的查询 SELECT*FROMtableWHERE b =2; -- 不使用索引 SELECT*FROMtableWHERE c =3; -- 不使用索引 SELECT*FROMtableWHERE b =2AND c =3; -- 不使用索引
(三)按功能特性分类
1. 普通索引
1 2
-- 普通索引:没有任何限制 CREATE INDEX idx_user_name ON users(name);
2. 唯一索引
1 2 3
-- 唯一索引:保证列值的唯一性 CREATEUNIQUE INDEX idx_user_email ON users(email); CREATEUNIQUE INDEX idx_user_phone ON users(phone);
3. 主键索引
1 2 3 4 5 6
-- 主键索引:自动创建,唯一且非空 CREATETABLE users ( id INTPRIMARY KEY AUTO_INCREMENT, name VARCHAR(50), email VARCHAR(100) );
-- InnoDB中,主键就是聚簇索引 CREATETABLE users ( id INTPRIMARY KEY, -- 聚簇索引 name VARCHAR(50), age INT );
2. 非聚簇索引(Non-Clustered Index)
索引和数据分开存储
索引指向数据行的物理位置
一个表可以有多个非聚簇索引
1 2 3
-- 普通索引都是非聚簇索引 CREATE INDEX idx_user_name ON users(name); -- 非聚簇索引 CREATE INDEX idx_user_age ON users(age); -- 非聚簇索引
四、索引的创建与管理
(一)创建索引的语法
1. 基本创建语法
1 2 3 4 5 6 7 8 9 10 11
-- 基本语法 CREATE [UNIQUE] INDEX index_name ON table_name(column_name);
-- 创建普通索引 CREATE INDEX idx_user_age ON users(age);
-- 创建唯一索引 CREATEUNIQUE INDEX idx_user_email ON users(email);
-- 创建复合索引 CREATE INDEX idx_user_age_city ON users(age, city);
2. 在表创建时定义索引
1 2 3 4 5 6 7 8 9 10 11 12 13 14
CREATETABLE users ( id INTPRIMARY KEY AUTO_INCREMENT, name VARCHAR(50), email VARCHAR(100), age INT, city VARCHAR(50), created_at TIMESTAMPDEFAULTCURRENT_TIMESTAMP, -- 创建索引 INDEX idx_name (name), UNIQUE INDEX idx_email (email), INDEX idx_age_city (age, city), INDEX idx_created_at (created_at) );
3. 使用ALTER TABLE添加索引
1 2 3 4 5 6 7 8
-- 添加普通索引 ALTERTABLE users ADD INDEX idx_phone (phone);
-- 添加唯一索引 ALTERTABLE users ADDUNIQUE INDEX idx_id_card (id_card);
-- 添加复合索引 ALTERTABLE users ADD INDEX idx_status_date (status, created_at);
(二)删除索引
1. 删除索引的语法
1 2 3 4 5 6
-- 删除索引 DROP INDEX index_name ON table_name;
-- 示例 DROP INDEX idx_user_age ON users; DROP INDEX idx_user_email ON users;
2. 使用ALTER TABLE删除索引
1 2 3
-- 使用ALTER TABLE删除索引 ALTERTABLE users DROP INDEX idx_user_age; ALTERTABLE users DROP INDEX idx_user_email;
(三)查看索引信息
1. 查看表的索引
1 2 3 4 5 6 7 8 9
-- 查看表的所有索引 SHOW INDEX FROM users;
-- 查看索引详细信息 SHOW INDEX FROM users\G
-- 查看表结构(包含索引) DESC users; SHOWCREATETABLE users;
2. 查看索引使用情况
1 2 3 4 5 6 7 8 9 10 11
-- 查看索引统计信息 SELECT TABLE_SCHEMA, TABLE_NAME, INDEX_NAME, COLUMN_NAME, CARDINALITY, INDEX_TYPE FROM information_schema.STATISTICS WHERE TABLE_SCHEMA ='your_database' AND TABLE_NAME ='users';
3. 分析查询是否使用索引
1 2 3 4 5 6
-- 使用EXPLAIN分析查询计划 EXPLAIN SELECT*FROM users WHERE age =25; EXPLAIN SELECT*FROM users WHERE age >20AND city ='Beijing';
-- 详细的执行计划 EXPLAIN FORMAT=JSON SELECT*FROM users WHERE age =25;
五、索引优化策略
(一)索引设计原则
1. 选择合适的列创建索引
1 2 3 4 5 6 7 8 9 10 11 12
-- 适合创建索引的列 -- 1. 经常出现在WHERE子句中的列 CREATE INDEX idx_user_status ON users(status);
-- 2. 经常用于JOIN的列 CREATE INDEX idx_order_user_id ON orders(user_id);
-- 3. 经常用于ORDER BY的列 CREATE INDEX idx_order_date ON orders(order_date);
-- 4. 经常用于GROUP BY的列 CREATE INDEX idx_product_category ON products(category_id);
2. 避免过度索引
1 2 3 4 5 6 7 8 9 10
-- 不好的做法:为每个列都创建索引 CREATE INDEX idx_user_name ON users(name); CREATE INDEX idx_user_age ON users(age); CREATE INDEX idx_user_city ON users(city); CREATE INDEX idx_user_phone ON users(phone); CREATE INDEX idx_user_email ON users(email);
-- 更好的做法:根据查询模式创建复合索引 CREATE INDEX idx_user_age_city ON users(age, city); CREATEUNIQUE INDEX idx_user_email ON users(email);
-- 2. 使用不等于操作符 -- 不好: SELECT*FROM users WHERE status !='active'; -- 好: SELECT*FROM users WHERE status IN ('inactive', 'pending', 'suspended');
-- 3. 使用OR连接不同列 -- 不好: SELECT*FROM users WHERE name ='John'OR age =25; -- 好:使用UNION SELECT*FROM users WHERE name ='John' UNION SELECT*FROM users WHERE age =25;
-- 4. 模糊查询以通配符开头 -- 不好: SELECT*FROM users WHERE name LIKE'%John'; -- 好: SELECT*FROM users WHERE name LIKE'John%';
3. 范围查询优化
1 2 3 4 5 6 7 8 9 10
-- 复合索引中范围查询的影响 CREATE INDEX idx_user_age_city_status ON users(age, city, status);
-- 这个查询只能使用索引的age部分 SELECT*FROM users WHERE age >20AND city ='Beijing'AND status ='active';
-- 优化:调整索引列顺序 CREATE INDEX idx_user_city_status_age ON users(city, status, age); -- 现在可以充分利用索引 SELECT*FROM users WHERE city ='Beijing'AND status ='active'AND age >20;
-- 查看索引使用统计 SELECT OBJECT_SCHEMA, OBJECT_NAME, INDEX_NAME, COUNT_FETCH, COUNT_INSERT, COUNT_UPDATE, COUNT_DELETE FROM performance_schema.table_io_waits_summary_by_index_usage WHERE OBJECT_SCHEMA ='your_database' ORDERBY COUNT_FETCH DESC;
-- 查找未使用的索引 SELECT OBJECT_SCHEMA, OBJECT_NAME, INDEX_NAME FROM performance_schema.table_io_waits_summary_by_index_usage WHERE OBJECT_SCHEMA ='your_database' AND INDEX_NAME ISNOTNULL AND COUNT_FETCH =0 AND COUNT_INSERT =0 AND COUNT_UPDATE =0 AND COUNT_DELETE =0;
2. 索引碎片整理
1 2 3 4 5 6 7 8 9 10 11
-- 查看索引碎片情况 SHOWTABLE STATUS LIKE'users';
-- 重建索引(消除碎片) ALTERTABLE users ENGINE=InnoDB;
-- 或者使用OPTIMIZE TABLE OPTIMIZE TABLE users;
-- 重建特定索引 ALTERTABLE users DROP INDEX idx_user_age, ADD INDEX idx_user_age (age);
3. 索引统计信息更新
1 2 3 4 5 6 7 8 9 10
-- 更新表的统计信息 ANALYZE TABLE users;
-- 查看索引基数(不同值的数量) SHOW INDEX FROM users;
-- 手动更新统计信息 SET SESSION optimizer_switch='use_index_extensions=off'; ANALYZE TABLE users; SET SESSION optimizer_switch='use_index_extensions=on';
六、实际应用案例
(一)电商系统索引设计
1. 用户表索引设计
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
CREATETABLE users ( id BIGINTPRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOTNULL, email VARCHAR(100) NOTNULL, phone VARCHAR(20), status TINYINT DEFAULT1, created_at TIMESTAMPDEFAULTCURRENT_TIMESTAMP, updated_at TIMESTAMPDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP, -- 索引设计 UNIQUE INDEX idx_username (username), UNIQUE INDEX idx_email (email), INDEX idx_phone (phone), INDEX idx_status_created (status, created_at) );
2. 订单表索引设计
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
CREATETABLE orders ( id BIGINTPRIMARY KEY AUTO_INCREMENT, user_id BIGINTNOTNULL, order_no VARCHAR(32) NOTNULL, status TINYINT NOTNULL, total_amount DECIMAL(10,2) NOTNULL, created_at TIMESTAMPDEFAULTCURRENT_TIMESTAMP, updated_at TIMESTAMPDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMP, -- 索引设计 UNIQUE INDEX idx_order_no (order_no), INDEX idx_user_id (user_id), INDEX idx_status_created (status, created_at), INDEX idx_user_status_created (user_id, status, created_at), INDEX idx_created_at (created_at), FOREIGN KEY (user_id) REFERENCES users(id) );
3. 商品表索引设计
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
CREATETABLE products ( id BIGINTPRIMARY KEY AUTO_INCREMENT, name VARCHAR(200) NOTNULL, category_id INTNOTNULL, brand_id INT, price DECIMAL(10,2) NOTNULL, stock INTNOTNULLDEFAULT0, status TINYINT DEFAULT1, created_at TIMESTAMPDEFAULTCURRENT_TIMESTAMP, -- 索引设计 INDEX idx_category_price (category_id, price), INDEX idx_brand_price (brand_id, price), INDEX idx_status_stock (status, stock), INDEX idx_name (name), FULLTEXT INDEX idx_name_fulltext (name) );
-- 查询某用户的访问记录 SELECT*FROM access_logs WHERE user_id =12345 AND created_at >='2023-01-01' AND created_at <'2023-02-01' ORDERBY created_at DESC LIMIT 100;
-- 统计每小时的访问量 SELECT DATE_FORMAT(created_at, '%Y-%m-%d %H:00:00') ashour, COUNT(*) as access_count FROM access_logs WHERE created_at >='2023-01-01' AND created_at <'2023-01-02' GROUPBYhour ORDERBYhour;
-- 查找异常响应 SELECT ip_address, COUNT(*) as error_count FROM access_logs WHERE response_code >=400 AND created_at >= DATE_SUB(NOW(), INTERVAL1HOUR) GROUPBY ip_address HAVING error_count >10 ORDERBY error_count DESC;
-- 创建测试表 CREATETABLE test_users ( id INTPRIMARY KEY AUTO_INCREMENT, name VARCHAR(50), age INT, city VARCHAR(50), email VARCHAR(100), created_at TIMESTAMPDEFAULTCURRENT_TIMESTAMP );
-- 插入测试数据(100万条) DELIMITER // CREATEPROCEDURE insert_test_data() BEGIN DECLARE i INTDEFAULT1; WHILE i <=1000000 DO INSERTINTO test_users (name, age, city, email) VALUES ( CONCAT('User', i), FLOOR(18+ RAND() *50), CASEFLOOR(RAND() *5) WHEN0THEN'Beijing' WHEN1THEN'Shanghai' WHEN2THEN'Guangzhou' WHEN3THEN'Shenzhen' ELSE'Hangzhou' END, CONCAT('user', i, '@example.com') ); SET i = i +1; END WHILE; END// DELIMITER ;
-- 正确用法: SELECT*FROM orders WHERE created_at >='2023-01-01'AND created_at <'2023-01-02'; SELECT*FROM orders WHERE created_at >='2023-01-01'AND created_at <'2024-01-01';
3. 复合索引使用不当
1 2 3 4 5 6 7 8 9 10 11 12 13
-- 复合索引:idx_user_age_city(user_id, age, city) CREATE INDEX idx_user_age_city ON orders(user_id, age, city);
-- 可以使用索引的查询 SELECT*FROM orders WHERE user_id =1; -- ✓ SELECT*FROM orders WHERE user_id =1AND age =25; -- ✓ SELECT*FROM orders WHERE user_id =1AND age =25AND city ='Beijing'; -- ✓ SELECT*FROM orders WHERE user_id =1AND city ='Beijing'; -- ✓(部分使用)
-- 无法使用索引的查询 SELECT*FROM orders WHERE age =25; -- ✗ SELECT*FROM orders WHERE city ='Beijing'; -- ✗ SELECT*FROM orders WHERE age =25AND city ='Beijing'; -- ✗
-- 计算列的选择性(不同值的比例) SELECT COUNT(DISTINCT age) /COUNT(*) as age_selectivity, COUNT(DISTINCT city) /COUNT(*) as city_selectivity, COUNT(DISTINCT gender) /COUNT(*) as gender_selectivity FROM users;