一、選取最適用的字段屬性
MySQL能夠很好的支持大數據量的存取,可是通常說來,數據庫中的表越小,在它上面執行的查詢也就會越快。所以,在建立表的時候,爲了得到更好的性能,咱們能夠將表中字段的寬度設得儘量小。例如,在定義郵政編碼這個字段時,若是將其設置爲CHAR (255),顯然給數據庫增長了沒必要要的空間,甚至使用VARCHAR這種類型也是多餘的,由於CHAR(6)就能夠很好的完成任務了。一樣的,若是能夠的話,咱們應該使用MEDIUMINT而不是BIGIN來定義整型字段。
另一個提升效率的方法是在可能的狀況下,應該儘可能把字段設置爲NOT NULL,這樣在未來執行查詢的時候,數據庫不用去比較NULL值。
對於某些文本字段,例如「省份」或者「性別」,咱們能夠將它們定義爲ENUM類型。由於在MySQL中,ENUM類型被看成數值型數據來處理,而數值型數據被處理起來的速度要比文本類型快得多。這樣,咱們又能夠提升數據庫的性能。
二、使用鏈接(JOIN)來代替子查詢(Sub-Queries)
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 (SELECT CustomerID FROM salesinfo )
若是使用鏈接(JOIN).. 來完成這個查詢工做,速度將會快不少。尤爲是當salesinfo表中對CustomerID建有索引的話,性能將會更好,查詢以下:
SELECT * FROM customerinfo LEFT JOIN salesinfoON customerinfo.CustomerID=salesinfo. CustomerID WHERE salesinfo.CustomerID IS NULL
鏈接(JOIN).. 之因此更有效率一些,是由於 MySQL不須要在內存中建立臨時表來完成這個邏輯上的須要兩個步驟的查詢工做。
三、使用聯合(UNION)來代替手動建立的臨時表
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
WHEREItem='book';
...
UPDATE inventory SET Quantity=11
WHEREItem='book';
UNLOCK TABLES
這裏,咱們用一個 SELECT 語句取出初始數據,經過一些計算,用 UPDATE 語句將新值更新到表中。包含有 WRITE 關鍵字的 LOCK TABLE 語句能夠保證在 UNLOCK TABLES 命令被執行以前,不會有其它的訪問來對 inventory 進行插入、更新或者刪除的操做。
六、使用外鍵
鎖定表的方法能夠維護數據的完整性,可是它卻不能保證數據的關聯性。這個時候咱們就可使用外鍵。例如,外鍵能夠保證每一條銷售記錄都指向某一個存在的客戶。在這裏,外鍵能夠把customerinfo 表中的CustomerID映射到salesinfo表中CustomerID,任何一條沒有合法CustomerID的記錄都不會被更新或插入到 salesinfo中。
CREATE TABLE customerinfo
(
CustomerID INT NOT NULL ,
PRIMARY KEY ( CustomerID )
) TYPE = INNODB;
CREATE TABLE salesinfo
(
SalesID INT NOT NULL,
CustomerID INT NOT NULL,
PRIMARY KEY(CustomerID, SalesID),
FOREIGN KEY (CustomerID) REFERENCES customerinfo
(CustomerID) ON DELETECASCADE
) TYPE = INNODB;
注意例子中的參數「ON DELETE CASCADE」。該參數保證當 customerinfo 表中的一條客戶記錄被刪除的時候,salesinfo 表中全部與該客戶相關的記錄也會被自動刪除。若是要在 MySQL 中使用外鍵,必定要記住在建立表的時候將表的類型定義爲事務安全表 InnoDB類型。該類型不是 MySQL 表的默認類型。定義的方法是在 CREATE TABLE 語句中加上 TYPE=INNODB。如例中所示。
七、使用索引
索引是提升數據庫性能的經常使用方法,它能夠令數據庫服務器以比沒有索引快得多的速度檢索特定的行,尤爲是在查詢語句當中包含有MAX(), MIN()和ORDERBY這些命令的時候,性能提升更爲明顯。那該對哪些字段創建索引呢?通常說來,索引應創建在那些將用於JOIN, WHERE判斷和ORDER BY排序的字段上。儘可能不要對數據庫中某個含有大量重複的值的字段創建索引。對於一個ENUM類型的字段來講,出現大量重複值是頗有可能的狀況,例如 customerinfo中的「province」.. 字段,在這樣的字段上創建索引將不會有什麼幫助;相反,還有可能下降數據庫的性能。咱們在建立表的時候能夠同時建立合適的索引,也可使用ALTER TABLE或CREATE INDEX在之後建立索引。此外,MySQL
從版本3.23.23開始支持全文索引和搜索。全文索引在 MySQL 中是一個FULLTEXT類型索引,但僅能用於MyISAM 類型的表。對於一個大的數據庫,將數據裝載到一個沒有FULLTEXT索引的表中,而後再使用ALTER TABLE或CREATE INDEX建立索引,將是很是快的。但若是將數據裝載到一個已經有FULLTEXT索引的表中,執行過程將會很是慢。
八、優化的查詢語句
絕大多數狀況下,使用索引能夠提升查詢的速度,但若是SQL語句使用不恰當的話,索引將沒法發揮它應有的做用。下面是應該注意的幾個方面。首先,最好是在相同類型的字段間進行比較的操做。在MySQL 3.23版以前,這甚至是一個必須的條件。例如不能將一個建有索引的INT字段和BIGINT字段進行比較;可是做爲特殊的狀況,在CHAR類型的字段和 VARCHAR類型字段的字段大小相同的時候,能夠將它們進行比較。其次,在建有索引的字段上儘可能不要使用函數進行操做。
例如,在一個DATE類型的字段上使用YEAE()函數時,將會使索引不能發揮應有的做用。因此,下面的兩個查詢雖然返回的結果同樣,但後者要比前者快得多。
SELECT * FROM order WHERE YEAR(OrderDate)<2001;
SELECT * FROM order WHERE OrderDate<"2001-01-01";
一樣的情形也會發生在對數值型字段進行計算的時候:
SELECT * FROM inventory WHERE Amount/7<24;
SELECT * FROM inventory WHERE Amount<24*7;
上面的兩個查詢也是返回相同的結果,但後面的查詢將比前面的一個快不少。第三,在搜索字符型字段時,咱們有時會使用 LIKE 關鍵字和通配符,這種作法雖然簡單,但卻也是以犧牲系統性能爲代價的。例以下面的查詢將會比較表中的每一條記錄。
SELECT * FROM books
WHERE name like "MySQL%"
可是若是換用下面的查詢,返回的結果同樣,但速度就要快上不少:
SELECT * FROM books
WHERE name>="MySQL"and name<"MySQM"
最後,應該注意避免在查詢中讓MySQL進行自動類型轉換,由於轉換過程也會使索引變得不起做用。
在 MySQL 5.0 以後,利用 --log-queries-not-using-indexes 參數能夠抓到未使用索引的慢查詢。爲何 SQL 沒有使用索引,這恐怕是開發 DB 應用的朋友要問的最多見的問題之一。數據庫
使用短主鍵。聯結表時使用數字而非字符串。安全
當使用多部分鍵碼時,第一部分應該時最經常使用的部分。服務器
有疑問時,首先使用更多重複的列以得到更好地鍵碼壓縮。函數
若是在同一臺機器上運行MySQL客戶和服務器,那麼在鏈接MySQL時則使用套接字而不是TCP/IP(這能夠提升性能7.5%)。可在鏈接MySQL服務器時不指定主機名或主機名爲localhost來作到。性能
若是可能,使用--skip-locking(在某些OS上爲默認),這將關閉外部鎖定並將提升性能。大數據
使用應用層哈希值而非長鍵碼:優化
SELECT * FROM table_name WHERE hash=MD5(concat(col1,col2)) AND網站
col_1='constant' AND col_2='constant'編碼
在文件中保存須要以文件形式訪問的BLOB,在數據庫中只保存文件名。spa
刪除全部行比刪除一大部分行要快。
若是SQL不夠快,研究一下訪問數據的較底層接口。