以下的測試數據根據環境的不同所耗費的時間有所不同,例如我在騰訊雲上的測試,以及在本機的測試速度相差三倍。但當環境因素一樣的時候,優化sql能夠帶來顯著的提升。 測試表數據:3百萬條數據,12個欄位,存儲引擎:myisam 1 確定搜索一條記錄的時候,加上limit 1 ,以此讓游標查到第一條結果時停 ...
以下的測試數據根據環境的不同所耗費的時間有所不同,例如我在騰訊雲上的測試,以及在本機的測試速度相差三倍。但當環境因素一樣的時候,優化sql能夠帶來顯著的提升。
測試表數據:3百萬條數據,12個欄位,存儲引擎:myisam
1 確定搜索一條記錄的時候,加上limit 1 ,以此讓游標查到第一條結果時停止,不需要遍歷下麵的結果
使用這樣子數據遞增的varchar欄位,在最優情況下,結果在第一條:
SQL : select * from tbl_game_user_test where channel= 'jieduan1';
TIME : 1.292 s
而當我們加上了limit 1 :
SQL : select * from tbl_game_user_test where channel= 'jieduan1' limit 1;
TIME : 0.0001 s
對比上面,提高了很多。而在最壞情況下,結果在最後一條,那麼兩者使用時間是一致的。
而當我們explain的時候,兩者是一致的:
+----+-------------+--------------------+------+---------------+------+---------+------+---------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+--------------------+------+---------------+------+---------+------+---------+-------------+
| 1 | SIMPLE | tbl_game_user_test | ALL | NULL | NULL | NULL | NULL | 3006343 | Using where |
+----+-------------+--------------------+------+---------------+------+---------+------+---------+-------------+
2 like查詢以%開頭索引會失效,沒有命中索引,查詢效率差別是很大的。而%在結尾,可以命中索引
未優化的sql:
SQL:select * from tbl_game_user_test where deviceID LIKE '%deviceID1111111'
TIME : 2.342 s
而當我們將%放到末尾:
SQL : select * from tbl_game_user_test where deviceID LIKE 'deviceID1111111%';
TIME : 0.001 s
花費的時間相差挺大
3 對於分頁的優化,可以採用覆蓋索引的原理來優化。
在數據量不大的時候,limit 分頁效率差別不大,當數據量大的時候,數據越大,效率差距越大,因此我們可以採用覆蓋索引的原理,來優化sql。
未優化前SQL:
SELECT * FROM tbl_game_user_test ORDER BY id LIMIT 1000000,10
Times : 4.55 s
而優化的SQL:
select * from tbl_game_user_test JOIN (SELECT id FROM tbl_game_user_test ORDER BY id LIMIT 100000,10)a USING(id)
Times : 0.014s
我們把第二種檢索方法叫做延遲關聯,因為延遲了對列的訪問。在查詢的第一階段MySQL可以使用覆蓋索引,在FROM字句的查詢中找到匹配的id,然後根據這些id值在外層查詢匹配獲取需要的列值
上述的語句,我們也可以這麼寫:
select * from tbl_game_user_test where id >=(SELECT id FROM tbl_game_user_test ORDER BY id LIMIT 100000,1) ORDER BY id limit 10
4 永遠為每張表設置一個ID
我們應該為資料庫里的每張表都設置一個ID做為其主鍵,而且最好的是一個INT型的(推薦使用UNSIGNED),並設置上自動增加的 AUTO_INCREMENT標誌。
就算是你 users 表有一個主鍵叫 “email”的欄位,你也別讓它成為主鍵。使用 VARCHAR 類型來當主鍵會使用得性能下降。另外,在你的程式中,你應該使用表的ID來構造你的數據結構。
而且,在MySQL數據引擎下,還有一些操作需要使用主鍵,在這些情況下,主鍵的性能和設置變得非常重要,比如,集群,分區……
5 合適的存儲引擎
InnoDB和MyISAM是許多人在使用MySQL時最常用的兩個表類型,這兩個表類型各有優劣,視具體應用而定。基本的差別為:MyISAM類型不支持事務處理等高級處理,而InnoDB類型支持。MyISAM類型的表強調的是性能,其執行數度比InnoDB類型更快,但是不提供事務支持,而InnoDB提供事務支持以及外部鍵等高級資料庫功能。
同樣的情況下,執行相同的查詢,myisam 比innodb 快接近4倍;
存儲引擎 : myisam SQL :select * from tbl_game_user_test where channel = 'jieduan65'; time : 1.2 s
而 : innodb 是 4.6s 【平均】
6 Mysql SQL Mode 可以禁止截斷保存操作
當我們對一個varchar(3)的欄位進行更新操作:
update tbl_game_user_test set platform = 'jieduanTest' where id = 1;
我們發現插入的字元被自動截斷了,
存入的是:
jie
出現這種情況往往會發生一些意料之外的事情,等到發生了,再來找問題,再擴大欄位長度就比較晚。我們可以在長度超過限制就報錯,通過報錯來提前報警。
可以通過:
set session sql_mode='STRICT_TRANS_TABLES'
就會提示:
[Err] 1406 - Data too long for column 'platform' at row 1
7 負向條件查詢不能使用索引
select * from order where status!=0 and stauts!=1
not in/not exists都不是好習慣
可以優化為in查詢:
select * from order where status in(2,3)
8 優化order by 語句
在某些情況中,MySQL 可以使用一個索引來滿足ORDER BY 子句,而不需要額外的排序。where 條件和order by 使用相同的索引,並且order by 的順序和索引順序相同,並且order by 的欄位都是升序或者都是降序。
例如:下列sql 可以使用索引。
SELECT * FROM t1 ORDER BY key_part1,key_part2,... ;
SELECT * FROM t1 WHERE key_part1=1 ORDER BY key_part1 DESC, key_part2 DESC;
SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 DESC;
但是以下情況不使用索引:
SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 ASC; --order by 的欄位混合ASC 和DESC
SELECT * FROM t1 WHERE key2=constant ORDER BY key1; --用於查詢行的關鍵字與ORDER BY 中所使用的不相同
SELECT * FROM t1 ORDER BY key1, key2; --對不同的關鍵字使用ORDER BY:
其他優化小技巧:
1 字元欄位只建首碼索引
2 導入大數據量時,先刪除索引,導入數據,再創建索引
3 在設計表的時候,可以按照定長欄位與變長欄位相分離,分開兩個表存放。例如論壇標題是我們經常獲取的,而論壇個人創建時間等,幾乎是不會去看,這種我們可以放於幾乎不查的表中。同時,我們也可以把較長內容的欄位獨立存放於一個表中。
4 冗餘欄位。很多時候我們設計表都要遵守第一,二,三範式,但有時我們為了加快查詢,而應該添加冗餘欄位。例如論壇欄目表中,一般而言,我們會有個欄目表,有文章表,而當我們獲取該欄目有多少篇文章的時候,聯表查詢顯得稍費時間,而我們可以在欄目表添加一個文章數量欄位,每次添加或者刪除都更新一下該欄位。