該用快取還是得用快取
後臺用了一年多,現在查詢報表變得很卡,大概要15~180秒,運營經常反映報表載入不出來,我去伺服器一看日誌,原來查詢花了200多秒,nginx直接超時了……
先優化一下 SQL
我的想法是優化報表的 SQL 語句,因為我發現查個 sql 居然加起來要 10幾秒。
我們的 sql 分為2條,一條是先查總數,這是為了下一步分頁用。另一條是查詳情。
查總數的 sql 我看了下,index 都有用到,但是在 explain 中多出了 using temporary; using file sort; 這2項。於是我把 group by date 去掉,這2項就沒了。
於是吭哧吭哧地把資料都加載出來,然後在程式碼裡 group by,雖然比較費記憶體和 CPU,但總比讓資料庫做好啊對不對?
後來想著對第二條也這樣優化,但是發現不行,因為第二條有個分頁,不 group by 的話,就沒法分頁了……這個暫時沒想到替代方法。可憐程式碼已經寫了2小時了,只好先不用了。
程式碼寫完了,本地測試一下,發現查詢速度從 9s 降到 1s,果然有效!
於是趕緊弄上伺服器,然後一試,變成 32s 了!而且 cpu 、記憶體狂漲!
無語了,趕緊回滾了。
事後分析,大概是因為資料量太大,伺服器也處理不過來了……
試試快取?
這事只好作罷,心裡想,慢點就慢點吧,反正也是自己人用。
但是渠道那邊又來找了,說是我們的 api 拉取超時了。這下就比較難辦了,渠道那邊超時,可能會影響我們正常的業務。況且這種現象如果聽之任之,最終可能引發雪崩效應,到時就麻煩了。
但是現在資料庫優化很蛋疼,怎麼辦呢?這時還是應該從業務角度考慮,渠道要的資料並不是實時資料,所以完全可以用快取頂一下嘛。
django 的話,直接用 django-cacheops 就行了,挺好用的,直接在 queryset 後面加個 .cache() 就可以啟用快取了,當然配置裡面要設定一下,不然它預設對所有查詢都快取就不好了,畢竟運營要看到的是實時資料。
程式碼改好了,部署上去,載入時間立即縮短了10倍!而且不是光 api 介面,連報表的查詢也大大縮短了!真是意外的驚喜啊。
所以從這件事可以總結出2個道理:
- sql 查詢變慢了,不一定是這條 sql 本身,可能是慢查詢太多,導致它被拖累了
- 快取一定要用上,減少資料庫的壓力是非常有必要的。
redis 的問題
不過,剛高興沒多久呢,也就1、2分鐘吧,介面就報 500 錯誤了,異常資訊提示Connection closed by server
,鬱悶,趕緊回滾。然後就在本地測,結果一點問題沒有。我猜可能是配置不一樣,但具體是哪個引數我也不知道了。
然後就是上網查資料,發現一個引數 timeout 貌似有用。分別在本地和伺服器端看了下,原來本地 timeout = 0,伺服器是 timeout = 60 。把它改過來之後,終於正常了。
不過呢,最近遇到的 redis 的問題挺多的,比如伺服器上經常遇到use of closed network connection
,這個就挺鬱悶的,完全不知道怎麼下手。
現在疏理一下,timeout 是 redis 用來控制客戶端連線的。如果 timeout > 0 的話,如果客戶端持續 timeout 秒沒有活動,redis 就會關閉這個連線。
如果設定成 timeout = 0,就表示永遠不關閉。
問題在於,admin 伺服器請求量小,確實可能 60s 裡沒有活動,redis 可以關閉它,但是廣告伺服器請求量很大,為什麼還是會關閉呢?