超全面常用的數據庫優化方案


一、數據庫設計優化篇

1.1 選取最合適的的字段屬性

關系型數據庫可以支持大數據量的存取,但是一般來說表越小,它執行的速度也就會越快。因此,在新建表的時候,在滿足我們業務需求的基礎上,盡可能的將字段的寬度設置的小一點。

例如,在定義郵政編碼這個字段時,如果將其設置為CHAR(100),顯然給數據庫增加了不必要的空間,甚至使用VARCHAR這種類型也是多余的,因為CHAR(6)就可以很好的完成任務了。相同的,如果TINYINT能滿足我們的業務需求,那我們沒有必要使用INT或者BIGINT。

1.2數據庫索引

索引是提高數據庫性能最常用的方法,它可以大大提高數據庫查詢的效率,尤其是在查詢語句當中包含有MAX(),MIN()和ORDER BY這些函數和語句的時候,性能提高更為明顯。

通常情況,索引應建立在那些將用於JOIN連接,WHERE判斷和ORDERBY排序的字段上。盡量不要對數據庫中某個含有大量重復的值的字段建立索引。如用戶表中的性別字段就不適合創建索引(因為性別只有男或女兩個值),在這樣的字段上創建索引不僅不會提高數據庫查詢的效率,反而有可能降低數據庫的性能。

索引並不是越多越好,索引固然可以提高相應的SELECT的效率,但同時也降低了INSERT及UPDATE 的效率,因為INSERT或UPDATE 時有會更新索引,所以怎樣建索引需要慎重考慮,視具體情況而定。一個表的索引數最好不要超過6個,若太多則應考慮一些不常使用到的列上建的索引是否有必要。

二、SQL語句優化篇

2.1 盡量避免使用子查詢,可以使用JOIN鏈接查詢替代

常用的關系型數據庫都支持子查詢,子查詢使用SELECT語句創建一個查詢結果,然后把這個結果作為一張臨時表用在另一個查詢中。使用子查詢可以一次完成多步SQL操作,也可以避免事務或者表鎖死,且寫起來比較容易。但是使用子查詢MYSQL會在內存中創建一張臨時表供外層查詢使用,所以會降低查詢的效率。這時候我們可以使用JOIN鏈接操作來替代子查詢。

2.2 UNION All能滿足業務需求不要使用UNION

如果我們需要將兩個或者多個SELECT語句的結果作為合並為一個整體顯示出來,我們可以用UNION或者UNION ALL關鍵字。UNION(聯合)和UNION ALL的作用是將多個結果合並在一起顯示出來。

兩者的區別是:

UNION會自動壓縮多個結果集合中的重復結果,而UNION ALL 則將所有的結果全部顯示出來,不管是不是重復。所以當UNION ALL能滿足業務需求的時候,盡量使用UNION ALL而不用UNION。

2.3 WHERE子句盡量避免使用!=或<>操作符

在WHERE子句中使用!=或<>操作符,查詢條件不會使用索引,會進行全表查詢。即影響查詢效率。

2.4 WHERE子句使用OR的優化

通常情況我們可以使用UNION ALL或UNION的方式替換OR會得到更好的效果。因為WHERE子句中使用了OR,將不會使用索引。

例如:SELECT ID FROM TABLENAME WHERE ID = 1 OR ID = 2 ;
優化:SELECT ID FROM TABLENAME WHERE ID = 1 UNION ALL SELECT ID FROM TABLENAME WHERE ID = 2 ;

2.5 WHERE子句使用IN或NOT IN優化

IN和NOT IN也要慎用,否則可能會導致全表掃描。

可用以下方案替換:

方案一:BETWEEN AND替換IN
例如:SELECT ID FROM TABLENAME WHERE ID IN(1,2,3);
優化:SELECT ID FROM TABLENAME WHERE ID BETWEEN 1 AND 3;

