MySQL億級(jí)大表安全添加字段的三種方案
1. 億級(jí)大表 ALTER 的風(fēng)險(xiǎn)評(píng)估
1.1 直接執(zhí)行 ALTER 的潛在問題
ALTER TABLE `orders` ADD COLUMN `is_priority` TINYINT NULL DEFAULT 0;
- 鎖表時(shí)間估算(經(jīng)驗(yàn)值):
- MySQL 5.6:約 2-6小時(shí)(完全阻塞)
- MySQL 5.7+:10-30分鐘(短暫阻塞寫入)
- 業(yè)務(wù)影響:
- 所有讀寫請(qǐng)求超時(shí)
- 連接池耗盡(
Too many connections
) - 可能觸發(fā)高可用切換(如 MHA)
1.2 關(guān)鍵指標(biāo)檢查
-- 查看表大小(GB) SELECT table_name, ROUND(data_length/1024/1024/1024,2) AS size_gb FROM information_schema.tables WHERE table_schema = 'your_db' AND table_name = 'orders'; -- 檢查當(dāng)前長(zhǎng)事務(wù) SELECT * FROM information_schema.innodb_trx WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 60;
2. 三種安全方案對(duì)比
方案 | 工具 | 執(zhí)行時(shí)間 | 阻塞情況 | 適用版本 | 復(fù)雜度 |
---|---|---|---|---|---|
Online DDL | 原生MySQL | 30min-2h | 短暫阻塞寫 | 5.7+ | ★★☆ |
pt-osc | Percona Toolkit | 2-4h | 零阻塞 | 所有版本 | ★★★ |
gh-ost | GitHub | 1-3h | 零阻塞 | 所有版本 | ★★★★ |
3. 方案一:MySQL 原生 Online DDL(5.7+)
3.1 最優(yōu)執(zhí)行命令
ALTER TABLE `orders` ADD COLUMN `is_priority` TINYINT NULL DEFAULT 0, ALGORITHM=INPLACE, LOCK=NONE;
3.2 監(jiān)控進(jìn)度(另開會(huì)話)
-- 查看 DDL 狀態(tài) SHOW PROCESSLIST; -- 查看 InnoDB 操作進(jìn)度 SELECT * FROM information_schema.innodb_alter_table;
3.3 預(yù)估執(zhí)行時(shí)間(經(jīng)驗(yàn)公式)
時(shí)間(min) = 表大小(GB) × 2 + 10
- 假設(shè)表大小 50GB → 約 110分鐘
4. 方案二:pt-online-schema-change 實(shí)戰(zhàn)
4.1 安裝與執(zhí)行
# 安裝 Percona Toolkit sudo yum install percona-toolkit # 執(zhí)行變更(自動(dòng)創(chuàng)建觸發(fā)器) pt-online-schema-change \ --alter "ADD COLUMN is_priority TINYINT NULL DEFAULT 0" \ D=your_db,t=orders \ --chunk-size=1000 \ --max-load="Threads_running=50" \ --critical-load="Threads_running=100" \ --execute
4.2 關(guān)鍵參數(shù)說明
參數(shù) | 作用 | 推薦值(億級(jí)表) |
---|---|---|
--chunk-size | 每次復(fù)制的行數(shù) | 500-2000 |
--max-load | 自動(dòng)暫停閾值 | Threads_running=50 |
--critical-load | 強(qiáng)制中止閾值 | Threads_running=100 |
--sleep | 批次間隔時(shí)間 | 0.5(秒) |
4.3 Java 應(yīng)用兼容性處理
// 在觸發(fā)器生效期間,需處理重復(fù)主鍵異常 try { orderDao.insert(newOrder); } catch (DuplicateKeyException e) { // 自動(dòng)重試或走降級(jí)邏輯 orderDao.update(newOrder); }
5. 方案三:gh-ost 高級(jí)用法
5.1 執(zhí)行命令(無需觸發(fā)器)
gh-ost \ --database="your_db" \ --table="orders" \ --alter="ADD COLUMN is_priority TINYINT NULL DEFAULT 0" \ --assume-rbr \ --allow-on-master \ --cut-over=default \ --execute
5.2 核心優(yōu)勢(shì)
- 無觸發(fā)器設(shè)計(jì):避免性能損耗
- 動(dòng)態(tài)限流:自動(dòng)適應(yīng)服務(wù)器負(fù)載
- 可交互控制:支持暫停/恢復(fù)
# 運(yùn)行時(shí)控制 echo throttle | nc -U /tmp/gh-ost.sock echo no-throttle | nc -U /tmp/gh-ost.sock
6. Java 應(yīng)用層適配策略
6.1 雙寫兼容模式(推薦)
// 在變更期間同時(shí)寫入新舊字段 public void createOrder(Order order) { order.setIsPriority(0); // 新字段默認(rèn)值 orderMapper.insert(order); // 兼容舊代碼 if (order.getV2() == null) { orderMapper.updateIsPriority(order.getId(), 0); } }
6.2 動(dòng)態(tài) SQL 路由
<!-- MyBatis 動(dòng)態(tài)字段映射 --> <insert id="insertOrder"> INSERT INTO orders (id, user_id, amount <if test="isPriority != null">, is_priority</if>) VALUES (#{id}, #{userId}, #{amount} <if test="isPriority != null">, #{isPriority}</if>) </insert>
7. 監(jiān)控與回滾方案
7.1 實(shí)時(shí)監(jiān)控指標(biāo)
# 監(jiān)控復(fù)制延遲(主從架構(gòu)) pt-heartbeat --monitor --database=your_db # 查看 gh-ost 進(jìn)度 tail -f gh-ost.log
7.2 緊急回滾步驟
# pt-osc 回滾(自動(dòng)清理臨時(shí)表) pt-online-schema-change --drop-new-table --alter="..." --execute # gh-ost 回滾 gh-ost --panic-on-failure --revert
8. 總結(jié)建議
首選方案:
- MySQL 8.0 → 原生
ALGORITHM=INSTANT
(秒級(jí)完成) - MySQL 5.7 →
gh-ost
(無觸發(fā)器影響)
- MySQL 8.0 → 原生
執(zhí)行窗口:
- 選擇業(yè)務(wù)流量最低時(shí)段(如凌晨 2-4 點(diǎn))
- 提前通知業(yè)務(wù)方準(zhǔn)備降級(jí)方案
驗(yàn)證流程:
-- 變更后檢查數(shù)據(jù)一致性 SELECT COUNT(*) FROM orders WHERE is_priority IS NULL;
- 后續(xù)優(yōu)化:
-- 添加完成后可改為 NOT NULL ALTER TABLE orders MODIFY COLUMN is_priority TINYINT NOT NULL DEFAULT 0;
通過合理選擇工具+應(yīng)用層適配,即使 1.35億條數(shù)據(jù) 的表也能實(shí)現(xiàn) 零感知 的字段添加。
以上就是MySQL億級(jí)大表安全添加字段的三種方案的詳細(xì)內(nèi)容,更多關(guān)于MySQL大表添加字段的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
解決Windows環(huán)境下安裝 mysql-8.0.11-winx64 遇到的問題
這篇文章主要介紹了Windows環(huán)境下安裝 mysql-8.0.11-winx64 遇到的問題及解決辦法 ,需要的朋友可以參考下2018-10-10Mysql查詢不需要Group?by的字段方法實(shí)例
在平時(shí)的開發(fā)任務(wù)中我們經(jīng)常會(huì)用到MYSQL的GROUP BY分組,用來獲取數(shù)據(jù)表中以分組字段為依據(jù)的統(tǒng)計(jì)數(shù)據(jù),下面這篇文章主要給大家介紹了關(guān)于Mysql查詢不需要Group?by字段方法的相關(guān)資料,需要的朋友可以參考下2024-04-04MySQL server has gone away的問題解決
本文主要介紹了MySQL server has gone away的問題解決,意思就是指client和MySQL server之間的鏈接斷開了,下面就來介紹一下幾種原因及其解決方法,感興趣的可以了解一下2024-07-07mysql語法時(shí)采用了雙引號(hào)““的錯(cuò)誤問題
錯(cuò)誤原因:使用雙引號(hào)定義表名和列名導(dǎo)致MySQL報(bào)錯(cuò),應(yīng)使用反引號(hào),修改方案:將雙引號(hào)改為反引號(hào),避免語法沖突,總結(jié):在MySQL中,正確使用反引號(hào)引用標(biāo)識(shí)符,確保SQL語句符合MySQL語法規(guī)則2024-10-10mysql中關(guān)于between和in的區(qū)別
這篇文章主要介紹了mysql中關(guān)于between和in的區(qū)別說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07