MySQL 性能優化技巧(一)

概述

這裏是記錄一些本人在看書或是開發過程中遇到的一些數據庫的性能優化問題,希望與君共勉。


版權說明

著作權歸作者所有。
商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。
本文作者:Coding-Naga
發表日期: 2016年4月5日
本文鏈接:http://blog.csdn.net/lemon_tree12138/article/details/51062548
來源:CSDN
更多內容:分類 >> 數據庫


奇技淫巧

0. 提前終止查詢

這一點可能注意過的人並不多,我現在假設我的數據表中有一label字段,假設我現在查詢的值在數據表中只有一份,而且比較靠前。SQL語句如下:

SELECT * FROM labels WHERE label='iwpfdc';

查詢的結果如下:

+----+--------+
| id | label  |
+----+--------+
| 25 | iwpfdc |
+----+--------+
1 row in set (22.13 sec)

查詢上面的結果,竟然耗費了22秒!這是一個恐怖的事情。而且可以看到id只有25,說明,這是一個比較靠前的數據。這是可怕的,因爲考慮到這條數據可能只有一條。可是,上面的查詢語句會對整個數據表進行全表掃描。爲了防止全表掃描,我們可以在查到了一條數據時,就中止查詢。所以這裏我們可以使用LIMIT 1來提前終止查詢。

SELECT * FROM labels WHERE label='iwpfdc' LIMIT 1;

現在來看結果:

+----+--------+
| id | label  |
+----+--------+
| 25 | iwpfdc |
+----+--------+
1 row in set (0.10 sec)

只有0.1秒了,時間是可觀的。不過這還不是最佳處理,還有一些更好的操作請繼續閱讀。


1. 爲搜索字段創建索引

這一點很重要,在平常的項目開發中,爲搜索字段創建索引是必不可少的。不過我們了不能爲了創建索引而創建索引,也不要爲所有的列都創建索引,畢竟數據庫在對索引的維護上也是有時間消耗的。下面暫且只說明索引帶來的好處。
還是上面的數據庫,在對label列創建完索引之後,再一次查詢時,結果有了明顯地提升。
查詢SQL語句

select * from labels where label='yjrplewcft';

查詢結果

+----+------------+
| id | label      |
+----+------------+
| 35 | yjrplewcft |
+----+------------+
1 row in set (0.11 sec)

上面是在進行“全表”掃描的結果,雖然如此,可是速度還是很快。下面就第1條優化建議進行查詢,看看結果。
查詢SQL語句

SELECT * FROM labels WHERE label='yjrplewcft' LIMIT 1;

查詢結果

+----+------------+
| id | label      |
+----+------------+
| 35 | yjrplewcft |
+----+------------+
1 row in set (0.03 sec)

這裏只用了0.03秒就完成了查詢。由此可見,索引和提前終止查詢在實際應用中的重要性。


2. 使用EXPLAIN

EXPLAIN在對SQL語句的優化上有指導性的意義,這很重要。比如下面的這條SQL語句:

EXPLAIN SELECT * FROM labels WHERE label='yjrplewcft' LIMIT 1;

下面是EXPLAIN的結果:

+----+-------------+--------+------------+------+---------------+--------------+---------+-------+------+----------+-------------+
| id | select_type | table  | partitions | type | possible_keys | key          | key_len | ref   | rows | filtered | Extra       |
+----+-------------+--------+------------+------+---------------+--------------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | labels | NULL       | ref  | index_labels  | index_labels | 92      | const |    1 |   100.00 | Using index |
+----+-------------+--------+------------+------+---------------+--------------+---------+-------+------+----------+-------------+
1 row in set, 1 warning (0.15 sec)

從上面可以很直觀地看到,type、rows、filtered、Extra等列的數據都是最佳的,由此可得出此條查詢語句是一條性能比較好的SQL語句。
本人在《MySQL多表查詢核心優化》文章中也有一些具體的實戰示例,感興趣的同學可以進行參考。


3. 避免返回不需要的列

其實在實際的查詢中很少有可能需要查詢數據表中所有的列,那麼就需要有選擇地進行選擇字段。所以在你的項目裏請避免使用 SELECT *
從數據庫裏讀出越多的數據,那麼查詢就會變得越慢。尤其在需要經過網絡傳輸時,這就會大大地增加網絡傳輸的負載。
所以,在實際的開發過程中,還是隻查詢自己當前需要的東西,避免使用 SELECT * 吧。


4. 儘可能地使用 NOT NULL

這一點對於新手來說可能也不是太瞭解,本人也是最近才瞭解到這一點。因爲在MySQL中對NULL字段處理的特殊性,致使我們需要對NULL字段進行格外關照。
首先,我們要知道在MySQL中,NULL值並非我們一般意義上的空值。他是佔用空間的,可以通過下面的查詢瞭解:

