mysql hint憂化一例
背景:對於經常使用oracle的朋友可能知道,oracle的hint功能種類很多,對於最佳化sql語句提供了很多方法。同樣,在mysql裡,也有類似的hint功能。mysql的最佳化器功能很弱,很多時候透過mysql hint強制制定表的連線順序,或者是否使用索引,能夠得到更快的效能。Alb中的一個sql,最佳化前15s,最佳化後0.38s。
例項:
碰到一個sql,需要執行15s。
SELECT t.oldcontractno, t.startdate, t.enddate, sum( confrec.confamt )
FROM (
SELECT ca1.contractno, f1.startdate, f1.enddate, ca1.oldcontractno
FROM contract c1, contractinapprove ca1, framepolicy f1
WHERE c1.contractinapproveid = ca1.contractid
AND ca1.contractid = f1.contractid
AND c1.contracttype =4
)t, contract c2, confrec
WHERE
(
(substring( c2.oldcontractno, 1, locate( '-', c2.oldcontractno ) -1 ) = t.oldcontractno)
OR
(c2.oldcontractno = t.oldcontractno)
)
AND confrec.contractid = c2.contractid
AND c2.customersitecode NOT IN ('JP', 'BD')
AND confrec.confdate BETWEEN t.startdate AND t.enddate
GROUP BY t.oldcontractno
執行計劃如下所示:
+----+-------------+------------+--------+---------------------+---------------------+---------+------------------------------+---------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+---------------------+---------------------+---------+------------------------------+---------+---------------------------------+
| 1 | PRIMARY |
| 1 | PRIMARY | confrec | ALL | contractid | NULL | NULL | NULL | 1007935 | Using where |
| 1 | PRIMARY | c2 | eq_ref | PRIMARY | PRIMARY | 4 | ap_db.confrec.contractid | 1 | Using where |
| 2 | DERIVED | f1 | ALL | FK28E4C8DF253521AD | NULL | NULL | NULL | 262 | |
| 2 | DERIVED | c1 | ref | contractinapproveid | contractinapproveid | 5 | ap_db.f1.contractid | 1 | Using where |
| 2 | DERIVED | ca1 | eq_ref | PRIMARY | PRIMARY | 4 | ap_db.c1.contractinapproveid | 1 | Using where |
+----+-------------+------------+--------+---------------------+---------------------+---------+------------------------------+---------+---------------------------------+
從執行計劃分析,最大的問題是confrec的全表掃描,但是confrec和contract的連線使用了索引。看執行計劃是對confrec進行了全表掃描,而contract使用了索引,現在修改一下,confrec(百萬級別)用索引,contract(3w)用全表掃描。由於contract的資料量小很多,效率高了,從執行計劃的預估行數可以看的很明顯。
使用了sql hint控制了表的連線順序解決該問題。
SELECT t.oldcontractno, t.startdate, t.enddate, sum( confrec.confamt )
FROM (
SELECT ca1.contractno, f1.startdate, f1.enddate, ca1.oldcontractno
FROM contract c1, contractinapprove ca1, framepolicy f1
WHERE c1.contractinapproveid = ca1.contractid
AND ca1.contractid = f1.contractid
AND c1.contracttype =4
)t, contract c2 straight_join confrec
WHERE
(
(substring( c2.oldcontractno, 1, locate( '-', c2.oldcontractno ) -1 ) = t.oldcontractno)
OR
(c2.oldcontractno = t.oldcontractno)
)
AND confrec.contractid = c2.contractid
AND c2.customersitecode NOT IN ('JP', 'BD')
AND confrec.confdate BETWEEN t.startdate AND t.enddate
GROUP BY t.oldcontractno
修改後的執行計劃:
+----+-------------+------------+--------+---------------------+---------------------+---------+------------------------------+-------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+---------------------+---------------------+---------+------------------------------+-------+---------------------------------+
| 1 | PRIMARY |
| 1 | PRIMARY | c2 | ALL | PRIMARY | NULL | NULL | NULL | 13135 | Using where |
| 1 | PRIMARY | confrec | ref | contractid | contractid | 4 | ap_db.c2.contractid | 45 | Using where |
| 2 | DERIVED | f1 | ALL | FK28E4C8DF253521AD | NULL | NULL | NULL | 262 | |
| 2 | DERIVED | c1 | ref | contractinapproveid | contractinapproveid | 5 | ap_db.f1.contractid | 1 | Using where |
| 2 | DERIVED | ca1 | eq_ref | PRIMARY | PRIMARY | 4 | ap_db.c1.contractinapproveid | 1 | Using where |
+----+-------------+------------+--------+---------------------+---------------------+---------+------------------------------+-------+---------------------------------+
看明顯看到了連線行數的減少。
ref: http://hi.baidu.com/veyroner/blog/item/c72827fd9403d3f7fd037f77.html
[@more@]普通MySQL執行,資料量和訪問量不大的話,是足夠快的,但是當資料量和訪問量劇增的時候,那麼就會明顯發現MySQL很慢,甚至down掉,那麼就要考慮最佳化我們的MySQL了。
最佳化無非是從三個角度入手:
第一個是從硬體,增加硬體,增加伺服器
第二個就是對我們的MySQL伺服器進行最佳化,增加快取大小,開多埠,讀寫分開
第三個就是我們的應用最佳化,建立索引,最佳化SQL查詢語句,建立快取等等
我就簡單的說說SQL查詢語句的最佳化。因為如果我們Web伺服器比資料庫服 務器多或者效能優良的話,我們完全可以把資料庫的壓力轉嫁到Web伺服器上,因為如果單臺MySQL,或者 Master/Slave 架構的資料庫伺服器都負擔比較重,那麼就可以考慮把MySQL的運算放到Web伺服器上去進行。當然了,如果你Web伺服器比資料庫伺服器差,那就把壓力 放在資料庫伺服器上吧,呵呵。
如果是把MySQL伺服器的壓力放在Web伺服器上,那麼很多運算就需要我們的程式去執行,比如Web程式中全部交給PHP腳 本去處理資料。單臺MySQL伺服器,查詢、更新、插入、刪除都在一臺伺服器上的話,訪問量一大,你會明顯發現鎖表現象,當對一個表進行更新刪除操作的時 候,就會拒絕其他操作,這樣就會導致鎖表,解決這個問題最簡單直接的辦法就是拿兩臺MySQL伺服器,一臺負責查詢(select)操作,另外一臺負責更 改(update/delete/insert),然後進行同步,這樣能夠避免鎖表,如果伺服器更多,那麼就更好處理了,可以採用分散式資料庫架構和資料 的雜湊儲存,下面我們會簡單說一下。
一、SQL的最佳化和注意事項
現在我們假設我們只有一臺MySQL伺服器,所有的select/update/insert/delete操作都是在這上面進行的,我們同時有三臺Web伺服器,透過DNS輪巡來訪問,那麼我們如何進行我們應用程式和SQL的最佳化。
1. Where條件
在查詢中,WHERE條件也是一個比較重要的因素,儘量少並且是合理的where條件是很重要的,在寫每一個where條件的時候都要仔細考慮,儘量在多個條件的時候,把會提取儘量少資料量的條件放在前面,這樣就會減少後一個where條件的查詢時間。
有時候一些where條件會導致索引無效,當使用了Mysql函式的時候,索引將無效,比如:select * from tbl1 where
left(name, 4) = 'hylr',那麼這時候索引無效,還有就是使用LIKE進行搜尋匹配的時候,這樣的語句索引是無效的:select
* from tbl1 where name like '%xxx%',但是這樣索引是有效的:select * from tbl1 where
name like 'xxx%',所以謹慎的寫你的SQL是很重要的。
2. 關聯查詢和子查詢
資料庫一個很重要的特點是關聯查詢,LEFT JOIN 和全關聯,特別是多個表進行關聯,因為每個關聯表查詢的時候,進行掃描的時候都是一個笛卡爾乘積的數量級,掃描數量很大,如果確實是需要進行關聯操作,請給where或者on的條件進行索引。
關聯操作也是可能交給應用去操作的,看資料量的大小,如果資料量不是非常大,比如10萬條以下,那麼就可以交給程式去處理(提出筆誤,特此修正),程式分別提取左右兩個表的資料,然後進行迴圈的掃描處理,返回結果,這個過程同樣非常耗費Web伺服器的資源,那麼就需要取決於你願意把壓力放在Web伺服器上或者資料庫伺服器上了。
子查詢是在mysql5中支援的功能,比如:select * from tbl1 where id in(select id from tbl1),那樣效率是非常非常低,要儘量避免使用子查詢,要是我,絕對不用,呵呵。
3. 一些耗費時間和資源的操作
SQL語句中一些浪費的操作,比如 DISTINCT、COUNT、GROUP
BY、各種MySQL函式。這些操作都是比較耗資源的,我想應用最多的是count字句吧,如果使用count,儘量不要count(*),最好
count一個欄位,比如count(id),或者count(1),(據測
試效率其實是一樣的),同樣能夠起到統計的作用。如果不是十分必要,儘量不要使用distinct操作,就是提取唯一值,你完全可以把這個操作交給指令碼程
序去執行提取唯一值,減少MySQL的負擔。group by
操作也是,確實需要分組的話,請謹慎的操作,如果是小批次的資料,可以考慮交給指令碼程式去做。
至於MySQL的函式,估計很多常用,比如有人喜歡把擷取字串也交給MySQL去操作,或者時間轉換操作,使用比較多的函式像 SUBSTR(),
CONCAT(), DATE_FORMAT(), TO_DAYS(), MAX(), MIN(), MD5()
等等,這些操作完全可以交給指令碼程式去做,減輕MySQL的負擔。
4. 合理的建立索引
索引的提升速度的一個非常重要的手段,索引在對一些經常進行select操作,並且值比較唯一的欄位是相當有效的,比如主鍵的id欄位,唯一的名字name欄位等等。
但是索引對於唯一值比較少的欄位,比如性別gender欄位,寥寥無幾的類別欄位等,意義不大,因為性別是50%的機率,索引幾乎沒有意義。對於update/delete/insert非常頻繁的表,建立索引要慎重考慮,因為這些頻繁的操作同樣對於索引的維護工作量也是很大的,最後反而得不償失,這個需要自己仔細考慮。索引同樣不是越多越好,適當的索引會起到很關鍵的作用,不適當的索引,反而減低效率維護,增加維護索引的負擔。
5. 監控sql執行效率
在select語句前面使用EXPLAIN字句能夠檢視當前這個select字句的執行情況,包括使用了什麼操作、返回多少機率、對索引的使用情況如何等等,能夠有效分析SQL語句的執行效率和合理程度。
另外使用MySQL中本身的慢查詢日誌:slow-log,同樣能夠記錄查詢中花費時間比較多的SQL語句,好對相應的語句進行最佳化和改寫。
另外在MySQL終端下,使用show processlist命令能夠有效的檢視當前MySQL在進行的執行緒,包括執行緒的狀態,是否鎖表等等,可以實時的檢視SQL執行情況,同時對一些鎖表操作進行最佳化。
二、資料庫伺服器的架構和分佈想法
對於伺服器的架構設計,這個其實是比較重要的,一個合理的設計,能夠讓應用更好的執行。當然,架構的設計,取決於你的應用和你硬體的實際情況。我就簡單的說說幾種不同的資料庫架構設計方式,權當是一個個人的想法,希望能夠有幫助。
1. 單臺伺服器開多程式和埠
單臺MySQL伺服器,如果使用長連結等等都無法解決負載太大,連線太多的問題,不凡考慮採用一臺MySQL上使用多個埠開啟多個MySQL守護程式的方法來緩解壓力。當然,前提是你的應用必須支援多埠,並且你的cpu和記憶體足夠執行多個守護程式。
優點 是能夠很好的緩解暫時伺服器的壓力,把不同的操作放在不同的埠,或者把不同的專案模組放在不同的埠去操作,良好的分擔單個守護程式的壓力。
缺點 是資料可能會產生紊亂,同時可能會導致很多未知的莫名錯誤。呵呵
2. 使用Master/Slave的伺服器結構
Mysql本身具有同步功能,完全可以利用這個功能。構建 Master/Slave 的主從伺服器結構,最少只需要兩臺MySQL伺服器,我們可以把
Master 伺服器使用者更新操作,包括 update/delete/insert,把Slave伺服器用於查詢操作,包括 select
操作,然後兩機進行同步。
優點 是合理的把更新和查詢的壓力分擔,並且能夠避免鎖表的問題。
缺點 是更新部實時,如果網路繁忙,可能會存在延遲的問題,並且任何一臺伺服器down掉了都很麻煩。
3. 使用分散式的雜湊儲存
這種結構適合大資料量,並且負載比較大,然後伺服器比較充足的情況。分散式儲存結構,簡單的可以是多臺伺服器,每臺伺服器功能是類似的,但是儲存的資料不
一樣,比如做一個使用者系統,那麼把使用者ID在1-10萬以內的儲存在A伺服器,使用者ID在10-20萬儲存在B伺服器,20-3-萬儲存在C伺服器,以此
類推。如果每個使用者訪問的伺服器不足,可以構建組伺服器,就是每組使用者擁有多臺伺服器,比如可以在某使用者組建立兩臺MySQL伺服器,一臺Master,
一臺Slave,同樣分離他們的更新和查詢操作,或者可以設計成雙向同步。同時,你的應用程式必須支援跨資料庫和跨伺服器的操作能力。
優點 是伺服器的負載合理的被平攤,每臺伺服器都是負責一部分使用者,如果一臺伺服器down掉了,不會影響其他使用者ID的使用者正常訪問。同時新增節點比較容易,如果又增加了10萬使用者,那麼又可以增加一個節點伺服器,升級很方便。
缺點 是任何一臺資料庫伺服器down掉或者資料丟失,那麼這部分伺服器的使用者將很鬱悶,資料都沒了,當然,這個需要良好的備份機制。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/7916042/viewspace-1029093/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Mysql 5.7憂化配置詳解MySql
- sql調優一例---索引排序hintSQL索引排序
- mysql常用的hintMySql
- MySQL SQL hint 提示MySql
- mysql常用的hint[轉]MySql
- MySQL的SQL語句最佳化一例MySql
- Percona MySQL 5.6 HINT介紹MySql
- MySQL:死鎖一例MySql
- MySQL SLAVE故障一例MySql
- HINT篇---優化器相關優化
- mysql oder by 使用index一例MySqlIndex
- mysql 引數修改 一例MySql
- mysql slave 損害一例MySql
- mysql儲存過程一例MySql儲存過程
- 遭遇mysql 5.6.10 BUG 一例MySql
- PL/SQL優化一例SQL優化
- Sql最佳化(五) hint(提示)介紹SQL
- 通過hint push_subq優化sql優化SQL
- ORACLE常用SQL最佳化hint語句OracleSQL
- oracle優化一例之sql優化Oracle優化SQL
- MySQL 5.7鎖的問題一例MySql
- 解決mysql innodb page corrupt一例MySql
- mysql資料庫恢復一例MySql資料庫
- outline優化一例優化
- oracle hintOracle
- 處理mysql複製故障一例薦MySql
- oracle update操作的優化一例Oracle優化
- sql優化一例(index_desc)SQL優化Index
- no_index最佳化sql一例IndexSQL
- mongodb之使用explain和hint效能分析和優化MongoDBAI優化
- oracle hint之hint_index_ffs,index_joinOracleIndex
- MySQL使用者許可權控制一例MySql
- 故障分析 | MySQL鎖等待超時一例分析MySql
- mysql 匯入中文亂碼解決一例MySql
- ORACLE 部分HINTOracle
- Append HintAPP
- oracle hint (續)Oracle
- Oracle Hint 精華Oracle