-- 示例表 CREATE TABLE `employees` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(24) NOT NULL DEFAULT '' COMMENT '姓名', `age` int(20) NOT NULL DEFAULT '0' COMMENT '年齡', `position` varchar(20) NOT NULL DEFAULT '' COMMENT '職位', `hire_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '入職時間', PRIMARY KEY (`id`), KEY `idx_name_age_position` (`name`,`age`,`position`) USING BTREE, KEY `idx_age` (`age`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=136326 DEFAULT CHARSET=utf8 COMMENT='員工表' --建立100000條記錄 drop procedure if EXISTS insert_emp; delimiter ;; create procedure insert_emp() BEGIN declare i int; set i=1; while(i < 100000)DO INSERT INTO employees(name,age,position) values(CONCAT('xiaoqiang',i),i,'coder'); SET i=i+1; end WHILE; end;; delimiter ; call insert_emp();
select * from employees LIMIT 9999 ,5;
表示從表employees 中取出從10000行開始的5行記錄。看似只查詢5條記錄,實際這條SQL是先讀取10005條記錄,而後拋棄前10000條記錄,而後讀到後面5條想要的數據。沒有添加單獨的order by,表示經過主鍵排序。
所以要查詢一張大表比較靠後的數據,執行效率是很是低的。
由於主鍵是自增且連續的,因此能夠改寫成按照主鍵查詢從第10001開始的五行數據,以下:web
select * from employees WHERE id > 9999 limit 5;
能夠看到兩個sql的執行計劃,顯然改寫後的sql走了索引,並且掃描的行數大大減小,執行效率會更高。可是,這條改寫的sql在不少場景下並不實用,由於表中可能某些記錄被刪除後,主鍵空缺,致使結果不一致。
先刪除一條記錄,而後測試下原來sql和優化後的sql:sql
select * from employees LIMIT 9999 ,5;
select * from employees where id> 9999 limit 5;
兩條sql的結果不同,所以,若是主鍵不連續,不能使用上面描述的方法。
另外因爲原來sql是order by非主鍵字段,按照上面的方法改寫sql的結果不一致。因此這種改寫得知足如下兩個條件:編程
結果是按照主鍵排序的後端
select * from employees order by name limit 9000, 5;
explain select * from employees order by name limit 9000, 5;
key字段對應的值爲null,發現並無使用name字段的索引。由於掃描整個索引並查找到沒有索引的行,可能要便利多個索引樹,其成本比掃描全表的成本更高,索引優化器放棄使用索引。
優化的關鍵是:讓排序時返回的字段儘量的少,因此可讓排序和分頁操做先查出主鍵,而後根據主鍵查到對應的記錄。
改下以下:機器學習
select * from employees as e inner join(select id from employees order by name limit 9000,5) as ed on e.id=ed.id;
能夠看到結果與原來的sql結果是一致的,執行時間減小了通常以上,再對比下執行計劃:
原來的sql使用的是filesort排序,而優化後的sql使用的是索引排序。編程語言
原則:小表驅動大表,即小表的數據集驅動大表的數據集
in:當B表的數據集小於A表的數據集時,in因爲exists工具
select * from A where id in(select id from B) 等價於 for(select id from B){ select * from A where A.id=B.id }
exists:當A表的數據集小於B表的數據集時,exitsts優於in
當著查詢A的數據,放到子查詢B中作條件驗證,根據驗證結果(true或false)來決定著查詢的數據是否保留。性能
select * from A exists(select 1 from B where A.id=B.id) 等價於 for(select * from A){ select * from B where A.id=B.id }
explain select count(1) from employees; explain select count(id) from employees; explain select count(name) from employees; explain select count(*) from employees;
四個sql的執行計劃幾乎同樣的,count(name)使用的是聯合索引, 主要區別根據某個字段作count操做不會統計字段爲null的值的數據行。
除了count(name)的其餘count操做,都是用的輔助索引而不是主鍵索引, 由於二級索引存儲數據更少,檢索性能更高。學習
還沒關注個人公衆號?測試