mysql優化案例分析

jopen 11年前發布 | 18K 次閱讀 MySQL 數據庫服務器

      本文總結了一些工作常見的sql優化例子,雖然比較簡單,但很實用,希望對大家有所幫助。sql優化一般分為兩類,一類是sql本身的優化,如何走到合適 的索引,如何減少排序,減少邏輯讀;另一類是sql本身沒有優化余地,需要結合業務場景進行優化。即在滿足業務需求的情況下對sql進行改造,已提高 sql執行速度,減少響應時間。

例子1:

SELECT ID FROM SENDLOG WHERE TO_DAYS(NOW())-TO_DAYS(GMT_CREATE) > 7;

問題:對索引列GMT_CREATE進行了運算,無法使用索引

mysql優化案例分析

優化后sql:
select id from sendlog where gmt_create < now() - 7
mysql優化案例分析
 
例子2:

SELECT * FROM SENDLOG where result = 1 and gmt_create > '2013-10-29 12:40:44' limit 2000;

mysql優化案例分析

問題:result列含有索引,但mysql執行計劃沒有用到。

分析:result類型為char,傳遞的值1為整型,數據類型不一致,導致沒法用索引,對于時間類型gmt_create > '2013-10-29 12:40:44',可以直接使用。

優化后sql:

SELECT * FROM SENDLOG where result = '1' and gmt_create > '2013-10-29 12:40:44' limit 2000;

mysql優化案例分析

例子3:
<div> 
 <p align="left"><span>場景:獲取某個賣家未讀的消息。</span></p>

 <p align="left"><span lang="EN-US">select count(*) from mc_msg where receiver='sun098' and status='UNREAD' and title is not null;</span></p>

 <p align="left"><span lang="EN-US"><span>問題:有時候db負載飆高,sql響應時間變慢。</span></span></p>

 <p align="left"><span>分析:導致db負載飆高的原因是多個大賣家并發查詢的時,</span><span lang="EN-US">cpu</span><span>和邏輯讀增加,</span><span lang="EN-US">load</span><span>飆高。由于receiver,status已有索引,sql本身已經沒有優化空間,</span><span>了解業務后發現其實業務不需要精確值,如果大于</span><span lang="EN-US">99</span><span>條,頁面就直接顯示為</span><span lang="EN-US">99+</span></p>

 <p align="left"><span>優化后</span><span lang="EN-US">sql</span><span>:</span></p>

 <p align="left"></p>

select count(*) from (select id from mc_msg where receiver='sun098' and status='UNREAD' and title is not null limit 100) a;

 <p align="left"><span style="color:#000000;" lang="EN-US">例子4:</span></p>

 <p align="left">場景:查看歷史訂單留言記錄,未讀留言的放在前面,已讀的放在后面,并且按時間遞減排序</p>

 select * from(
select ID,GMT_CREATE,GMT_MODIFIED,SENDER_ALI_ID,RECEIVER_ALI_ID,UNREAD_COUNT,STATUS,LAST_MESSAGE_ID,RELATION_ID,SELLER_ADMIN_SEQ,IS_READ 
from message_relation_sender
WHERE SENDER_ALI_ID = 119545671  and UNREAD_COUNT > 0 
order by LAST_MESSAGE_ID desc) m 
union all 
select * from(
select ID,GMT_CREATE,GMT_MODIFIED,SENDER_ALI_ID,RECEIVER_ALI_ID,UNREAD_COUNT,STATUS,LAST_MESSAGE_ID,RELATION_ID,SELLER_ADMIN_SEQ,IS_READ from message_relation_sender
WHERE SENDER_ALI_ID = 119545671  and UNREAD_COUNT = 0 
order by LAST_MESSAGE_ID desc) n limit 5000,15; 

 
 <div>
  分析:
 </div>

 <div>
  (1)unread_count表示未讀的訂單留言記錄數目;
 </div>

 <div>
  (2)第一個子查詢獲取未讀留言記錄,第二子查詢獲取已讀留言記錄;
 </div>

 <div>
  (3)LAST_MESSAGE_ID 遞增,最新的訂單留言,LAST_MESSAGE_ID最大。
 </div>

 <div>
  (4)limit 5000,15是分頁查詢
 </div>

 <div>
  這里使用union all,主要特點在于union all 不對結果集排序,直接合并,達到了“未讀留言的放在前面,已讀的放在后面”的效果,但同時也造成了兩次掃描索引的結果,每個子查詢都需要排序;而且union all還會產生臨時表,執行代價會更大。
 </div>

 <div>
   
 </div>

 <div>
  優化:
 </div>

 <div>
  這里看到unread_count實際值對這個查詢沒有實際意義,我們只需要區分已讀和未讀即可。由于sql本身已經沒有優化余地,考慮對表結 構進行修改,加一個字段is_read,表示已讀和未讀。is_read=2表示未讀;is_read=1表示已讀。通過組合索引 (SENDER_ALI_ID,is_read, LAST_MESSAGE_ID),既可以完成過濾,還可以完成排序。
 </div>

 <div>
   
 </div>

 <div>
  優化后sql:
 </div>

 <div><pre class="brush:sql; toolbar: true; auto-links: false;"> select ID,GMT_CREATE,GMT_MODIFIED,SENDER_ALI_ID,RECEIVER_ALI_ID,UNREAD_COUNT,STATUS,LAST_MESSAGE_ID,RELATION_ID,SELLER_ADMIN_SEQ,IS_READ from message_relation_sender

where SENDER_ALI_ID = 119545671 order by is_read desc, LAST_MESSAGE_ID desc limit 5000,15 </pre>

第二次優化:
  <div>
   由于索引不包含所有的返回字段,因此需要回表,而mysql對于limit 5000,15的查詢卻需要返回5015次,這種無效的返回很影響查詢效率。
  </div>

 </div>

 <p align="left">分頁的優化寫法:</p>

</div>

select t1.ID,GMT_CREATE,GMT_MODIFIED,SENDER_ALI_ID,RECEIVER_ALI_ID,UNREAD_COUNT,STATUS,LAST_MESSAGE_ID,RELATION_ID,SELLER_ADMIN_SEQ,IS_READ from message_relation_sender t1,
( select id 
 from message_relation_sender
 where SENDER_ALI_ID = 119545671 order by is_read desc, LAST_MESSAGE_ID desc limit 5000,15
)t2 where t1.id = t2.id
  

分析:由于id是主鍵,不需要回表,通過連接查詢,最終只需要15次回表即可。

來自:http://www.cnblogs.com/cchust/p/3444510.html

</div>

</div>

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!