目前系統(tǒng)的 Stat 表以每天 20W 條的數(shù)據(jù)量增加,盡管已經把超過3個月的數(shù)據(jù) dump 到其他地方避乏,但表中仍然有接近 2KW 條數(shù)據(jù),容量接近 2GB。
Stat 表已經加上索引慧脱,直接 select … where … limit 的話,速度還是很快的贺喝,但一旦涉及到 group by 分頁菱鸥,就會變得很慢宗兼。
據(jù)觀察,7天內的 group by 需要 35~50s 左右氮采。運營反映體驗極其不友好殷绍。
于是上網(wǎng)搜索 MySQL 分區(qū)方案。發(fā)現(xiàn)網(wǎng)上的基本上都是在系統(tǒng)性地講解 partition 的概念和種類鹊漠,以及一些實驗性質的效果主到,并不貼近實戰(zhàn)。
通過參考 MySQL手冊以及自己的摸索躯概,最終在當前系統(tǒng)中實現(xiàn)了分區(qū)登钥,因為記錄一下。
分區(qū)類型的選擇
Stat 表本身是一個統(tǒng)計報表娶靡,所以它的數(shù)據(jù)都是按日期來存放的牧牢,并且熱數(shù)據(jù)一般只限于當天,以及7天內姿锭。所以我選擇了 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ù)一個預留的分區(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