今天,數據庫的操做愈來愈成爲整個應用的性能瓶頸了,這點對於Web應用尤爲明顯。關於數據庫的性能,這並不僅是DBA才須要擔憂的事,而這更是咱們程序員須要去關注的事情。當咱們去設計數據庫表結構,對操做數據庫時(尤爲是查表時的SQL語句),咱們都須要注意數據操做的性能。這裏,咱們不會講過多的SQL語句的優化,而只是針對MySQL這一Web應用最多的數據庫。但願下面的這些優化技巧對你有用。php
1. 爲查詢緩存優化你的查詢mysql
大多數的MySQL服務器都開啓了查詢緩存。這是提升性最有效的方法之一,並且這是被MySQL的數據庫引擎處理的。當有不少相同的查詢被執行了屢次的時候,這些查詢結果會被放到一個緩存中,這樣,後續的相同的查詢就不用操做表而直接訪問緩存結果了。程序員
這裏最主要的問題是,對於程序員來講,這個事情是很容易被忽略的。由於,咱們某些查詢語句會讓MySQL不使用緩存。請看下面的示例:redis
上面兩條SQL語句的差異就是 CURDATE() ,MySQL的查詢緩存對這個函數不起做用。因此,像 NOW() 和 RAND()sql
或是其它的諸如此類的SQL函數都不會開啓查詢緩存,由於這些函數的返回是會不定的易變的。因此,你所須要的就是用一個變量來代替MySQL的函數,從而開啓緩存。數據庫
2. EXPLAIN 你的 SELECT 查詢緩存
使用 EXPLAIN 關鍵字可讓你知道MySQL是如何處理你的SQL語句的。這能夠幫你分析你的查詢語句或是表結構的性能瓶頸。安全
EXPLAIN 的查詢結果還會告訴你你的索引主鍵被如何利用的,你的數據表是如何被搜索和排序的……等等,等等。服務器
挑一個你的SELECT語句(推薦挑選那個最複雜的,有多表聯接的),把關鍵字EXPLAIN加到前面。你可使用phpmyadmin來作這個事。而後,你會看到一張表格。下面的這個示例中,咱們忘記加上了group_id索引,而且有表聯接:網絡
當咱們爲 group_id 字段加上索引後:
咱們能夠看到,前一個結果顯示搜索了 7883 行,然後一個只是搜索了兩個表的 9 和 16 行。查看rows列可讓咱們找到潛在的性能問題。
3. 當只要一行數據時使用 LIMIT 1
當你查詢表的有些時候,你已經知道結果只會有一條結果,但由於你可能須要去fetch遊標,或是你也許會去檢查返回的記錄數。
在這種狀況下,加上 LIMIT 1 能夠增長性能。這樣同樣,MySQL數據庫引擎會在找到一條數據後中止搜索,而不是繼續日後查少下一條符合記錄的數據。
下面的示例,只是爲了找一下是否有「中國」的用戶,很明顯,後面的會比前面的更有效率。(請注意,第一條中是Select *,第二條是Select 1)
4. 爲搜索字段建索引
索引並不必定就是給主鍵或是惟一的字段。若是在你的表中,有某個字段你總要會常常用來作搜索,那麼,請爲其創建索引吧。
從上圖你能夠看到那個搜索字串 「last_name LIKE ‘a%’」,一個是建了索引,一個是沒有索引,性能差了4倍左右。
另外,你應該也須要知道什麼樣的搜索是不能使用正常的索引的。例如,當你須要在一篇大的文章中搜索一個詞時,如: 「WHERE
post_content LIKE ‘%apple%’」,索引多是沒有意義的。你可能須要使用MySQL全文索引
或是本身作一個索引(好比說:搜索關鍵詞或是Tag什麼的)
5. 在Join表的時候使用至關類型的例,並將其索引
若是你的應用程序有不少 JOIN 查詢,你應該確認兩個表中Join的字段是被建過索引的。這樣,MySQL內部會啓動爲你優化Join的SQL語句的機制。
並且,這些被用來Join的字段,應該是相同的類型的。例如:若是你要把 DECIMAL 字段和一個 INT 字段Join在一塊兒,MySQL就沒法使用它們的索引。對於那些STRING類型,還須要有相同的字符集才行。(兩個表的字符集有可能不同)
6. 千萬不要 ORDER BY RAND()
想打亂返回的數據行?隨機挑一個數據?真不知道誰發明了這種用法,但不少新手很喜歡這樣用。但你確不瞭解這樣作有多麼可怕的性能問題。
若是你真的想把返回的數據行打亂了,你有N種方法能夠達到這個目的。這樣使用只讓你的數據庫的性能呈指數級的降低。這裏的問題是:MySQL會不得不去執行RAND()函數(很耗CPU時間),並且這是爲了每一行記錄去記行,而後再對其排序。就算是你用了Limit1也無濟於事(由於要排序)
下面的示例是隨機挑一條記錄
7. 避免 SELECT *
從數據庫裏讀出越多的數據,那麼查詢就會變得越慢。而且,若是你的數據庫服務器和WEB服務器是兩臺獨立的服務器的話,這還會增長網絡傳輸的負載。
因此,你應該養成一個須要什麼就取什麼的好的習慣。
8. 永遠爲每張表設置一個ID
咱們應該爲數據庫裏的每張表都設置一個ID作爲其主鍵,並且最好的是一個INT型的(推薦使用UNSIGNED),並設置上自動增長的AUTO_INCREMENT標誌。
就算是你 users 表有一個主鍵叫 「email」的字段,你也別讓它成爲主鍵。使用 VARCHAR 類型來當主鍵會使用得性能降低。另外,在你的程序中,你應該使用表的ID來構造你的數據結構。
並且,在MySQL數據引擎下,還有一些操做須要使用主鍵,在這些狀況下,主鍵的性能和設置變得很是重要,好比,集羣,分區……
在這裏,只有一個狀況是例外,那就是「關聯表」的「外鍵」,也就是說,這個表的主鍵,經過若干個別的表的主鍵構成。咱們把這個狀況叫作「外鍵」。好比:有一個「學生表」有學生的ID,有一個「課程表」有課程ID,那麼,「成績表」就是「關聯表」了,其關聯了學生表和課程表,在成績表中,學生ID和課程ID叫「外鍵」其共同組成主鍵。
9. 使用 ENUM 而不是 VARCHAR
ENUM 類型是很是快和緊湊的。在實際上,其保存的是 TINYINT,但其外表上顯示爲字符串。這樣一來,用這個字段來作一些選項列表變得至關的完美。
若是你有一個字段,好比「性別」,「國家」,「民族」,「狀態」或「部門」,你知道這些字段的取值是有限並且固定的,那麼,你應該使用 ENUM 而不是 VARCHAR。
MySQL也有一個「建議」(見第十條)告訴你怎麼去從新組織你的表結構。當你有一個 VARCHAR 字段時,這個建議會告訴你把其改爲 ENUM 類型。使用 PROCEDURE ANALYSE() 你能夠獲得相關的建議。
10. 從 PROCEDURE ANALYSE() 取得建議
PROCEDURE ANALYSE() 會讓 MySQL 幫你去分析你的字段和其實際的數據,並會給你一些有用的建議。只有表中有實際的數據,這些建議纔會變得有用,由於要作一些大的決定是須要有數據做爲基礎的。
例如,若是你建立了一個 INT 字段做爲你的主鍵,然而並無太多的數據,那麼,PROCEDURE
ANALYSE()會建議你把這個字段的類型改爲 MEDIUMINT 。或是你使用了一個 VARCHAR
字段,由於數據很少,你可能會獲得一個讓你把它改爲 ENUM 的建議。這些建議,都是可能由於數據不夠多,因此決策作得就不夠準。
在phpmyadmin裏,你能夠在查看錶時,點擊 「Propose table structure」 來查看這些建議
必定要注意,這些只是建議,只有當你的表裏的數據愈來愈多時,這些建議纔會變得準確。必定要記住,你纔是最終作決定的人。
11. 儘量的使用 NOT NULL
除非你有一個很特別的緣由去使用 NULL 值,你應該老是讓你的字段保持 NOT NULL。這看起來好像有點爭議,請往下看。
首先,問問你本身「Empty」和「NULL」有多大的區別(若是是INT,那就是0和NULL)?若是你以爲它們之間沒有什麼區別,那麼你就不要使用NULL。(你知道嗎?在 Oracle 裏,NULL 和 Empty 的字符串是同樣的!)
不要覺得 NULL 不須要空間,其須要額外的空間,而且,在你進行比較的時候,你的程序會更復雜。 固然,這裏並非說你就不能使用NULL了,現實狀況是很複雜的,依然會有些狀況下,你須要使用NULL值。
12. Prepared Statements
Prepared Statements很像存儲過程,是一種運行在後臺的SQL語句集合,咱們能夠從使用 prepared statements 得到不少好處,不管是性能問題仍是安全問題。
Prepared Statements
能夠檢查一些你綁定好的變量,這樣能夠保護你的程序不會受到「SQL注入式」攻擊。固然,你也能夠手動地檢查你的這些變量,然而,手動的檢查容易出問題,並且很常常會被程序員忘了。當咱們使用一些framework或是ORM的時候,這樣的問題會好一些。
在性能方面,當一個相同的查詢被使用屢次的時候,這會爲你帶來可觀的性能優點。你能夠給這些Prepared Statements定義一些參數,而MySQL只會解析一次。
雖然最新版本的MySQL在傳輸Prepared Statements是使用二進制形勢,因此這會使得網絡傳輸很是有效率。
固然,也有一些狀況下,咱們須要避免使用Prepared Statements,由於其不支持查詢緩存。但聽說版本5.1後支持了。
在PHP中要使用prepared statements,你能夠查看其使用手冊:mysqli 擴展 或是使用數據庫抽象層,如: PDO.
13. 無緩衝的查詢
正常的狀況下,當你在當你在你的腳本中執行一個SQL語句的時候,你的程序會停在那裏直到沒這個SQL語句返回,而後你的程序再往下繼續執行。你可使用無緩衝查詢來改變這個行爲。
mysql_unbuffered_query()
發送一個SQL語句到MySQL而並不像mysql_query()同樣去自動fethch和緩存結果。這會至關節約不少可觀的內存,尤爲是那些會產生大量結果的查詢語句,而且,你不須要等到全部的結果都返回,只須要第一行數據返回的時候,你就能夠開始立刻開始工做於查詢結果了。
然而,這會有一些限制。由於你要麼把全部行都讀走,或是你要在進行下一次的查詢前調用 mysql_free_result()
清除結果。並且, mysql_num_rows() 或 mysql_data_seek()
將沒法使用。因此,是否使用無緩衝的查詢你須要仔細考慮。
14. 把IP地址存成 UNSIGNED INT
不少程序員都會建立一個 VARCHAR(15)
字段來存放字符串形式的IP而不是整形的IP。若是你用整形來存放,只須要4個字節,而且你能夠有定長的字段。並且,這會爲你帶來查詢上的優點,尤爲是當你須要使用這樣的WHERE條件:IP
between ip1 and ip2。
咱們必須要使用UNSIGNED INT,由於 IP地址會使用整個32位的無符號整形。
而你的查詢,你可使用 INET_ATON() 來把一個字符串IP轉成一個整形,並使用 INET_NTOA() 把一個整形轉成一個字符串IP。在PHP中,也有這樣的函數 ip2long() 和 long2ip()。
15. 固定長度的表會更快
若是表中的全部字段都是「固定長度」的,整個表會被認爲是 「static」 或 「fixed-length」。
例如,表中沒有以下類型的字段:
VARCHAR,TEXT,BLOB。只要你包括了其中一個這些字段,那麼這個表就不是「固定長度靜態表」了,這樣,MySQL
引擎會用另外一種方法來處理。
固定長度的表會提升性能,由於MySQL搜尋得會更快一些,由於這些固定的長度是很容易計算下一個數據的偏移量的,因此讀取的天然也會很快。而若是字段不是定長的,那麼,每一次要找下一條的話,須要程序找到主鍵。
而且,固定長度的表也更容易被緩存和重建。不過,惟一的反作用是,固定長度的字段會浪費一些空間,由於定長的字段不管你用不用,他都是要分配那麼多的空間。
使用「垂直分割」技術(見下一條),你能夠分割你的表成爲兩個一個是定長的,一個則是不定長的。
16. 垂直分割
「垂直分割」是一種把數據庫中的表按列變成幾張表的方法,這樣能夠下降表的複雜度和字段的數目,從而達到優化的目的。(之前,在銀行作過項目,見過一張表有100多個字段,很恐怖)
示例一:在Users表中有一個字段是家庭地址,這個字段是可選字段,相比起,並且你在數據庫操做的時候除了我的信息外,你並不須要常常讀取或是改寫這個字段。那麼,爲何不把他放到另一張表中呢?
這樣會讓你的表有更好的性能,你們想一想是否是,大量的時候,我對於用戶表來講,只有用戶ID,用戶名,口令,用戶角色等會被常用。小一點的表老是會有好的性能。
示例二: 你有一個叫 「last_login」
的字段,它會在每次用戶登陸時被更新。可是,每次更新時會致使該表的查詢緩存被清空。因此,你能夠把這個字段放到另外一個表中,這樣就不會影響你對用戶ID,用戶名,用戶角色的不停地讀取了,由於查詢緩存會幫你增長不少性能。
另外,你須要注意的是,這些被分出去的字段所造成的表,你不會常常性地去Join他們,否則的話,這樣的性能會比不分割時還要差,並且,會是極數級的降低。
17. 拆分大的 DELETE 或 INSERT 語句
若是你須要在一個在線的網站上去執行一個大的 DELETE 或 INSERT 查詢,你須要很是當心,要避免你的操做讓你的整個網站中止相應。由於這兩個操做是會鎖表的,表一鎖住了,別的操做都進不來了。
Apache 會有不少的子進程或線程。因此,其工做起來至關有效率,而咱們的服務器也不但願有太多的子進程,線程和數據庫連接,這是極大的佔服務器資源的事情,尤爲是內存。
若是你把你的表鎖上一段時間,好比30秒鐘,那麼對於一個有很高訪問量的站點來講,這30秒所積累的訪問進程/線程,數據庫連接,打開的文件數,可能不只僅會讓你泊WEB服務Crash,還可能會讓你的整臺服務器立刻掛了。
因此,若是你有一個大的處理,你定你必定把其拆分,使用 LIMIT 條件是一個好的方法。下面是一個示例:
18. 越小的列會越快
對於大多數的數據庫引擎來講,硬盤操做多是最重大的瓶頸。因此,把你的數據變得緊湊會對這種狀況很是有幫助,由於這減小了對硬盤的訪問。
參看 MySQL 的文檔 Storage Requirements 查看全部的數據類型。
若是一個表只會有幾列罷了(好比說字典表,配置表),那麼,咱們就沒有理由使用 INT 來作主鍵,使用 MEDIUMINT, SMALLINT 或是更小的 TINYINT 會更經濟一些。若是你不須要記錄時間,使用 DATE 要比 DATETIME 好得多。
固然,你也須要留夠足夠的擴展空間,否則,你往後來幹這個事,你會死的很難看,參看Slashdot的例子(2009年11月06日),一個簡單的ALTER TABLE語句花了3個多小時,由於裏面有一千六百萬條數據。
19. 選擇正確的存儲引擎
在 MySQL 中有兩個存儲引擎 MyISAM 和 InnoDB,每一個引擎都有利有弊。酷殼之前文章《MySQL: InnoDB 仍是 MyISAM?》討論和這個事情。
MyISAM
適合於一些須要大量查詢的應用,但其對於有大量寫操做並非很好。甚至你只是須要update一個字段,整個表都會被鎖起來,而別的進程,就算是讀進程都沒法操做直到讀操做完成。另外,MyISAM
對於 SELECT COUNT(*) 這類的計算是超快無比的。
InnoDB 的趨勢會是一個很是複雜的存儲引擎,對於一些小的應用,它會比 MyISAM 還慢。他是它支持「行鎖」 ,因而在寫操做比較多的時候,會更優秀。而且,他還支持更多的高級應用,好比:事務。
下面是MySQL的手冊
target=」_blank」MyISAM Storage Engine
InnoDB Storage Engine
20. 使用一個對象關係映射器(Object Relational Mapper)
使用 ORM (Object Relational Mapper),你可以得到可靠的性能增漲。一個ORM能夠作的全部事情,也能被手動的編寫出來。可是,這須要一個高級專家。
ORM 的最重要的是「Lazy Loading」,也就是說,只有在須要的去取值的時候纔會去真正的去作。但你也須要當心這種機制的反作用,由於這頗有可能會由於要去建立不少不少小的查詢反而會下降性能。
ORM 還能夠把你的SQL語句打包成一個事務,這會比單獨執行他們快得多得多。
目前,我的最喜歡的PHP的ORM是:Doctrine。
21. 當心「永久連接」
「永久連接」的目的是用來減小從新建立MySQL連接的次數。當一個連接被建立了,它會永遠處在鏈接的狀態,就算是數據庫操做已經結束了。並且,自從咱們的Apache開始重用它的子進程後——也就是說,下一次的HTTP請求會重用Apache的子進程,並重用相同的
MySQL 連接。
PHP手冊:mysql_pconnect()
在理論上來講,這聽起來很是的不錯。可是從我的經驗(也是大多數人的)上來講,這個功能製造出來的麻煩事更多。由於,你只有有限的連接數,內存問題,文件句柄數,等等。
並且,Apache 運行在極端並行的環境中,會建立不少不少的了進程。這就是爲何這種「永久連接」的機制工做地很差的緣由。在你決定要使用「永久連接」以前,你須要好好地考慮一下你的整個系統的架構。
TechTarget中國原創內容,原文連接:http://www.searchdatabase.com.cn/showcontent_38045.htm
基本配置
你須要常常察看如下3個配置項。否則,可能很快就會出問題。
innodb_buffer_pool_size:這是你安裝完InnoDB後第一個應該設置的選項。緩衝池是數據和索引緩存的地方:這個值越大越好,這能保證你在大多數的讀取操做時使用的是內存而不是硬盤。典型的值是5-6GB(8GB內存),20-25GB(32GB內存),100-120GB(128GB內存)。
innodb_log_file_size:這是redo日誌的大小。redo日誌被用於確保寫操做快速而可靠而且在崩潰時恢復。一直到MySQL 5.1,它都難於調整,由於一方面你想讓它更大來提升性能,另外一方面你想讓它更小來使得崩潰後更快恢復。幸運的是從MySQL 5.5以後,崩潰恢復的性能的到了很大提高,這樣你就能夠同時擁有較高的寫入性能和崩潰恢復性能了。一直到MySQL 5.5,redo日誌的總尺寸被限定在4GB(默承認以有2個log文件)。這在MySQL 5.6裏被提升。
一開始就把innodb_log_file_size設置成512M(這樣有1GB的redo日誌)會使你有充裕的寫操做空間。若是你知道你的應用程序須要頻繁的寫入數據而且你使用的時MySQL 5.6,你能夠一開始就把它這是成4G。
max_connections:若是你常常看到‘Too many connections'錯誤,是由於max_connections的值過低了。這很是常見由於應用程序沒有正確的關閉數據庫鏈接,你須要比默認的151鏈接數更大的值。max_connection值被設高了(例如1000或更高)以後一個主要缺陷是當服務器運行1000個或更高的活動事務時會變的沒有響應。在應用程序裏使用鏈接池或者在MySQL裏使用進程池有助於解決這一問題。
InnoDB配置
從MySQL 5.5版本開始,InnoDB就是默認的存儲引擎而且它比任何其餘存儲引擎的使用都要多得多。那也是爲何它須要當心配置的緣由。
innodb_file_per_table:這項設置告知InnoDB是否須要將全部表的數據和索引存放在共享表空間裏(innodb_file_per_table = OFF) 或者爲每張表的數據單獨放在一個.ibd文件(innodb_file_per_table = ON)。每張表一個文件容許你在drop、truncate或者rebuild表時回收磁盤空間。這對於一些高級特性也是有必要的,好比數據壓縮。可是它不會帶來任何性能收益。你不想讓每張表一個文件的主要場景是:有很是多的表(好比10k+)。
MySQL 5.6中,這個屬性默認值是ON,所以大部分狀況下你什麼都不須要作。對於以前的版本你必需在加載數據以前將這個屬性設置爲ON,由於它只對新建立的表有影響。
innodb_flush_log_at_trx_commit:默認值爲1,表示InnoDB徹底支持ACID特性。當你的主要關注點是數據安全的時候這個值是最合適的,好比在一個主節點上。可是對於磁盤(讀寫)速度較慢的系統,它會帶來很巨大的開銷,由於每次將改變flush到redo日誌都須要額外的fsyncs。將它的值設置爲2會致使不太可靠(reliable)由於提交的事務僅僅每秒才flush一次到redo日誌,但對於一些場景是能夠接受的,好比對於主節點的備份節點這個值是能夠接受的。若是值爲0速度就更快了,但在系統崩潰時可能丟失一些數據:只適用於備份節點。
innodb_flush_method: 這項配置決定了數據和日誌寫入硬盤的方式。通常來講,若是你有硬件RAID控制器,而且其獨立緩存採用write-back機制,並有着電池斷電保護,那麼應該設置配置爲O_DIRECT;不然,大多數狀況下應將其設爲fdatasync(默認值)。sysbench是一個能夠幫助你決定這個選項的好工具。
innodb_log_buffer_size: 這項配置決定了爲還沒有執行的事務分配的緩存。其默認值(1MB)通常來講已經夠用了,可是若是你的事務中包含有二進制大對象或者大文本字段的話,這點緩存很快就會被填滿並觸發額外的I/O操做。看看Innodb_log_waits狀態變量,若是它不是0,增長innodb_log_buffer_size。
其餘設置
query_cache_size: query cache(查詢緩存)是一個衆所周知的瓶頸,甚至在併發並很少的時候也是如此。 最佳選項是將其從一開始就停用,設置query_cache_size = 0(如今MySQL 5.6的默認值)並利用其餘方法加速查詢:優化索引、增長拷貝分散負載或者啓用額外的緩存(好比memcache或redis)。若是你已經爲你的應用啓用了query cache而且尚未發現任何問題,query cache可能對你有用。這是若是你想停用它,那就得當心了。
log_bin:若是你想讓數據庫服務器充當主節點的備份節點,那麼開啓二進制日誌是必須的。若是這麼作了以後,還別忘了設置server_id爲一個惟一的值。就算只有一個服務器,若是你想作基於時間點的數據恢復,這(開啓二進制日誌)也是頗有用的:從你最近的備份中恢復(全量備份),並應用二進制日誌中的修改(增量備份)。二進制日誌一旦建立就將永久保存。因此若是你不想讓磁盤空間耗盡,你能夠用 PURGE BINARY LOGS 來清除舊文件,或者設置 expire_logs_days 來指定過多少天日誌將被自動清除。
記錄二進制日誌不是沒有開銷的,因此若是你在一個非主節點的複製節點上不須要它的話,那麼建議關閉這個選項。
skip_name_resolve:當客戶端鏈接數據庫服務器時,服務器會進行主機名解析,而且當DNS很慢時,創建鏈接也會很慢。所以建議在啓動服務器時關閉skip_name_resolve選項而不進行DNS查找。惟一的侷限是以後GRANT語句中只能使用IP地址了,所以在添加這項設置到一個已有系統中必須格外當心。
Ref:
http://www.jb51.net/article/47419.htm
1.儘可能少 join
MySQL 的優點在於簡單,但這在某些方面其實也是其劣勢。MySQL 優化器效率高,可是因爲其統計信息的量有限,優化器工做過程出現誤差的可能性也就更多。對於複雜的多表 Join,一方面因爲其優化器受限,再者在 Join 這方面所下的功夫還不夠,因此性能表現離 Oracle 等關係型數據庫前輩仍是有必定距離。但若是是簡單的單表查詢,這一差距就會極小甚至在有些場景下要優於這些數據庫前輩。
2.儘可能少排序
排序操做會消耗較多的 CPU 資源,因此減小排序能夠在緩存命中率高等 IO 能力足夠的場景下會較大影響 SQL 的響應時間。
對於MySQL來講,減小排序有多種辦法,好比:
上面誤區中提到的經過利用索引來排序的方式進行優化
減小參與排序的記錄條數
非必要不對數據進行排序
…
3.儘可能避免 select *
不少人看到這一點後以爲比較難理解,上面不是在誤區中剛剛說 select 子句中字段的多少並不會影響到讀取的數據嗎?
是的,大多數時候並不會影響到 IO 量,可是當咱們還存在 order by 操做的時候,select 子句中的字段多少會在很大程度上影響到咱們的排序效率,這一點能夠經過我以前一篇介紹 MySQL ORDER BY 的實現分析的文章中有較爲詳細的介紹。
此外,上面誤區中不是也說了,只是大多數時候是不會影響到 IO 量,當咱們的查詢結果僅僅只須要在索引中就能找到的時候,仍是會極大減小 IO 量的。
4.儘可能用 join 代替子查詢
雖然 Join 性能並不佳,可是和 MySQL 的子查詢比起來仍是有很是大的性能優點。MySQL 的子查詢執行計劃一直存在較大的問題,雖然這個問題已經存在多年,可是到目前已經發布的全部穩定版本中都廣泛存在,一直沒有太大改善。雖然官方也在很早就認可這一問題,而且承諾儘快解決,可是至少到目前爲止咱們尚未看到哪個版本較好的解決了這一問題。
5.儘可能少 or
當 where 子句中存在多個條件以「或」並存的時候,MySQL 的優化器並無很好的解決其執行計劃優化問題,再加上 MySQL 特有的 SQL 與 Storage 分層架構方式,形成了其性能比較低下,不少時候使用 union all 或者是union(必要的時候)的方式來代替「or」會獲得更好的效果。
6.儘可能用 union all 代替 union
union 和 union all 的差別主要是前者須要將兩個(或者多個)結果集合並後再進行惟一性過濾操做,這就會涉及到排序,增長大量的 CPU 運算,加大資源消耗及延遲。因此當咱們能夠確認不可能出現重複結果集或者不在意重複結果集的時候,儘可能使用 union all 而不是 union。
7.儘可能早過濾
這一優化策略其實最多見於索引的優化設計中(將過濾性更好的字段放得更靠前)。
在 SQL 編寫中一樣可使用這一原則來優化一些 Join 的 SQL。好比咱們在多個表進行分頁數據查詢的時候,咱們最好是可以在一個表上先過濾好數據分好頁,而後再用分好頁的結果集與另外的表 Join,這樣能夠儘量多的減小沒必要要的 IO 操做,大大節省 IO 操做所消耗的時間。
8.避免類型轉換
這裏所說的「類型轉換」是指 where 子句中出現 column 字段的類型和傳入的參數類型不一致的時候發生的類型轉換:
人爲在column_name 上經過轉換函數進行轉換
直接致使 MySQL(實際上其餘數據庫也會有一樣的問題)沒法使用索引,若是非要轉換,應該在傳入的參數上進行轉換
由數據庫本身進行轉換
若是咱們傳入的數據類型和字段類型不一致,同時咱們又沒有作任何類型轉換處理,MySQL 可能會本身對咱們的數據進行類型轉換操做,也可能不進行處理而交由存儲引擎去處理,這樣一來,就會出現索引沒法使用的狀況而形成執行計劃問題。
9.優先優化高併發的 SQL,而不是執行頻率低某些「大」SQL
對於破壞性來講,高併發的 SQL 老是會比低頻率的來得大,由於高併發的 SQL 一旦出現問題,甚至不會給咱們任何喘息的機會就會將系統壓跨。而對於一些雖然須要消耗大量 IO 並且響應很慢的 SQL,因爲頻率低,即便遇到,最多就是讓整個系統響應慢一點,但至少可能撐一下子,讓咱們有緩衝的機會。
10.從全局出發優化,而不是片面調整
SQL 優化不能是單獨針對某一個進行,而應充分考慮系統中全部的 SQL,尤爲是在經過調整索引優化 SQL 的執行計劃的時候,千萬不能顧此失彼,因小失大。
11.儘量對每一條運行在數據庫中的SQL進行 explain
優化 SQL,須要作到心中有數,知道 SQL 的執行計劃才能判斷是否有優化餘地,才能判斷是否存在執行計劃問題。在對數據庫中運行的 SQL 進行了一段時間的優化以後,很明顯的問題 SQL 可能已經不多了,大多都須要去發掘,這時候就須要進行大量的 explain 操做收集執行計劃,並判斷是否須要進行優化。