存儲優化(2)-排序引發的慢查詢優化

摘要

排序引發的慢查詢,一般不是那麼容易發現,常常和數據分佈有關係。每每在業務剛開始時並無什麼問題,可是隨着業務的發展,數據分佈呈現一種特定的規律,致使了慢查詢,或者並非什麼慢查詢,可是隨着併發請求數增長,數據庫的IOPS使用率變高,進一步致使cpu/內存使用率飆高。形成線上故障。redis

問題

由於排序引發的問題遇到不少次sql

例1:某日收到線上cpu告警

而後查看慢sql日誌 大量的慢查詢指向了這個查詢mongodb

SELECT
        id,
        prize_id,
        user_id,
        name,
		biz_id
        FROM play 
        WHERE biz_id = xx
        AND status = 1
        AND prize_type = '大獎'
        ORDER BY id DESC
        LIMIT 0, 10
複製代碼

play是抽獎記錄表,sql是查抽中獎品的前10個大獎中獎者,來吸引其餘用戶參與抽獎,biz_id建了索引數據庫

例2 某日上線一個新功能,在第五次壓測時,數據庫cpu告警

查看數據庫慢日誌,沒有一條慢sql(耗時>100ms)。最後經過查閱代碼,sql調用統計。發現有大量下面的SQL調用緩存

SELECT
        id,
        commit_id
        FROM commit_record
        WHERE biz_id = 'xxx' 
        AND id >=  #{fromId}
       AND id <= #{toId}
複製代碼

biz_id有索引bash

例3 某日線上服務報API響應時間超過X秒

經過查看應用日誌,發現大量com.mongodb.MongoSocketReadTimeoutException:mongo的錯誤。通過多重定位,發現從庫的IOPS使用率快接近100%了,同時發現有些慢查詢併發

"query":{"find":"historyRecord","filter":{"bizId":1234567,"version":23},"sort":{"_id":-1},"limit":1}}
複製代碼

索引是bizId,version的聯合索引優化

問題分析

這幾個查詢形成的線上問題的形式雖然各有不一樣,但本質上都是同樣,沒法利用索引排序,須要用到數據庫排序,當內存夠大或沒超過排序上限時,就會在內存中排序,這樣單個查詢相對比較快,可是併發量高了,內存容量不夠了,須要進行磁盤排序時,就會變得很慢。spa

而後通過仔細觀察,發現容易寫出這種語句,忽視了排序形成的風險。經常是根據主鍵排序。開發者容易想固然的覺得主鍵是有索引的,因此排序會走索引,因此不會有什麼大問題。但其實像例子中那些案例,都是沒法利用索引排序的。 曾經在mongo索引篇介紹聯合索引如何建立時也提到過。.net

總結一下,形成數據庫服務問題主要根由是

  1. 查詢沒有利用到索引排序
  2. 索引過濾後下面數據仍然有不少,須要掃描排序的數據不少
  3. 請求的併發量很高,數據庫IOPS使用率高,內存佔用高。

問題解決

首先,平常開發時避免寫出這種SQL,尤爲針對數據量比較大的表。或者索引下數據分佈可能不均勻的狀況。

線上解決 收到線上警告,發現是此類問題。

  1. 判斷業務側可否降級,即減小此類查詢。確保不要影響其餘業務。
  2. 數據庫升級配置(須要作到對業務無影響)

線上問題的臨時解決方案只能解一時燃煤之急,真正的解決問題仍是須要從查詢着手。

查詢優化

  1. 業務側避免此類查詢 從業務側分析,是否是須要此類查詢。好比例3,bizId,version_id是否是自己能夠做爲有序的,版本號version_id能夠設計成有序的,這樣就不須要根據主鍵_id來保持有序
  2. 減小併發 是否是全部的這類查詢都是必須的,能不能接受緩存。
  3. 引入其餘存儲方案 好比例1,業務須要查詢按照時間順序的中大獎的前N我的。這個業務側能夠將數據保存到在redis中,listz中存topN的數據。而後發現有中大獎的人,扔到redis隊列便可。
  4. 增長一個聯合索引 好比例3能夠增長一個bizId,version,_id聯合索引
    "query":{"find":"historyRecord","filter":{"bizId":1234567,"version":23},"sort":{"_id":-1},"limit":1}}
    複製代碼
相關文章
相關標籤/搜索