假設有一張訂單表 order,主要包含了主鍵訂單編碼 order_no、訂單狀態 status、提交時間 create_time 等列,並且創建了 status 列索引和 create_time 列索引。此時通過創建時間降序獲取狀態爲 1 的訂單編碼,以下是具體實現代碼:
select order_no from order where status =1 order by create_time desc
你知道其中的問題所在嗎?我們又該如何優化?
實踐
1. 造數據
創建表order01,主鍵索引,status,create_time 索引
CREATE TABLE `order01` (
`oder_no` bigint(0) NOT NULL AUTO_INCREMENT,
`status` bigint(0) NULL DEFAULT NULL,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`create_time` datetime(0) NULL DEFAULT NULL,
PRIMARY KEY (`oder_no`) USING BTREE,
INDEX `idx_status`(`status`) USING BTREE,
INDEX `idx_create_time`(`create_time`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
創建表order02,主鍵索引,status和create_time 聯合索引
CREATE TABLE `order02` (
`id` bigint(0) NOT NULL AUTO_INCREMENT,
`status` bigint(0) NULL DEFAULT NULL,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`create_time` datetime(0) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_status_create_time`(`status`, `create_time`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
隨機插入10000條數據,方便測試
DROP PROCEDURE IF EXISTS proc_initData;--如果存在此存儲過程則刪掉
DELIMITER $
CREATE PROCEDURE proc_initData()
BEGIN
DECLARE i INT DEFAULT 1;
WHILE i<=10000 DO
insert into order01(STATUS,name,create_time) VALUES(RAND()*10000000,RAND()*10000000,now());
SET i = i+1;
END WHILE;
END $
CALL proc_initData();
執行完,我手動把1000條數據的status值置爲1,方便測試。
2. 查看執行計劃
EXPLAIN select * from `order01` WHERE STATUS = 1 ORDER BY create_time;
EXPLAIN select * from `order02` WHERE STATUS = 1 ORDER BY create_time;
執行時間對比
3. 結論
status和create_time單獨建索引,在查詢時只會遍歷status索引對數據進行過濾,不會用到create_time列索引,將符合條件的數據返回到server層,在server層對數據通過快排算法進行排序,Extra列會出現filesort;
應該利用索引的有序性,在status和creat_time列建立聯合索引,這樣根據status過濾後的數據就是按照create_time排好序的,避免在server層排序。