方案二:EXISTS替換IN
例如:SELECT ID FROM TABLEA WHERE ID IN (SELECT ID FROM TABLEB ) 
優化:SELECT ID FROM TABLEA AS A WHERE ID EXISTS(SELECT 1 FROM TABLEB AS A WHERE B.ID = A.ID)

方案三:LEFT JOIN替換IN
例如:SELECT ID FROM TABLEA WHERE ID IN(SELECT ID FROM TABLEB) 
優化:SELECT ID FROM TABLEA AS A LEFT JOIN TABLEB AS B ON A.ID = B.ID

2.6 WHERE子句中使用IS NULL或IS NOT NULL優化

在WHERE子句中使用IS NULL或IS NOT NULL判斷,索引將被放棄使用,會進行全表查詢。

例如:SELECT ID FROM TABLENAME WHERE AGE IS NULL 
優化成AGE上設置默認值0,確保表中AGE沒有NULL值,
優化:SELECT ID FROM TABLENAME WHERE AGE = 0

2.7 LIKE語句優化

一般情況下不建議使用LIKE操作,特別是數據量較大的表。

例如:SELECT NAME FROM TABLEA WHERE NAME LIKE '%張%';不會使用索引
優化:SELECT NAME FROM TABLEA WHERE NAME LIKE '張%';會使用索引

2.8 WHERE子句中避免對字段進行表達式操作

盡量不要在WHERE子句中的=左邊進行函數、算數運算或其他表達式運算,否則系統將無法正確使用索引。

例如:SELECT ID FROM TABLENAME WHERE ID/2 = 50 
優化:SELECT ID FROM TABLENAME WHERE ID = 50*2

例如:SELECT ID FROM TABLENAME WHERE substring(name,1,2) = '歐陽' 
優化:SELECT ID FROM TABLENAME WHERE LIKE '歐陽%'

2.9 一定不要用SELECT * FROM TABLENAME

在定義SQL語句字段列表替換"*",盡量避免返回無用的時候,要用具體的的字段。

2.10 LIMIT分頁優化

MYSQL數據庫實現分頁一般都會使用LIMIT,但是當偏移量比較大時,LIMIT的效率會非常低,導致查詢超時。

如下SQL:
SELECT ID FROM TABLENAME LIMIT 1000,10   執行很快
SELECT ID FROM TABLENAME LIMIT 100000,10 執行很慢

優化方法:
方法一:SELECT ID FROM TABLENAME ORDER BY ID LIMIT 100000,10; 執行很快(因為用了ID主鍵做索引)
上述方法一是我們最常用的,但是如果表中的數據是千萬級別的,即便使用方法一,查詢速度可能還是比較慢,這時候我們可以把上一頁ID的最大值作為查詢條件來實現分頁,如方法二。

方法二:SELECT ID FROM TABLENAME WHERE id > @MAXID limit 10;
@MAXID的值是上一頁查詢結果中ID的最大值。

2.11 EXISTS代替IN

SELECT ID FROM TABLEA WHERE ID IN (SELECT ID FROM TABLEB)

如上SQL,IN執行的時候是在內存中遍歷比較,IN(SELECT ID FROM TABLEB)括號中語句只執行一次,把TABLEB表中的所有ID字段緩存起來,之后檢查TABLEA表的ID是否與TABLEB表中的ID相等,如果ID相等則將TABLEA表中的記錄加入到結果集中,直到遍歷完TABLEA表的所有記錄。

SELECT ID FROM TABLEA WHERE ID EXISTS(SELECT ID FROM TABLEA.ID= TABLEB.ID)

如上SQL,EXISTS查詢是遍歷TABLEA中的數據,TABLEA中的每一條數據與TABLEB連表查詢,如果有返回結果,則把該記錄添加到結果集中,所以當TABLEB的數據量遠大於TANLEA時,EXISTS效率大大優於IN.當TABLEA表數據與TABLEB表數據一樣大時,IN與EXISTS效率差不多

 

轉載自: 超全面常用的數據庫優化方案 - 知乎 (zhihu.com)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM