1. 程式人生 > >mysql分頁查詢優化(索引延遲關聯)

mysql分頁查詢優化(索引延遲關聯)

對於web後臺報表匯出是一種常見的功能點,實際對應服務後端即資料庫的排序分頁查詢。如下示例為公司商戶積分報表匯出其中一個sql ,當大批量的匯出請求進入時候,mysql的cpu急劇上升瞬間有拖垮庫的風險。

SELECT
    *
FROM
    coupons.cp_score_log
WHERE
    `m_shopid` = 40861
AND `add_time` BETWEEN 1483200000
AND 1544543999
ORDER BY
    add_time DESC
LIMIT 118000 ,1000 ;

報表匯出功能存在幾個問題:

1、時間跨度太大,資料量劇增。(可以結合業務需求,限制一定時間範圍,比如只能匯出3個月以內資料)

2、DB方面沒有限制併發。(需要dba一起參與)

3、sql未考慮LIMIT分頁過大,查詢效能問題。(索引延遲關聯,本文重點 或者 限制分頁上限

 

 

執行結果:18.94s (結果受到機器峰值影響,可能低一些,可能更高)

 

sql執行計劃 :

 

表結構:

CREATE TABLE `cp_score_log` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
   ......
  `m_shopid` int(10) DEFAULT '0' COMMENT '總店id
', ...... `add_time` int(10) DEFAULT '0', `......PRIMARY KEY (`id`), KEY `idx_cardno` (`card_no`), KEY `idx_shopid` (`shopid`) USING BTREE, KEY `idx_orderid` (`order_id`), KEY `idx_shopid_add_time_score` (`shopid`,`add_time`,`score`), KEY `idx_mshopid` (`m_shopid`,`add_time`) USING BTREE ) ENGINE
=InnoDB AUTO_INCREMENT=24130302 DEFAULT CHARSET=utf8 COMMENT='積分記錄表'

 

表的資料基本在2400萬左右未進行拆分,通過檢視sql和執行計劃發現已經命中索引【idx_mshopid】,但是查詢效率仍然很低。拋開其他問題只針對sql來說,核心的問題出在LIMIT。

MySQL中 【LIMIT offset, m】並不是跳過offset然後取m行資料,而是直接取【offset+m】行資料,丟棄前offset行返回m行。因此查詢的效率就特別的低,特別當offset特別大的時候。

 

針對上面提出第3點問題,可以考慮使用後索引延遲關聯,即 通過建立中間表覆蓋索引查詢返回需要的主鍵,再根據主鍵關聯原表獲得需要的資料。 同時限制最大的分頁數量,比如百度最發分頁即為79頁 。

SELECT
    *
FROM
    cp_score_log
INNER JOIN (
    SELECT
        id,
        add_time
    FROM
        coupons.cp_score_log
    WHERE
        `m_shopid` = 40861
    AND `add_time` BETWEEN 1483200000
    AND 1544543999
    ORDER BY
        add_time DESC
    LIMIT LIMIT 118000 ,1000
) b ON cp_score_log.id = b.id
ORDER BY
    b.add_time DESC;

 

 

執行結果:0.7s (天差地別)

 

 sql執行計劃:

 

  由此可見,使用sql延遲關聯sql效率確實提升明顯。但是並非能解決所以問題,當表資料過大已經資料庫併發提高時,還是會出現查詢慢甚至拖垮db當風險。所以因綜合考慮,將請求量削峰。