MySQL千萬(wàn)表歸檔的項(xiàng)目實(shí)踐
概述
隨著項(xiàng)目數(shù)據(jù)量的急劇增長(zhǎng),為了優(yōu)化性能提升產(chǎn)品體驗(yàn)感,我們決定對(duì)數(shù)據(jù)進(jìn)行歸檔處理。歸檔策略為實(shí)時(shí)數(shù)據(jù)僅保留6個(gè)月,超過(guò)期限的數(shù)據(jù)將被歸檔至歷史表中,在此過(guò)程中,我們遇到了數(shù)據(jù)庫(kù)主從延遲的問(wèn)題,下面將進(jìn)行分析去解決
背景
硬件:阿里云MySQL主從服務(wù)
項(xiàng)目:深度使用主從庫(kù),非操作類處理大多都查從庫(kù)
涉及表:主要以訂單及賬務(wù)業(yè)務(wù)為中心的表
數(shù)據(jù)量:最大表當(dāng)前數(shù)據(jù)量接近五千余萬(wàn)
注:以下歸檔將不分析歸檔后業(yè)務(wù)影響,需結(jié)合自身業(yè)務(wù)去考量相關(guān)的善后工作(例:業(yè)務(wù)該如何查詢歷史數(shù)據(jù),報(bào)表該如何跑歷史數(shù)據(jù)等)
歸檔方案
在歸檔實(shí)踐中,我們是以分布式調(diào)度存儲(chǔ)過(guò)程進(jìn)行以減少網(wǎng)絡(luò)開(kāi)銷,但為避免業(yè)務(wù)敏感以及方案通用性,下面將以業(yè)務(wù)程序代碼講解歸檔步驟
# 演示表 DROP TABLE IF EXISTS `biz_order`; CREATE TABLE `biz_order` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id', `order_no` varchar(20) NOT NULL COMMENT '訂單編號(hào)', `order_status` tinyint NOT NULL COMMENT '訂單狀態(tài)(1:成功 2:失敗 3:支付中)', ..... 省略N個(gè)字段 `gmt_create` datetime NOT NULL COMMENT '創(chuàng)建時(shí)間', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '訂單表' ROW_FORMAT = Dynamic; # 這里非常重要,歷史表必需與原表結(jié)構(gòu)保持一致,即使可能原表上會(huì)有N個(gè)索引影響遷移的性能 CREATE TABLE biz_order_history LIKE biz_order; # 歸檔日志記錄表 DROP TABLE IF EXISTS `biz_archive_log`; CREATE TABLE `biz_archive_log` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '自增id', `src_table` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '歸檔的表名', `sql_text` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '執(zhí)行的腳本', `paras` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '實(shí)際執(zhí)行參數(shù)', `flag` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '是否執(zhí)行成功:成功,失敗', `result_text` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL COMMENT '執(zhí)行結(jié)果描述', `row_num` int NULL DEFAULT NULL COMMENT '影響行數(shù)', `exec_times` int NULL DEFAULT NULL COMMENT '執(zhí)行耗時(shí)ms', `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) USING BTREE, INDEX `flag`(`flag` ASC) USING BTREE, INDEX `create_time`(`create_time` ASC) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci COMMENT = '歸檔日志表' ROW_FORMAT = Dynamic;
準(zhǔn)備工作都完成后下面正式開(kāi)始?xì)w檔流程,下面將以 biz_order表 為例
- 確定biz_order表歸檔條件,即:哪些數(shù)據(jù)需要?dú)w檔,哪些數(shù)據(jù)能歸檔?
以order表為例,那么只能歸檔狀態(tài)為: 1:成功 2:失敗 的訂單(實(shí)踐時(shí)這里需要結(jié)合業(yè)務(wù)考量清楚) - 當(dāng)確定歸檔條件后,我們下一步需要做的事就是需要確定這些條件有沒(méi)有索引
# 因?yàn)槲覀兒罄m(xù)將會(huì)以此條件去刪除原表數(shù)據(jù),繼而寫(xiě)入目標(biāo)表(若無(wú)索引在千萬(wàn)級(jí)表中想刪除部分?jǐn)?shù)據(jù)那可真難哦) ALTER TABLE `biz_order` ADD INDEX `idx_create_status`(`gmt_create`, `order_status`);
現(xiàn)在我們知道了歸檔方向,也做好了歸檔的前期準(zhǔn)備,那么該如何進(jìn)行歸檔呢?可以一次性直接將6個(gè)月前數(shù)據(jù)歸檔嗎?
顯然這是不行的,因?yàn)榇笈縿h除可能會(huì)鎖表,會(huì)影響正常的實(shí)時(shí)業(yè)務(wù),這是我們不能接受的現(xiàn)象
這里給到一種歸檔策略:分段歸檔
顧名思義,我們可以將歷史要?dú)w檔的數(shù)據(jù)拆分成一個(gè)個(gè)小段,然后對(duì)這些小段進(jìn)行歸檔操作,少量多次的原理
例:minute按分鐘循環(huán)歸檔 tenMinutes按十分鐘循環(huán)歸檔 hour按小時(shí)循環(huán)歸檔 day按天循環(huán)歸檔 month按月循環(huán)歸檔
歸檔實(shí)踐
# 拿按小時(shí)歸檔舉例 LocalDate startDate = '2023-07-01'; # 舉例直觀說(shuō)明,非實(shí)際Api LocalDate endDate = '2023-07-31'; for (LocalDate tmpDate = startDate; tmpDate.isBefore(endDate; tmpDate = tmpDate.plusHours(1)) { 1. 開(kāi)啟事務(wù) 2. insert into biz_order_history select * from payment where create_time >= '2023-07-01 00:00' and create_time < '2023-07-01 01:00' and order_status in (1, 2); 3. delete from biz_order where create_time >= '2023-07-01 00:00' and create_time < '2023-07-01 01:00' and order_status in (1, 2); # 以上任何一個(gè)步驟異常都將進(jìn)行回滾并記錄 biz_archive_log 日志 # 若插入 biz_archive_log 日志也失敗則忽略,不影響業(yè)務(wù) 4. insert into biz_archive_log 5. 提交事務(wù) } # 具體使用什么歸檔方式取決于具體要?dú)w檔的表,需慎重評(píng)估:粒度太大會(huì)執(zhí)行較慢,形成慢SQL大事務(wù)影響業(yè)務(wù)。粒度太小則也會(huì)造成歸檔時(shí)間過(guò)長(zhǎng),從服務(wù)器延遲高(埋個(gè)伏筆,下面會(huì)提到)
到目前為止,我們的歸檔也已經(jīng)做完了,可以開(kāi)始測(cè)試,繼而上線了
因這個(gè)歸檔方案我們之前在其他項(xiàng)目也用過(guò),就沒(méi)搭相關(guān)環(huán)境進(jìn)行深度性能測(cè)試,也正是因?yàn)檫@里讓我們?cè)诰€上踩了大坑
2024年12月的一天凌晨,此歸檔版本正式上線,根據(jù)既定好的驗(yàn)證流程,一步步執(zhí)行,在這過(guò)程中并沒(méi)有什么異常,性能也符合我們預(yù)期,隨即我們開(kāi)始跑其他歷史表,但天有不測(cè)風(fēng)云,問(wèn)題來(lái)了
主從延遲自9點(diǎn)多的100多秒然后到10點(diǎn)左右達(dá)到了巔峰的800多秒,業(yè)務(wù)也隨著主從延遲的增加而炸鍋,因上面提及的項(xiàng)目深度使用從庫(kù),這時(shí)導(dǎo)致很多業(yè)務(wù)已經(jīng)無(wú)法正常使用,隨即我們陸續(xù)停掉了歸檔任務(wù)…
主從延遲出現(xiàn)原因分析
# 首先分析異步主從為什么會(huì)有延遲(這里使用的是異步復(fù)制) a. 主庫(kù)執(zhí)行完一個(gè)事務(wù),寫(xiě)入binlog,我們把這個(gè)時(shí)刻記為T1 b. 主庫(kù)同步數(shù)據(jù)給從庫(kù),從庫(kù)接收完這個(gè)binlog的時(shí)刻,記錄為T2 c. 從庫(kù)執(zhí)行完這個(gè)事務(wù),這個(gè)時(shí)刻記錄為T3 所謂主從延遲,其實(shí)就是指同一個(gè)事務(wù),在從庫(kù)執(zhí)行完的時(shí)間和在主庫(kù)執(zhí)行完的時(shí)間差值,即T3-T1 # 主從延遲出現(xiàn)原因 a. 機(jī)器性能差 b. 從庫(kù)的壓力大 c. 大事務(wù) d. 網(wǎng)絡(luò)延遲 e. 從庫(kù)數(shù)量多,復(fù)制壓力大 f. 低版本只支持單線程復(fù)制,高版本是支持多線程復(fù)制
主從延遲解決思路分析
因我們使用的是阿里云MySQL8.0主從服務(wù),有且只有一個(gè)從庫(kù)
所以a/d/e/f原因可以排除掉,那么還剩下b/c兩個(gè)原因
當(dāng)時(shí)提工單聯(lián)系阿里云售后,給出的答復(fù)也大致是我們同步的操作是否是大事務(wù),以及同步數(shù)據(jù)量過(guò)大導(dǎo)致負(fù)載較高,繼而出現(xiàn)主從延遲高的問(wèn)題
順著這個(gè)思路,dba同事提出2個(gè)方案
1. 將從服務(wù)器配置為代理地址,歸檔時(shí)在代理服務(wù)器將從庫(kù)切換到主庫(kù),從而使得整個(gè)系統(tǒng)都運(yùn)行在主庫(kù)之上,此時(shí)歸檔雖然會(huì)導(dǎo)致主從延遲過(guò)高,但不影響業(yè)務(wù)(此方案同樣可在大表加索引等場(chǎng)景使用)
缺點(diǎn):主庫(kù)在此時(shí)壓力會(huì)劇增
2. 開(kāi)始重新改造存儲(chǔ)過(guò)程,在每個(gè)批次(hour)歸檔完成后,強(qiáng)制sleep1秒來(lái)降低從服務(wù)器的復(fù)制壓力
這里我們批次hour實(shí)際上事務(wù)并不算大,若hour級(jí)別數(shù)據(jù)量大可以考慮實(shí)現(xiàn)minute、tenMinutes來(lái)進(jìn)一步的降低事務(wù)的粒度
因第一個(gè)方案是需要重新配置改造,故我們選擇第二個(gè)方案先進(jìn)行嘗試
emm 如同柳暗花明又一村,這時(shí)歸檔數(shù)據(jù)的延遲已經(jīng)降到1s以下,這時(shí)幾乎可以忽略不計(jì),對(duì)業(yè)務(wù)不會(huì)造成影響
注:從庫(kù)版本為8.0.28,slave_parallel_workers配置為8
結(jié)語(yǔ)
暫停策略是一個(gè)臨時(shí)解決方案,但它在一定程度上緩解了主從延遲問(wèn)題,保證了歸檔操作的順利進(jìn)行。我們將繼續(xù)監(jiān)控?cái)?shù)據(jù)庫(kù)性能,并尋求更長(zhǎng)期的解決方案,以確保數(shù)據(jù)的一致性和歸檔操作的效率。
到此這篇關(guān)于MySQL千萬(wàn)表歸檔的項(xiàng)目實(shí)踐的文章就介紹到這了,更多相關(guān)MySQL千萬(wàn)表歸檔內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MySQL 視圖 第1349號(hào)錯(cuò)誤解決方法
把下面SQL里的SELECT單獨(dú)執(zhí)行,沒(méi)有問(wèn)題,但是用來(lái)CREATE VIEW 就報(bào)錯(cuò)了.2008-03-03mysql優(yōu)化取隨機(jī)數(shù)據(jù)慢的方法
mysql取隨機(jī)數(shù)據(jù)慢,怎么辦?下面小編與大家一起來(lái)看看mysql取隨機(jī)數(shù)據(jù)慢優(yōu)化的過(guò)程。2013-11-11MySQL多層級(jí)結(jié)構(gòu)-區(qū)域表使用樹(shù)詳解
前面我們大概介紹了一下樹(shù)結(jié)構(gòu)表的基本使用。在我們項(xiàng)目中有好幾塊有用到多層級(jí)的概念。下面我們哪大家都比較熟悉的區(qū)域表來(lái)做演示2016-07-07遠(yuǎn)程連接mysql數(shù)據(jù)庫(kù)注意事項(xiàng)記錄(遠(yuǎn)程連接慢skip-name-resolve)
有時(shí)候我們需要遠(yuǎn)程連接mysql數(shù)據(jù)庫(kù),就需要注意下面的問(wèn)題,方便大家解決,腳本之家小編特為大家準(zhǔn)備了一些資料2012-07-07mysql遠(yuǎn)程跨庫(kù)聯(lián)合查詢的示例
本文主要介紹了mysql遠(yuǎn)程跨庫(kù)聯(lián)合查詢的示例,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03Mysql報(bào)錯(cuò)1292:Incorrect datetime value for 
本文主要介紹了Mysql報(bào)錯(cuò)1292:Incorrect datetime value for column create_time at row 1 解決方案,1292 是指插入或更新操作時(shí),日期或時(shí)間值不正確引起的錯(cuò)誤,下面就來(lái)介紹一下2024-02-02