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