SELECT LENGTH(''), LENGTH(' '), LENGTH(NULL);
+------------+-------------+--------------+
| LENGTH('') | LENGTH(' ') | LENGTH(NULL) |
+------------+-------------+--------------+
|          0 |           1 |         NULL |
+------------+-------------+--------------+
1 row in set (0.00 sec)

其次,雖然NULL佔用了空間,可是MySQL卻並不爲NULL字段創建索引。


5. 儘可能地使用定長的字段

這一點要怎麼理解呢?如果你學習或是瞭解過操作系統中的地址偏移量的概念,那麼這個技巧你應該會很快就理解了。
固定長度的表會提高性能,MySQL在搜尋的時候也會更快一些。因爲這些固定的長度是很容易就可以計算下一個數據的偏移量,所以讀取的自然也會很快。而如果字段不是定長的,那麼,每一次要找下一條的話,需要程序先找到主鍵。


6. 不要以字符串定義IP地址

IP地址的形式一般就是這樣的:127.100.100.101。而根據上面第5點的技巧,我們也可以很快對這一字段有一個很好的設計,就像這樣:

ip VARCHAR(15) NOT NULL

是的,你看已經對字段進行了定長的設計,這個很合理呀。可是,如果你瞭解IP地址是可以進行整型轉換的話,那麼這樣的設計顯然有點遜色了。我們可以將這個字段定義成UNSIGNED INT或是LONG
將IP定義成整型的好處在於,方便地對其創建索引,還可以方便地進行範圍查詢:

... WHERE ip BETWEEN ip1 AND ip2

這一點在實際的項目開發中使用得還是比較多的。


7. 儘量避免使用ORDER BY

在MySQL中,對結果進行排序是很耗時間的。我們還可是通過一個實例來說明會更加直觀一些。

SELECT * FROM labels ORDER BY label LIMIT 200000, 5;
+---------+------------------------+
| id      | label                  |
+---------+------------------------+
| 3182819 | anjmlhwztqbu           |
| 1654768 | anjmlrf                |
| 6719238 | anjmopcfeydwkubtqhgxlr |
| 9301930 | anjmq                  |
| 3042587 | anjmqhxbzyrkpvswlto    |
+---------+------------------------+
SELECT * FROM labels LIMIT 200000, 5;
+---------+------------------------+
| id      | label                  |
+---------+------------------------+
| 3182819 | anjmlhwztqbu           |
| 1654768 | anjmlrf                |
| 6719238 | anjmopcfeydwkubtqhgxlr |
| 9301930 | anjmq                  |
| 3042587 | anjmqhxbzyrkpvswlto    |
+---------+------------------------+
5 rows in set (0.08 sec)

所以,還是在不需要排序的時候避免使用ORDER BY吧。


8. 大量數據的情況下,儘量使用分頁查詢

這一條其實與第3條很類似,一個是從縱向的角度切分,一個是從橫向的角度進行切分。對於分頁的使用在第7條中也有實例應用,可以參見。


9. 在要求不是很苛刻的情況下,可以使用max(id)代替count(*)

對於存在大量數據的情況下,如果我們需要查詢當前數據表中有多少條記錄,這個要怎麼做呢?
很容易想到的是使用COUNT()函數來完成,可是對於數據量很大的情況下,這裏的查詢會很慢。因爲我們在設計數據表的時候,一般會爲數據表設計一個id字段,這個字段會隨着數據的添加而自增。所以,如果我們對於查詢結果只是想有一個粗略地瞭解的話,我們完全可以採用MAX()函數查詢。下面就是實際的應用:
COUNT()

SELECT COUNT(*) FROM labels;

查詢結果

+----------+
| COUNT(*) |
+----------+
| 10534997 |
+----------+
1 row in set (5.04 sec)

MAX(id)

SELECT MAX(id) FROM labels;

查詢結果

+----------+
| MAX(id)  |
+----------+
| 10537190 |
+----------+
1 row in set (0.00 sec)

可以看到這裏的查詢結果是有很明顯地懸殊的。所以如果你對查詢結果並沒有很嚴格的要求的話,建議採用MAX(id)來查詢。


Ref

  • 《高性能MySQL(第3版).Baron.Scbwartz》
  • 《MySQL技術內幕 InnoDB存儲引擎 第2版》
  • http://www.bitscn.com/pdb/mysql/201007/188342.html

相關資源下載


徵集

如果你也需要使用ProcessOn這款在線繪圖工具,可以使用如下邀請鏈接進行註冊:
https://www.processon.com/i/56205c2ee4b0f6ed10838a6d

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章