引言:
關於數據庫優化,網上有很多資料和方法,可是很多質量良莠不齊,有些總結的不夠到位,內容冗雜 偶爾發現了這篇文章,總結得很經典,文章流量也很大,因此拿到本身的總結文集中,積累優質文章,提高我的能力,但願對你們從此開發中也有幫助
MySQL能夠很好的支持大數據量的存取,可是通常說來,數據庫中的表越小,在它上面執行的查詢也就會越快。所以,在建立表的時候,爲了得到更好的性能,咱們能夠將表中字段的寬度設得儘量小。sql
例如,在定義郵政編碼這個字段時,若是將其設置爲CHAR(255),顯然給數據庫增長了沒必要要的空間,甚至使用VARCHAR這種類型也是多餘的,由於CHAR(6)就能夠很好的完成任務了。一樣的,若是能夠的話,咱們應該使用MEDIUMINT而不是BIGIN來定義整型字段。數據庫
另一個提升效率的方法是在可能的狀況下,應該儘可能把字段設置爲NOTNULL,這樣在未來執行查詢的時候,數據庫不用去比較NULL值。
對於某些文本字段,例如「省份」或者「性別」,咱們能夠將它們定義爲ENUM類型。由於在MySQL中,ENUM類型被看成數值型數據來處理,而數值型數據被處理起來的速度要比文本類型快得多。這樣,咱們又能夠提升數據庫的性能。安全
MySQL從4.1開始支持SQL的子查詢。這個技術可使用SELECT語句來建立一個單列的查詢結果,而後把這個結果做爲過濾條件用在另外一個查詢中。例如,咱們要將客戶基本信息表中沒有任何訂單的客戶刪除掉,就能夠利用子查詢先從銷售信息表中將全部發出訂單的客戶ID取出來,而後將結果傳遞給主查詢,以下所示:服務器
DELETE FROM customerinfo
函數
WHERE CustomerID NOT IN (SELECT CustomerID FROM salesinfo)
性能
使用子查詢能夠一次性的完成不少邏輯上須要多個步驟才能完成的SQL操做,同時也能夠避免事務或者表鎖死,而且寫起來也很容易。可是,有些狀況下,子查詢能夠被更有效率的鏈接(JOIN)..替代。例如,假設咱們要將全部沒有訂單記錄的用戶取出來,能夠用下面這個查詢完成:大數據
SELECT * FROM customerinfo
優化
WHERE CustomerID NOT IN (SELECTC ustomerID FROM salesinfo)
網站
若是使用鏈接(JOIN)..來完成這個查詢工做,速度將會快不少。尤爲是當salesinfo表中對CustomerID建有索引的話,性能將會更好,查詢以下:編碼
SELECT * FROM customerinfo
LEFT JOIN salesinfo ON customerinfo.CustomerID=salesinfo.CustomerID
WHERE salesinfo.CustomerID ISNULL
鏈接(JOIN)..之因此更有效率一些,是由於MySQL不須要在內存中建立臨時表來完成這個邏輯上的須要兩個步驟的查詢工做。
MySQL從4.0的版本開始支持union查詢,它能夠把須要使用臨時表的兩條或更多的select查詢合併的一個查詢中。在客戶端的查詢會話結束的時候,臨時表會被自動刪除,從而保證數據庫整齊、高效。使用union來建立查詢的時候,咱們只須要用UNION做爲關鍵字把多個select語句鏈接起來就能夠了,要注意的是全部select語句中的字段數目要想同。下面的例子就演示了一個使用UNION的查詢。
SELECT Name,Phone FROM client UNION
SELECT Name,BirthDate FROM author UNION
SELECT Name,Supplier FROM product
儘管咱們可使用子查詢(Sub-Queries)、鏈接(JOIN)和聯合(UNION)來建立各類各樣的查詢,但不是全部的數據庫操做均可以只用一條或少數幾條SQL語句就能夠完成的。更多的時候是須要用到一系列的語句來完成某種工做。可是在這種狀況下,當這個語句塊中的某一條語句運行出錯的時候,整個語句塊的操做就會變得不肯定起來。設想一下,要把某個數據同時插入兩個相關聯的表中,可能會出現這樣的狀況:第一個表中成功更新後,數據庫忽然出現意外情況,形成第二個表中的操做沒有完成,這樣,就會形成數據的不完整,甚至會破壞數據庫中的數據。要避免這種狀況,就應該使用事務,它的做用是:要麼語句塊中每條語句都操做成功,要麼都失敗。換句話說,就是能夠保持數據庫中數據的一致性和完整性。事物以BEGIN關鍵字開始,COMMIT關鍵字結束。在這之間的一條SQL操做失敗,那麼,ROLLBACK命令就能夠把數據庫恢復到BEGIN開始以前的狀態。
BEGIN; INSERT INTO salesinfo SET CustomerID=14; UPDATE inventory SET Quantity=11 WHERE item='book'; COMMIT;
事務的另外一個重要做用是當多個用戶同時使用相同的數據源時,它能夠利用鎖定數據庫的方法來爲用戶提供一種安全的訪問方式,這樣能夠保證用戶的操做不被其它的用戶所幹擾。
儘管事務是維護數據庫完整性的一個很是好的方法,但卻由於它的獨佔性,有時會影響數據庫的性能,尤爲是在很大的應用系統中。因爲在事務執行的過程當中,數據庫將會被鎖定,所以其它的用戶請求只能暫時等待直到該事務結束。若是一個數據庫系統只有少數幾個用戶來使用,事務形成的影響不會成爲一個太大的問題;但假設有成千上萬的用戶同時訪問一個數據庫系統,例如訪問一個電子商務網站,就會產生比較嚴重的響應延遲。
其實,有些狀況下咱們能夠經過鎖定表的方法來得到更好的性能。下面的例子就用鎖定表的方法來完成前面一個例子中事務的功能。
LOCK TABLE inventory WRITE SELECT Quantity FROM inventory WHERE Item='book';
...
UPDATE inventory SET Quantity=11 WHERE Item='book'; UNLOCKTABLES
這裏,咱們用一個select語句取出初始數據,經過一些計算,用update語句將新值更新到表中。包含有WRITE關鍵字的LOCKTABLE語句能夠保證在UNLOCKTABLES命令被執行以前,不會有其它的訪問來對inventory進行插入、更新或者刪除的操做。
鎖定表的方法能夠維護數據的完整性,可是它卻不能保證數據的關聯性。這個時候咱們就可使用外鍵。
例如,外鍵能夠保證每一條銷售記錄都指向某一個存在的客戶。在這裏,外鍵能夠把customerinfo表中的CustomerID映射到salesinfo表中CustomerID,任何一條沒有合法CustomerID的記錄都不會被更新或插入到salesinfo中。
CREATE TABLE customerinfo( CustomerIDINT NOT NULL,PRIMARYKEY(CustomerID))TYPE=INNODB; CREATE TABLE salesinfo( SalesIDNT NOT NULL,CustomerIDINT NOT NULL, PRIMARYKEY(CustomerID,SalesID), FOREIGNKEY(CustomerID) REFERENCES customerinfo(CustomerID) ON DELETE CASCADE)TYPE=INNODB;
注意例子中的參數「ON DELETE CASCADE」。該參數保證當customerinfo表中的一條客戶記錄被刪除的時候,salesinfo表中全部與該客戶相關的記錄也會被自動刪除。若是要在MySQL中使用外鍵,必定要記住在建立表的時候將表的類型定義爲事務安全表InnoDB類型。該類型不是MySQL表的默認類型。定義的方法是在CREATETABLE語句中加上TYPE=INNODB。如例中所示。
索引是提升數據庫性能的經常使用方法,它能夠令數據庫服務器以比沒有索引快得多的速度檢索特定的行,尤爲是在查詢語句當中包含有MAX(),MIN()和ORDERBY這些命令的時候,性能提升更爲明顯。
那該對哪些字段創建索引呢?
通常說來,索引應創建在那些將用於JOIN,WHERE判斷和ORDERBY排序的字段上。儘可能不要對數據庫中某個含有大量重複的值的字段創建索引。對於一個ENUM類型的字段來講,出現大量重複值是頗有可能的狀況
例如customerinfo中的「province」..字段,在這樣的字段上創建索引將不會有什麼幫助;相反,還有可能下降數據庫的性能。咱們在建立表的時候能夠同時建立合適的索引,也可使用ALTERTABLE或CREATEINDEX在之後建立索引。此外,MySQL從版本3.23.23開始支持全文索引和搜索。全文索引在MySQL中是一個FULLTEXT類型索引,但僅能用於MyISAM類型的表。對於一個大的數據庫,將數據裝載到一個沒有FULLTEXT索引的表中,而後再使用ALTERTABLE或CREATEINDEX建立索引,將是很是快的。但若是將數據裝載到一個已經有FULLTEXT索引的表中,執行過程將會很是慢。
絕大多數狀況下,使用索引能夠提升查詢的速度,但若是SQL語句使用不恰當的話,索引將沒法發揮它應有的做用。
下面是應該注意的幾個方面。
首先,最好是在相同類型的字段間進行比較的操做。
在MySQL3.23版以前,這甚至是一個必須的條件。例如不能將一個建有索引的INT字段和BIGINT字段進行比較;可是做爲特殊的狀況,在CHAR類型的字段和VARCHAR類型字段的字段大小相同的時候,能夠將它們進行比較。
其次,在建有索引的字段上儘可能不要使用函數進行操做。
例如,在一個DATE類型的字段上使用YEAE()函數時,將會使索引不能發揮應有的做用。因此,下面的兩個查詢雖然返回的結果同樣,但後者要比前者快得多。
例以下面的查詢將會比較表中的每一條記錄。
SELECT * FROM books WHERE name like"MySQL%"
可是若是換用下面的查詢,返回的結果同樣,但速度就要快上不少:
SELECT * FROM books WHERE name>="MySQL" andname <"MySQM"
最後,應該注意避免在查詢中讓MySQL進行自動類型轉換,由於轉換過程也會使索引變得不起做用。