近期,數據中心系統負荷大,mysql伺服器的CPU動輒達到90%以上。代碼和數據表存在很大優化空間。 這裡分享一個定時任務批量處理數據的優化過程。 先介紹定時任務 先介紹下麵2張數據表 欄位 數據量 platform_order 平臺交易訂單表 有超過50多個欄位。 包括 主鍵自增id、客戶id、客 ...
近期,數據中心系統負荷大,mysql伺服器的CPU動輒達到90%以上。代碼和數據表存在很大優化空間。
這裡分享一個定時任務批量處理數據的優化過程。
先介紹定時任務
先介紹下麵2張數據表
欄位 | 數據量 | |
platform_order 平臺交易訂單表 |
有超過50多個欄位。 包括 主鍵自增id、客戶id、客戶名稱(冗餘欄位)、服務商id(levy_id)、服務商名稱(levy_name,冗餘欄位)、 付款方式、付款狀態、收款人、收款人收款賬號(卡號/支付寶/微信)、項目id、付款金額、渠道商、銷售代表、 創建時間、最近更新時間、付款完成時間,等等。 |
550w,每天增量3w |
levy_info 服務商基礎信息表 |
欄位包括 服務商id(levy_id)、服務商名稱(levy_name),等若幹欄位 | 50條,很少新增 |
項目程式里有一個定時任務,每間隔5分鐘,定期為platform_order的冗餘欄位levy_name賦值。也就是,根據levy表裡的信息來更新platform_order表。
最初的程式實現
我相信這是絕大多數程式員的實現方式。
【第一步】求count: select count(1) from platform_order where levy_name is null
【第二步】分頁從數據表獲取levy_name為null的記錄,例如每頁1000條,放到List集合里。
【第三步】遍歷List集合里的元素,根據記錄的levy_id去查levy_info表,得到levy_name,執行SQL:update platform_order set levy_name=#{levy_name} where id=#{id}
這個定時任務啟動後,不停刷日誌,耗時≈3min
改進的程式實現
【第一步】求count:SQL同上
【第二步】如果count>0,則執行一條update語句:update platform_order a join levy_info b on a.levy_id=b.levy_id set a.levy_name=b.levy_name where a.levy_name is null
這個實現方式,java著手少了許多代碼,不過,資料庫倒是出現慢sql了。第二步的update語句耗時10~12s。
count耗時≈2s,整體耗時≈15s
洪荒之力,優化到200ms以內
【第一步】
不再是傻瓜式地一個 levy_name is null 條件了。而是再加一個id>#{maxId}條件。 maxId 值從哪裡來?每次定時任務執行完後將最大記錄id緩存起來。當然,服務啟動後第一次是沒有緩存的,就讓maxId=0。
再者,執行的sql不是簡單的count,而是 select levy_id, min(id) as minId,max(id) as maxId from platform_order where id>#{maxId} and levy_name is null group by levy_id
【第二步】
上面的分組查詢得到一個List集合,遍歷集合元素,同樣根據levy_id查levy_info表得到levy_info記錄。
然後,如果你跟得上我的節奏,你應該能猜到,執行這樣一個SQL:
update platform_order set levy_name=#{levy_name} where levy_id=#{levy_id} and id between #{minId} and #{maxId} and levy_name is nullView Code
要提到的一點是,根據levy_id獲取levy_info記錄,我使用了緩存,緩存24h,是不是很豪橫~
【第三步】
緩存最大id ---> maxId
這麼優化之後,job的耗時在100ms~200ms之間,這個耗時足可以令伙伴們尖叫!
當看到一些不好的代碼時,會發現我還算優秀;當看到優秀的代碼時,也才意識到持續學習的重要!--buguge
本文來自博客園,轉載請註明原文鏈接:https://www.cnblogs.com/buguge/p/16812025.html