索引優化,查詢優化,查詢緩存,服務器設置優化,操做系統和硬件優化,應用層面優化(web服務器,緩存)等等。這裏的記錄的優化技巧更適用於開發人員,都是從網絡上收集和本身整理的,主要是查詢語句上面的優化,其它層面的優化技巧在此不作記錄。mysql
查詢的開銷指標:web
執行時間算法
檢查的行數sql
返回的行數數據庫
創建索引的幾個準則:緩存
(1)、合理的創建索引可以加速數據讀取效率,不合理的創建索引反而會拖慢數據庫的響應速度。服務器
(2)、索引越多,更新數據的速度越慢。網絡
(3)、儘可能在採用MyIsam做爲引擎的時候使用索引(由於MySQL以BTree存儲索引),而不是InnoDB。但MyISAM不支持Transcation。分佈式
(4)、當你的程序和數據庫結構/SQL語句已經優化到沒法優化的程度,而程序瓶頸並不能順利解決,那就是應該考慮使用諸如memcached這樣的分佈式緩存系統的時候了。memcached
(5)、習慣和強迫本身用EXPLAIN來分析你SQL語句的性能。
1、count的優化
好比:計算id大於5的城市
(1). select count(*) from world.city where id > 5;
(2). select (select count() from world.city) – count() from world.city where id <= 5;
a語句當行數超過11行的時候須要掃描的行數比b語句要多, b語句掃描了6行,此種狀況下,b語句比a語句更有效率。當沒有where語句的時候直接select count(*) from world.city這樣會更快,由於mysql老是知道表的行數。
2、避免使用不兼容的數據類型
例如float和int、char和varchar、binary和varbinary是不兼容的。數據類型的不兼容可能使優化器沒法執行一些原本能夠進行的優化操做。
在程序中,保證在實現功能的基礎上,儘可能減小對數據庫的訪問次數;經過搜索參數,儘可能減小對錶的訪問行數,最小化結果集,從而減輕網絡負擔;可以分開的操做盡可能分開處理,提升每次的響應速度;在數據窗口使用SQL時,儘可能把使用的索引放在選擇的首列;算法的結構儘可能簡單;在查詢時,不要過多地使用通配符如 SELECT * FROM T1語句,要用到幾列就選擇幾列如:SELECT COL1,COL2 FROM T1;在可能的狀況下儘可能限制儘可能結果集行數如:SELECT TOP 300 COL1,COL2,COL3 FROM T1,由於某些狀況下用戶是不須要那麼多的數據的。不要在應用中使用數據庫遊標,遊標是很是有用的工具,但比使用常規的、面向集的SQL語句須要更大的開銷;按照特定順序提取數據的查找。
3、索引字段上進行運算會使索引失效
儘可能避免在WHERE子句中對字段進行函數或表達式操做,這將致使引擎放棄使用索引而進行全表掃描。如:
SELECT * FROM T1 WHERE F1/2=100 應改成: SELECT * FROM T1 WHERE F1=100*2
4、避免使用!=或<>、IS NULL或IS NOT NULL、IN ,NOT IN等這樣的操做符
由於這會使系統沒法使用索引,而只能直接搜索表中的數據。例如: SELECT id FROM employee WHERE id != 「B%」 優化器將沒法經過索引來肯定將要命中的行數,所以須要搜索該表的全部行。在in語句中能用exists語句代替的就用exists.
5、儘可能使用數字型字段
一部分開發人員和數據庫管理人員喜歡把包含數值信息的字段
設計爲字符型,這會下降查詢和鏈接的性能,並會增長存儲開銷。這是由於引擎在處理查詢和鏈接回逐個比較字符串中每個字符,而對於數字型而言只須要比較一次就夠了。
6、合理使用EXISTS,NOT EXISTS子句
以下所示:
(1). SELECT SUM(T1.C1) FROM T1 WHERE (SELECT COUNT(*)FROM T2 WHERE T2.C2=T1.C2>0)
(2). SELECT SUM(T1.C1) FROM T1WHERE EXISTS(SELECT * FROM T2 WHERE T2.C2=T1.C2)
二者產生相同的結果,可是後者的效率顯然要高於前者。由於後者不會產生大量鎖定的表掃描或是索引掃描。若是你想校驗表裏是否存在某條紀錄,不要用count(*)那樣效率很低,並且浪費服務器資源。能夠用EXISTS代替。如:
IF (SELECT COUNT() FROM table_name WHERE column_name = ‘xxx’)能夠寫成:IF EXISTS (SELECT FROM table_name WHERE column_name = ‘xxx’)
7、 可以用BETWEEN的就不要用IN
8、 可以用DISTINCT的就不用GROUP BY
9、儘可能不要用SELECT INTO語句。SELECT INTO 語句會致使表鎖定,阻止其餘用戶訪問該表
10、 必要時強制查詢優化器使用某個索引
SELECT * FROM T1 WHERE nextprocess = 1 AND processid IN (8,32,45) 改爲:
SELECT * FROM T1 (INDEX = IX_ProcessID) WHERE nextprocess = 1 AND processid IN (8,32,45)
則查詢優化器將會強行利用索引IX_ProcessID 執行查詢。
11、消除對大型錶行數據的順序存取
儘管在全部的檢查列上都有索引,但某些形式的WHERE子句強迫優化器使用順序存取。如:
SELECT * FROM orders WHERE (customer_num=104 AND order_num>1001) OR order_num=1008
解決辦法可使用並集來避免順序存取:
SELECT * FROM orders WHERE customer_num=104 AND order_num>1001 UNION SELECT * FROM orders WHERE order_num=1008
這樣就能利用索引路徑處理查詢。【jacking 數據結果集不少,但查詢條件限定後結果集不大的狀況下,後面的語句快】
12、儘可能避免在索引過的字符數據中,使用非打頭字母搜索。這也使得引擎沒法利用索引
見以下例子:
SELECT * FROM T1 WHERE NAME LIKE ‘%L%’
SELECT * FROM T1 WHERE SUBSTING(NAME,2,1)=’L’
SELECT * FROM T1 WHERE NAME LIKE ‘L%’
即便NAME字段建有索引,前兩個查詢依然沒法利用索引完成加快操做,引擎不得不對全表全部數據逐條操做來完成任務。而第三個查詢可以使用索引來加快操做,不要習慣性的使用 ‘%L%’這種方式(會致使全表掃描),若是可使用`L%’相對來講更好;
十3、雖然UPDATE、DELETE語句的寫法基本固定,可是仍是對UPDATE語句給點建議
(1). 儘可能不要修改主鍵字段。
(2). 當修改VARCHAR型字段時,儘可能使用相同長度內容的值代替。
(3). 儘可能最小化對於含有UPDATE觸發器的表的UPDATE操做。
(4). 避免UPDATE將要複製到其餘數據庫的列。
(5). 避免UPDATE建有不少索引的列。
(6). 避免UPDATE在WHERE子句條件中的列。
十4、能用UNION ALL就不要用UNION
UNION ALL不執行SELECT DISTINCT函數,這樣就會減小不少沒必要要的資源
在跨多個不一樣的數據庫時使用UNION是一個有趣的優化方法,UNION從兩個互不關聯的表中返回數據,這就意味着不會出現重複的行,同時也必須對數據進行排序,咱們知道排序是很是耗費資源的,特別是對大表的排序。
UNION ALL能夠大大加快速度,若是你已經知道你的數據不會包括重複行,或者你不在意是否會出現重複的行,在這兩種狀況下使用UNION ALL更適合。此外,還能夠在應用程序邏輯中採用某些方法避免出現重複的行,這樣UNION ALL和UNION返回的結果都是同樣的,但UNION ALL不會進行排序。
十5、字段數據類型優化
(1). 避免使用NULL類型:NULL對於大多數數據庫都須要特殊處理,MySQL也不例外,它須要更多的代碼,更多的檢查和特殊的索引邏輯,有些開發人員徹底沒有意識到,建立表時NULL是默認值,但大多數時候應該使用NOT NULL,或者使用一個特殊的值,如0,-1做爲默認值。
(2). 儘量使用更小的字段,MySQL從磁盤讀取數據後是存儲到內存中的,而後使用cpu週期和磁盤I/O讀取它,這意味着越小的數據類型佔用的空間越小,從磁盤讀或打包到內存的效率都更好,但也不要太過執着減少數據類型,要是之後應用程序發生什麼變化就沒有空間了。修改表將須要重構,間接地可能引發代碼的改變,這是很頭疼的問題,所以須要找到一個平衡點。
(3). 優先使用定長型
十7、關於大數據量limit分佈的優化(當偏移量特別大時,limit效率會很是低)
附上一個提升limit效率的簡單技巧,在覆蓋索引(覆蓋索引用通俗的話講就是在select的時候只用去讀取索引而取得數據,無需進行二次select相關表)上進行偏移,而不是對全行數據進行偏移。能夠將從覆蓋索引上提取出來的數據和全行數據進行聯接,而後取得須要的列,會更有效率,看看下面的查詢:
mysql> select film_id, description from sakila.film order by title limit 50, 5;
若是表很是大,這個查詢最好寫成下面的樣子:
mysql> select film.film_id, film.description from sakila.film
inner join(select film_id from sakila.film order by title liimit 50,5) as film usinig(film_id);
十8、程序中若是一次性對同一個表插入多條數據
好比如下語句:
insert into person(name,age) values(‘xboy’, 14);
insert into person(name,age) values(‘xgirl’, 15);
insert into person(name,age) values(‘nia’, 19);
把它拼成一條語句執行效率會更高.
insert into person(name,age) values(‘xboy’, 14), (‘xgirl’, 15),(‘nia’, 19);
十9、不要在選擇的欄位上放置索引,這是無心義的。應該在條件選擇的語句上合理的放置索引,好比where,order by
SELECT id,title,content,cat_id FROM article WHERE cat_id = 1;
上面這個語句,你在id/title/content上放置索引是毫無心義的,對這個語句沒有任何優化做用。可是若是你在外鍵cat_id上放置一個索引,那做用就至關大了。
二10、ORDER BY語句的MySQL優化
(1). ORDER BY + LIMIT組合的索引優化。若是一個SQL語句形如:
SELECT [column1],[column2],…. FROM [TABLE] ORDER BY [sort] LIMIT [offset],[LIMIT];
這個SQL語句優化比較簡單,在[sort]這個欄位上創建索引便可。
(2). WHERE + ORDER BY + LIMIT組合的索引優化,形如:
SELECT [column1],[column2],…. FROM [TABLE] WHERE [columnX] = [VALUE] ORDER BY [sort] LIMIT [offset],[LIMIT];
這個語句,若是你仍然採用第一個例子中創建索引的方法,雖然能夠用到索引,可是效率不高。更高效的方法是創建一個聯合索引(columnX,sort)
(3). WHERE + IN + ORDER BY + LIMIT組合的索引優化,形如:
SELECT [column1],[column2],…. FROM [TABLE] WHERE [columnX] IN ([value1],[value2],…) ORDER BY [sort] LIMIT [offset],[LIMIT];
這個語句若是你採用第二個例子中創建索引的方法,會得不到預期的效果(僅在[sort]上是using index,WHERE那裏是using where;using filesort),理由是這裏對應columnX的值對應多個。
目前哥還木有找到比較優秀的辦法,等待高手指教。
(4).WHERE+ORDER BY多個欄位+LIMIT,好比:
SELECT * FROM [table] WHERE uid=1 ORDER x,y LIMIT 0,10;
對於這個語句,你們多是加一個這樣的索引:(x,y,uid)。但實際上更好的效果是(uid,x,y)。這是由MySQL處理排序的機制形成的。