目前系統(tǒng)的 Stat 表以每天 20W 條的數(shù)據(jù)量增加,盡管已經(jīng)把超過3個月的數(shù)據(jù) dump 到其他地方,但表中仍然有接近 2KW 條數(shù)據(jù),容量接近 2GB。
Stat 表已經(jīng)加上索引,直接 select … where … limit 的話,速度還是很快的,但一旦涉及到 group by 分頁,就會變得很慢。
據(jù)觀察,7天內(nèi)的 group by 需要 35~50s 左右。運營反映體驗極其不友好。
于是上網(wǎng)搜索 MySQL 分區(qū)方案。發(fā)現(xiàn)網(wǎng)上的基本上都是在系統(tǒng)性地講解 partition 的概念和種類,以及一些實驗性質(zhì)的效果,并不貼近實戰(zhàn)。
通過參考 MySQL手冊以及自己的摸索,最終在當前系統(tǒng)中實現(xiàn)了分區(qū),因為記錄一下。
分區(qū)類型的選擇
Stat 表本身是一個統(tǒng)計報表,所以它的數(shù)據(jù)都是按日期來存放的,并且熱數(shù)據(jù)一般只限于當天,以及7天內(nèi)。所以我選擇了 Range 類型來進行分區(qū)。
為當前表創(chuàng)建分區(qū)
因為是對已有表進行改造,所以只能用 alter 的方式:
ALTER TABLE stat
PARTITION BY RANGE(TO_DAYS(dt)) (
PARTITION p0 VALUES LESS THAN(0),
PARTITION p190214 VALUES LESS THAN(TO_DAYS('2019-02-14')),
PARTITION pm VALUES LESS THAN(MAXVALUE)
);
這里有2點要注意:
一是 p0 分區(qū),這是因為 MySQL(我是5.7版) 有個 bug,就是不管你查的數(shù)據(jù)在哪個區(qū),它都會掃一下第一個區(qū),我們每個區(qū)的數(shù)據(jù)都有幾十萬條,掃一下很是肉疼啊,所以為了避免不必要的掃描,直接弄個0數(shù)據(jù)分區(qū)就行了。
二是 pm 分區(qū),這個是最大分區(qū)。假如不要 pm,那你存 2019-02-15 的數(shù)據(jù)就會報錯。所以 pm 實際上是給未來的數(shù)據(jù)一個預(yù)留的分區(qū)。
定期擴展分區(qū)
由于 MySQL 的分區(qū)并不能自己動態(tài)擴容,所以我們要寫個代碼為它動態(tài)的增加分區(qū)。
增加分區(qū)需要用到 REORGANIZE 命令,它的作用是對某個分區(qū)重新分配。
比如明天是 15 號,那我們要給 15 號也增加個分區(qū),實際上就是把 pm 分區(qū)拆分成2個分區(qū):
ALTER TABLE stat
REORGANIZE PARTITION pm INTO (
PARTITION p190215 VALUES LESS THAN(TO_DAYS('2019-02-15')),
PARTITION pm VALUES LESS THAN(MAXVALUE)
);
這里就涉及到一個問題,即如何獲得當前表的所有分區(qū)?網(wǎng)上有挺多方法,但我試了下感覺還是先 show create table stat 然后用正則匹配出所有分區(qū)更方便一點。
定期刪除分區(qū)
隨著數(shù)據(jù)庫越來越大,我們肯定是要清除舊的數(shù)據(jù),同時也要清除舊的分區(qū)。
這個也比較簡單:
ALTER TABLE stat DROP PARTITION p190214, p190215