feat:一亿用户数据迁移
This commit is contained in:
406
docs/1亿数据迁移/应急预案.md
Normal file
406
docs/1亿数据迁移/应急预案.md
Normal file
@@ -0,0 +1,406 @@
|
||||
# 应急预案
|
||||
|
||||
## 常见问题及处理方案
|
||||
|
||||
### 问题1:双写失败率突然升高
|
||||
|
||||
**现象:**
|
||||
- 双写成功率从 99.9% 下降到 90%
|
||||
- 补偿队列快速堆积
|
||||
|
||||
**可能原因:**
|
||||
1. 新库连接池耗尽
|
||||
2. 新库性能问题(慢查询、锁等待)
|
||||
3. 网络问题
|
||||
4. 新库磁盘满
|
||||
|
||||
**处理步骤:**
|
||||
|
||||
1. **立即检查新库状态**
|
||||
```sql
|
||||
-- 查看连接数
|
||||
SHOW PROCESSLIST;
|
||||
|
||||
-- 查看慢查询
|
||||
SHOW FULL PROCESSLIST;
|
||||
|
||||
-- 查看锁等待
|
||||
SELECT * FROM information_schema.innodb_locks;
|
||||
```
|
||||
|
||||
2. **如果是连接池问题**
|
||||
- 临时扩大连接池配置
|
||||
- 重启应用实例
|
||||
|
||||
3. **如果是性能问题**
|
||||
- 分析慢查询,优化索引
|
||||
- Kill 掉异常的长事务
|
||||
|
||||
4. **如果无法快速解决**
|
||||
- 关闭双写开关
|
||||
- 等问题解决后重新开启
|
||||
- 补偿队列的数据后续补偿
|
||||
|
||||
**回滚方案:**
|
||||
```yaml
|
||||
migration:
|
||||
dual-write:
|
||||
enabled: false # 关闭双写
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 问题2:数据迁移影响线上性能
|
||||
|
||||
**现象:**
|
||||
- 老库 CPU 飙升到 90%
|
||||
- 接口响应时间变慢
|
||||
- 用户投诉
|
||||
|
||||
**可能原因:**
|
||||
1. 迁移脚本没有限流
|
||||
2. 迁移脚本查询没有索引
|
||||
3. 迁移时间选择不当(高峰期)
|
||||
|
||||
**处理步骤:**
|
||||
|
||||
1. **立即暂停迁移脚本**
|
||||
- 停止所有迁移任务
|
||||
- 观察老库 CPU 是否恢复
|
||||
|
||||
2. **分析慢查询**
|
||||
```sql
|
||||
-- 查看正在执行的查询
|
||||
SHOW FULL PROCESSLIST;
|
||||
|
||||
-- 查看慢查询日志
|
||||
SELECT * FROM mysql.slow_log ORDER BY start_time DESC LIMIT 10;
|
||||
```
|
||||
|
||||
3. **优化迁移脚本**
|
||||
- 增加 sleep 时间(100ms → 500ms)
|
||||
- 减少批次大小(1000 → 500)
|
||||
- 添加必要的索引
|
||||
- 选择低峰期迁移(凌晨 2-6 点)
|
||||
|
||||
4. **重新启动迁移**
|
||||
- 从断点继续迁移
|
||||
- 实时监控老库性能
|
||||
|
||||
**预防措施:**
|
||||
- 迁移前在测试环境压测
|
||||
- 设置 CPU 阈值,超过自动暂停
|
||||
- 错峰迁移
|
||||
|
||||
---
|
||||
|
||||
### 问题3:灰度读取新库失败率高
|
||||
|
||||
**现象:**
|
||||
- 新库读取成功率只有 95%
|
||||
- 频繁降级到老库
|
||||
- 用户偶尔看到数据不一致
|
||||
|
||||
**可能原因:**
|
||||
1. 新库数据不完整(迁移未完成)
|
||||
2. 新库性能问题
|
||||
3. 分片路由错误
|
||||
4. 新库某些分片故障
|
||||
|
||||
**处理步骤:**
|
||||
|
||||
1. **立即降低灰度比例**
|
||||
```yaml
|
||||
migration:
|
||||
read:
|
||||
gray-percent: 1 # 从 10% 降到 1%
|
||||
```
|
||||
|
||||
2. **分析失败原因**
|
||||
- 查看错误日志,定位具体问题
|
||||
- 检查是否有数据缺失
|
||||
- 检查是否有分片故障
|
||||
|
||||
3. **数据缺失问题**
|
||||
- 对比老库和新库数据
|
||||
- 补充缺失的数据
|
||||
- 重新校验
|
||||
|
||||
4. **性能问题**
|
||||
- 优化慢查询
|
||||
- 添加索引
|
||||
- 调整连接池配置
|
||||
|
||||
5. **分片故障**
|
||||
- 修复故障分片
|
||||
- 或者临时屏蔽故障分片
|
||||
|
||||
6. **问题解决后逐步放开**
|
||||
- 1% → 5% → 10% → 50% → 100%
|
||||
- 每次放开后观察 1-2 天
|
||||
|
||||
**回滚方案:**
|
||||
```yaml
|
||||
migration:
|
||||
read:
|
||||
from-new: false # 完全关闭新库读取
|
||||
gray-percent: 0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 问题4:数据不一致
|
||||
|
||||
**现象:**
|
||||
- 数据校验发现不一致率 > 1%
|
||||
- 用户反馈数据错误
|
||||
|
||||
**可能原因:**
|
||||
1. 双写失败未补偿
|
||||
2. 迁移脚本有 bug
|
||||
3. 并发更新导致数据覆盖
|
||||
4. 分片路由错误
|
||||
|
||||
**处理步骤:**
|
||||
|
||||
1. **立即停止灰度**
|
||||
```yaml
|
||||
migration:
|
||||
read:
|
||||
from-new: false
|
||||
gray-percent: 0
|
||||
```
|
||||
|
||||
2. **定位不一致的数据**
|
||||
- 导出不一致的 user_id 列表
|
||||
- 分析不一致的原因
|
||||
|
||||
3. **修复数据**
|
||||
- 以老库为准,修复新库数据
|
||||
- 或者重新迁移这部分数据
|
||||
|
||||
4. **修复 bug**
|
||||
- 如果是代码 bug,修复后重新部署
|
||||
- 如果是迁移脚本 bug,修复后重新迁移
|
||||
|
||||
5. **全量校验**
|
||||
- 修复后进行全量数据校验
|
||||
- 确保一致性 > 99.9%
|
||||
|
||||
6. **重新开始灰度**
|
||||
- 从 1% 开始重新灰度
|
||||
|
||||
**预防措施:**
|
||||
- 迁移前充分测试
|
||||
- 双写失败必须补偿
|
||||
- 定期数据校验
|
||||
|
||||
---
|
||||
|
||||
### 问题5:新库性能不如老库
|
||||
|
||||
**现象:**
|
||||
- 新库响应时间是老库的 2 倍
|
||||
- 新库 CPU 使用率高
|
||||
- 用户感知到变慢
|
||||
|
||||
**可能原因:**
|
||||
1. 索引缺失或不合理
|
||||
2. 分片策略不合理(数据倾斜)
|
||||
3. 连接池配置不合理
|
||||
4. 硬件配置不足
|
||||
|
||||
**处理步骤:**
|
||||
|
||||
1. **立即降低灰度比例**
|
||||
```yaml
|
||||
migration:
|
||||
read:
|
||||
gray-percent: 10 # 降低到 10%
|
||||
```
|
||||
|
||||
2. **分析慢查询**
|
||||
```sql
|
||||
-- 查看慢查询
|
||||
SELECT * FROM mysql.slow_log
|
||||
WHERE start_time > NOW() - INTERVAL 1 HOUR
|
||||
ORDER BY query_time DESC
|
||||
LIMIT 20;
|
||||
```
|
||||
|
||||
3. **优化索引**
|
||||
- 对比老库和新库的索引
|
||||
- 添加缺失的索引
|
||||
- 删除冗余的索引
|
||||
|
||||
4. **检查数据倾斜**
|
||||
```sql
|
||||
-- 检查每个分片的数据量
|
||||
SELECT COUNT(*) FROM users_0;
|
||||
SELECT COUNT(*) FROM users_1;
|
||||
...
|
||||
```
|
||||
- 如果数据倾斜严重,考虑调整分片策略
|
||||
|
||||
5. **调整配置**
|
||||
- 连接池大小
|
||||
- 缓存配置
|
||||
- 硬件资源(CPU、内存、磁盘)
|
||||
|
||||
6. **优化后重新灰度**
|
||||
- 从 10% 开始重新灰度
|
||||
- 观察性能是否改善
|
||||
|
||||
**回滚方案:**
|
||||
- 如果优化后仍然不行,考虑放弃分库分表
|
||||
- 或者重新设计分片策略
|
||||
|
||||
---
|
||||
|
||||
### 问题6:补偿队列堆积
|
||||
|
||||
**现象:**
|
||||
- 补偿队列长度持续增长
|
||||
- 从 1000 增长到 10 万
|
||||
- 补偿任务处理不过来
|
||||
|
||||
**可能原因:**
|
||||
1. 新库持续故障
|
||||
2. 补偿任务处理速度慢
|
||||
3. 补偿任务本身有 bug
|
||||
|
||||
**处理步骤:**
|
||||
|
||||
1. **检查新库状态**
|
||||
- 如果新库故障,先修复新库
|
||||
- 如果新库正常,继续下一步
|
||||
|
||||
2. **增加补偿任务并发度**
|
||||
- 增加补偿任务的线程数
|
||||
- 或者启动多个补偿任务实例
|
||||
|
||||
3. **分批处理**
|
||||
- 不要一次性处理所有队列
|
||||
- 分批处理,避免压垮新库
|
||||
|
||||
4. **如果队列过大**
|
||||
- 考虑临时关闭双写
|
||||
- 等队列处理完再重新开启
|
||||
|
||||
**预防措施:**
|
||||
- 补偿任务要有限流
|
||||
- 补偿队列要有长度告警
|
||||
- 双写失败率要及时告警
|
||||
|
||||
---
|
||||
|
||||
## 回滚决策树
|
||||
|
||||
```
|
||||
发现问题
|
||||
↓
|
||||
问题是否影响用户?
|
||||
↓ 是
|
||||
影响面 > 10%?
|
||||
↓ 是
|
||||
立即回滚
|
||||
↓ 否
|
||||
降低灰度比例,观察
|
||||
↓ 否
|
||||
问题是否可以快速修复(< 30分钟)?
|
||||
↓ 是
|
||||
修复问题,继续观察
|
||||
↓ 否
|
||||
回滚,等修复后重新上线
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 回滚操作手册
|
||||
|
||||
### 从阶段3回滚(关闭双写)
|
||||
```yaml
|
||||
migration:
|
||||
dual-write:
|
||||
enabled: false
|
||||
compensation:
|
||||
enabled: false
|
||||
```
|
||||
|
||||
**影响:** 新数据不再同步到新库
|
||||
|
||||
### 从阶段6回滚(关闭灰度)
|
||||
```yaml
|
||||
migration:
|
||||
read:
|
||||
from-new: false
|
||||
gray-percent: 0
|
||||
```
|
||||
|
||||
**影响:** 所有读取回到老库
|
||||
|
||||
### 从阶段7回滚(切回老库)
|
||||
```yaml
|
||||
migration:
|
||||
dual-write:
|
||||
enabled: true # 继续双写
|
||||
read:
|
||||
from-new: false # 读取切回老库
|
||||
gray-percent: 0
|
||||
```
|
||||
|
||||
**影响:** 完全回到老库
|
||||
|
||||
---
|
||||
|
||||
## 值班要求
|
||||
|
||||
### 值班时间
|
||||
- 阶段3-6:7×24 小时值班
|
||||
- 阶段7:观察期 2 周,7×24 小时值班
|
||||
|
||||
### 值班人员
|
||||
- 主值班:1 人(负责处理问题)
|
||||
- 备值班:1 人(主值班无法处理时接手)
|
||||
- DBA:1 人(数据库问题支持)
|
||||
|
||||
### 值班职责
|
||||
1. 实时监控告警
|
||||
2. 发现问题立即处理
|
||||
3. 无法处理立即上报
|
||||
4. 记录问题和处理过程
|
||||
|
||||
### 联系方式
|
||||
- 主值班:手机 + 钉钉
|
||||
- 备值班:手机 + 钉钉
|
||||
- DBA:手机 + 钉钉
|
||||
- 技术负责人:手机
|
||||
|
||||
---
|
||||
|
||||
## 应急演练
|
||||
|
||||
### 演练内容
|
||||
1. 双写失败处理
|
||||
2. 灰度回滚
|
||||
3. 数据不一致修复
|
||||
4. 新库故障切换
|
||||
|
||||
### 演练频率
|
||||
- 上线前:至少演练 2 次
|
||||
- 上线后:每周演练 1 次
|
||||
|
||||
### 演练要求
|
||||
- 全员参与
|
||||
- 记录演练过程
|
||||
- 总结改进点
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
**核心原则:**
|
||||
1. **快速发现**:监控要全面,告警要及时
|
||||
2. **快速决策**:问题影响面大,立即回滚
|
||||
3. **快速恢复**:回滚操作要简单,一键完成
|
||||
4. **事后复盘**:每次问题都要复盘,避免再犯
|
||||
355
docs/1亿数据迁移/整体流程.md
Normal file
355
docs/1亿数据迁移/整体流程.md
Normal file
@@ -0,0 +1,355 @@
|
||||
# 1亿用户数据分库分表迁移方案
|
||||
|
||||
## 背景
|
||||
|
||||
- **数据量**:1亿用户数据
|
||||
- **数据库**:MySQL
|
||||
- **分片策略**:按 user_id 取模
|
||||
- **QPS**:1000
|
||||
- **要求**:线上业务零影响,可随时回滚
|
||||
|
||||
---
|
||||
|
||||
## 整体架构
|
||||
|
||||
### 分片规划
|
||||
```
|
||||
1亿用户 → 8个库 × 16张表 = 128个分片
|
||||
每个分片约 78万 数据
|
||||
|
||||
库:user_db_0 ~ user_db_7
|
||||
表:users_0 ~ users_15
|
||||
|
||||
路由规则:
|
||||
db_index = user_id % 8
|
||||
table_index = (user_id / 8) % 16
|
||||
```
|
||||
|
||||
**为什么这样分?**
|
||||
- 8个库:方便后续扩容到16、32个库
|
||||
- 16张表:单表控制在100万以内,性能最优
|
||||
- 总共128个分片:数据足够分散
|
||||
|
||||
---
|
||||
|
||||
## 核心思路
|
||||
|
||||
**渐进式迁移,每一步都可回滚**
|
||||
|
||||
整个过程分为 **8个阶段**,像开关一样,每个阶段都可以随时回退到上一步。
|
||||
|
||||
---
|
||||
|
||||
## 迁移阶段
|
||||
|
||||
### 阶段0:当前状态(基线)
|
||||
```
|
||||
写 → 老库
|
||||
读 → 老库
|
||||
```
|
||||
|
||||
- 这是现状,作为基线
|
||||
- 任何时候出问题都可以回到这个状态
|
||||
|
||||
**风险**:无
|
||||
**回滚**:不需要
|
||||
|
||||
---
|
||||
|
||||
### 阶段1:准备新库(1天)
|
||||
```
|
||||
写 → 老库
|
||||
读 → 老库
|
||||
新库 → 创建好,但不使用
|
||||
```
|
||||
|
||||
**做什么:**
|
||||
- 创建 8 个新数据库
|
||||
- 每个库创建 16 张表(总共 128 个分片)
|
||||
- 引入分库分表中间件(ShardingSphere)
|
||||
- 配置分片规则(user_id % 8 定位库,user_id / 8 % 16 定位表)
|
||||
- 测试环境验证分片逻辑是否正确
|
||||
|
||||
**风险**:无(线上还没用新库)
|
||||
**回滚**:不需要
|
||||
|
||||
---
|
||||
|
||||
### 阶段2:代码改造(2天)
|
||||
```
|
||||
写 → 老库
|
||||
读 → 老库
|
||||
代码 → 支持双写,但开关关闭
|
||||
```
|
||||
|
||||
**做什么:**
|
||||
- 代码层面支持"双数据源"(老库 + 新库)
|
||||
- 增加配置开关(双写开关、读取开关、灰度比例)
|
||||
- 写操作:主写老库,可选异步写新库
|
||||
- 读操作:可选从老库或新库读,支持灰度
|
||||
- 双写失败不影响主流程,记录到队列补偿
|
||||
- 增加补偿任务,定期重试失败的写操作
|
||||
|
||||
**风险**:无(开关默认关闭,逻辑不变)
|
||||
**回滚**:不需要
|
||||
|
||||
---
|
||||
|
||||
### 阶段3:开启双写(1天)
|
||||
```
|
||||
写 → 老库(主)+ 新库(异步)
|
||||
读 → 老库
|
||||
```
|
||||
|
||||
**做什么:**
|
||||
- 打开双写开关
|
||||
- 所有新增/修改/删除操作同时写入两个库
|
||||
- 老库写成功就算成功,新库写失败不影响业务
|
||||
- 新库写失败的记录到 Redis 队列,后台补偿任务重试
|
||||
|
||||
**目的:**
|
||||
- 从这一刻开始,新数据实时同步到新库
|
||||
- 为后续迁移历史数据做准备
|
||||
|
||||
**监控指标:**
|
||||
- 双写失败率(应该 < 0.1%)
|
||||
- 补偿队列长度(应该很短)
|
||||
- 新库写入 QPS(应该接近老库)
|
||||
|
||||
**风险**:低
|
||||
- 读取仍然从老库,用户无感知
|
||||
- 新库写失败不影响业务
|
||||
- 最多就是新库数据不完整,但不影响线上
|
||||
|
||||
**回滚**:关闭双写开关即可
|
||||
|
||||
---
|
||||
|
||||
### 阶段4:历史数据迁移(3-7天)
|
||||
```
|
||||
写 → 老库 + 新库(双写继续)
|
||||
读 → 老库
|
||||
迁移 → 老库历史数据 → 新库(离线任务)
|
||||
```
|
||||
|
||||
**做什么:**
|
||||
- 写一个离线迁移脚本
|
||||
- 从老库分批读取历史数据(每次 1000 条)
|
||||
- 写入新库(用 INSERT IGNORE 避免和双写冲突)
|
||||
- 每批次之间 sleep 100ms,避免影响线上数据库
|
||||
- 多线程并行迁移(比如 10 个线程)
|
||||
|
||||
**关键点:**
|
||||
- 迁移期间双写继续,保证新数据不丢
|
||||
- 用 INSERT IGNORE 去重(因为双写可能已经写入)
|
||||
- 限流很重要,不能影响线上数据库性能
|
||||
- 记录迁移进度,支持断点续传
|
||||
|
||||
**时间估算:**
|
||||
- 1 亿数据,每批 1000 条,每批 sleep 100ms
|
||||
- 单线程约 3 小时,10 线程并行约 20 分钟
|
||||
- 但为了安全,建议分多天慢慢迁移
|
||||
|
||||
**监控指标:**
|
||||
- 迁移进度(已迁移多少条)
|
||||
- 迁移失败率
|
||||
- 老库/新库的 CPU、IO、慢查询
|
||||
|
||||
**风险**:低
|
||||
- 读写仍然走老库,用户无感知
|
||||
- 迁移脚本只是读老库、写新库,不影响业务逻辑
|
||||
- 最坏情况就是迁移失败,重新跑一遍
|
||||
|
||||
**回滚**:停止迁移脚本即可
|
||||
|
||||
---
|
||||
|
||||
### 阶段5:数据校验(1-2天)
|
||||
```
|
||||
写 → 老库 + 新库(双写继续)
|
||||
读 → 老库
|
||||
校验 → 随机抽样对比两个库的数据
|
||||
```
|
||||
|
||||
**做什么:**
|
||||
- 随机抽取 1 万个 user_id
|
||||
- 分别从老库和新库查询
|
||||
- 对比数据是否一致
|
||||
- 记录不一致的数据,人工排查原因
|
||||
|
||||
**目的:**
|
||||
- 确认数据迁移完整性
|
||||
- 发现潜在问题(比如某些边界情况没处理好)
|
||||
|
||||
**风险**:无(只是读取对比)
|
||||
**回滚**:不需要
|
||||
|
||||
---
|
||||
|
||||
### 阶段6:灰度读取(7-14天)
|
||||
```
|
||||
写 → 老库 + 新库(双写继续)
|
||||
读 → 老库(主)+ 新库(灰度 1% → 10% → 50% → 100%)
|
||||
```
|
||||
|
||||
**做什么:**
|
||||
- 逐步放开从新库读取的流量
|
||||
- 第 1 天:1% 流量读新库(按 user_id % 100 < 1)
|
||||
- 第 3 天:10% 流量读新库
|
||||
- 第 5 天:50% 流量读新库
|
||||
- 第 7 天:100% 流量读新库
|
||||
- 新库读取失败自动降级到老库
|
||||
|
||||
**目的:**
|
||||
- 验证新库的性能和稳定性
|
||||
- 小流量试错,发现问题影响面小
|
||||
|
||||
**监控指标:**
|
||||
- 新库查询成功率(应该 > 99.9%)
|
||||
- 新库响应时间(应该和老库差不多)
|
||||
- 降级次数(应该很少)
|
||||
- 业务指标(订单量、支付成功率等)
|
||||
|
||||
**风险**:中
|
||||
- 灰度流量可能受影响(但可以降级)
|
||||
- 新库可能有性能问题(慢查询、连接池不够等)
|
||||
|
||||
**回滚**:调整灰度比例或关闭新库读取
|
||||
|
||||
---
|
||||
|
||||
### 阶段7:完全切换(观察 1-2 周后)
|
||||
```
|
||||
写 → 新库(主)+ 老库(备份)
|
||||
读 → 新库
|
||||
```
|
||||
|
||||
**做什么:**
|
||||
- 新库成为主库
|
||||
- 写操作主写新库,异步写老库(作为备份)
|
||||
- 读操作 100% 从新库读
|
||||
- 老库继续保留,作为降级方案
|
||||
|
||||
**目的:**
|
||||
- 新库正式上线
|
||||
- 老库作为保险,万一新库出问题可以快速切回
|
||||
|
||||
**监控指标:**
|
||||
- 新库的所有指标(QPS、响应时间、错误率)
|
||||
- 业务指标
|
||||
- 老库作为对照组
|
||||
|
||||
**风险**:中
|
||||
- 新库成为主库,出问题影响面大
|
||||
- 但可以快速切回老库
|
||||
|
||||
**回滚**:切换写入和读取开关,回到老库
|
||||
|
||||
---
|
||||
|
||||
### 阶段8:下线老库(观察 1-2 周后)
|
||||
```
|
||||
写 → 新库
|
||||
读 → 新库
|
||||
老库 → 归档或删除
|
||||
```
|
||||
|
||||
**做什么:**
|
||||
- 关闭双写老库
|
||||
- 老库数据归档备份
|
||||
- 下线老库
|
||||
|
||||
**风险**:低(已经稳定运行很久了)
|
||||
|
||||
---
|
||||
|
||||
## 时间规划
|
||||
|
||||
| 阶段 | 时间 | 风险 | 可回滚 |
|
||||
|------|------|------|--------|
|
||||
| 0. 当前状态 | - | 无 | - |
|
||||
| 1. 准备新库 | 1天 | 无 | ✅ |
|
||||
| 2. 代码改造 | 2天 | 无 | ✅ |
|
||||
| 3. 开启双写 | 1天 | 低 | ✅ |
|
||||
| 4. 数据迁移 | 3-7天 | 低 | ✅ |
|
||||
| 5. 数据校验 | 1-2天 | 无 | ✅ |
|
||||
| 6. 灰度读取 | 7-14天 | 中 | ✅ |
|
||||
| 7. 完全切换 | 观察1-2周 | 中 | ✅ |
|
||||
| 8. 下线老库 | 1天 | 低 | ❌ |
|
||||
|
||||
**总计:3-5 周**
|
||||
|
||||
---
|
||||
|
||||
## 关键设计思想
|
||||
|
||||
### 1. 渐进式切换
|
||||
- 不是一步到位,而是分 8 个阶段
|
||||
- 每个阶段都可以观察、验证、回滚
|
||||
- 出问题影响面可控
|
||||
|
||||
### 2. 双写保证一致性
|
||||
- 迁移期间新数据实时同步
|
||||
- 历史数据离线迁移
|
||||
- 两者结合保证数据完整
|
||||
|
||||
### 3. 异步 + 补偿保证可靠性
|
||||
- 双写新库失败不影响主流程
|
||||
- 失败的记录到队列,后台重试
|
||||
- 最终一致性
|
||||
|
||||
### 4. 灰度 + 降级保证稳定性
|
||||
- 小流量试错,发现问题影响面小
|
||||
- 新库失败自动降级到老库
|
||||
- 用户无感知
|
||||
|
||||
### 5. 监控 + 告警保证可观测性
|
||||
- 每个阶段都有明确的监控指标
|
||||
- 异常及时告警
|
||||
- 数据驱动决策(什么时候进入下一阶段)
|
||||
|
||||
---
|
||||
|
||||
## 核心优势
|
||||
|
||||
1. **零停机**:整个过程不需要停服
|
||||
2. **可回滚**:每一步都可以回退
|
||||
3. **低风险**:渐进式切换,影响面可控
|
||||
4. **可观测**:每个阶段都有明确的监控指标
|
||||
5. **可验证**:数据校验、灰度验证,确保正确性
|
||||
|
||||
---
|
||||
|
||||
## 常见问题
|
||||
|
||||
### Q1:双写期间数据不一致怎么办?
|
||||
- 异步双写失败会记录到队列,后台补偿
|
||||
- 定期抽样校验,发现不一致及时修复
|
||||
- 最终一致性,不是强一致性
|
||||
|
||||
### Q2:迁移期间更新/删除怎么处理?
|
||||
- 双写模式下,更新/删除同时操作两个库
|
||||
- 迁移脚本用 INSERT IGNORE,不会覆盖新数据
|
||||
|
||||
### Q3:如何保证迁移不影响线上性能?
|
||||
- 限流:每批次之间 sleep
|
||||
- 错峰:凌晨低峰期迁移
|
||||
- 监控:实时监控数据库负载,超阈值暂停
|
||||
|
||||
### Q4:新库性能不如老库怎么办?
|
||||
- 灰度阶段会发现
|
||||
- 可以优化索引、调整分片策略
|
||||
- 实在不行可以回滚
|
||||
|
||||
### Q5:如果中途想放弃怎么办?
|
||||
- 关闭所有开关,回到阶段 0
|
||||
- 新库数据可以保留,也可以删除
|
||||
- 对线上业务无影响
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
这个方案的核心就是:**小步快跑,随时可退**。
|
||||
|
||||
每一步都很小,风险可控,出问题可以快速回滚。通过渐进式迁移,保证线上业务零影响。
|
||||
316
docs/1亿数据迁移/监控指标.md
Normal file
316
docs/1亿数据迁移/监控指标.md
Normal file
@@ -0,0 +1,316 @@
|
||||
# 监控指标
|
||||
|
||||
## 核心监控指标
|
||||
|
||||
### 1. 双写监控
|
||||
|
||||
#### 双写成功率
|
||||
```
|
||||
指标:dual_write_success_rate
|
||||
计算:成功次数 / 总次数 * 100%
|
||||
阈值:> 99.9%
|
||||
告警:< 99% 立即告警
|
||||
```
|
||||
|
||||
#### 双写失败次数
|
||||
```
|
||||
指标:dual_write_fail_count
|
||||
计算:每分钟失败次数
|
||||
阈值:< 10 次/分钟
|
||||
告警:> 100 次/分钟 立即告警
|
||||
```
|
||||
|
||||
#### 补偿队列长度
|
||||
```
|
||||
指标:compensation_queue_length
|
||||
计算:Redis 队列长度
|
||||
阈值:< 1000
|
||||
告警:> 10000 立即告警
|
||||
```
|
||||
|
||||
#### 补偿成功率
|
||||
```
|
||||
指标:compensation_success_rate
|
||||
计算:补偿成功次数 / 补偿总次数 * 100%
|
||||
阈值:> 95%
|
||||
告警:< 80% 告警
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 数据库性能监控
|
||||
|
||||
#### QPS(每秒查询数)
|
||||
```
|
||||
老库 QPS:old_db_qps
|
||||
新库 QPS:new_db_qps
|
||||
|
||||
阈值:
|
||||
- 老库:1000 QPS(当前基线)
|
||||
- 新库:应该接近老库
|
||||
|
||||
告警:
|
||||
- 新库 QPS 突然下降 > 50%
|
||||
- 新库 QPS 超过老库 2 倍(可能有问题)
|
||||
```
|
||||
|
||||
#### 响应时间
|
||||
```
|
||||
老库平均响应时间:old_db_avg_rt
|
||||
新库平均响应时间:new_db_avg_rt
|
||||
|
||||
阈值:
|
||||
- 老库:< 50ms(当前基线)
|
||||
- 新库:应该和老库差不多
|
||||
|
||||
告警:
|
||||
- 新库响应时间 > 老库 2 倍
|
||||
- 新库响应时间 > 100ms
|
||||
```
|
||||
|
||||
#### 慢查询
|
||||
```
|
||||
老库慢查询数:old_db_slow_query_count
|
||||
新库慢查询数:new_db_slow_query_count
|
||||
|
||||
阈值:< 10 次/分钟
|
||||
告警:> 100 次/分钟
|
||||
```
|
||||
|
||||
#### CPU 使用率
|
||||
```
|
||||
老库 CPU:old_db_cpu_usage
|
||||
新库 CPU:new_db_cpu_usage
|
||||
|
||||
阈值:< 70%
|
||||
告警:> 80%
|
||||
```
|
||||
|
||||
#### 连接数
|
||||
```
|
||||
老库连接数:old_db_connection_count
|
||||
新库连接数:new_db_connection_count
|
||||
|
||||
阈值:< 最大连接数的 80%
|
||||
告警:> 最大连接数的 90%
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. 数据一致性监控
|
||||
|
||||
#### 数据校验不一致率
|
||||
```
|
||||
指标:data_inconsistency_rate
|
||||
计算:不一致数量 / 抽样总数 * 100%
|
||||
阈值:< 0.1%
|
||||
告警:> 1% 告警
|
||||
```
|
||||
|
||||
#### 数据迁移进度
|
||||
```
|
||||
指标:migration_progress
|
||||
计算:已迁移数量 / 总数量 * 100%
|
||||
展示:实时进度条
|
||||
```
|
||||
|
||||
#### 数据迁移失败率
|
||||
```
|
||||
指标:migration_fail_rate
|
||||
计算:失败数量 / 总数量 * 100%
|
||||
阈值:< 0.1%
|
||||
告警:> 1% 告警
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. 灰度读取监控
|
||||
|
||||
#### 新库读取成功率
|
||||
```
|
||||
指标:new_db_read_success_rate
|
||||
计算:成功次数 / 总次数 * 100%
|
||||
阈值:> 99.9%
|
||||
告警:< 99% 立即告警
|
||||
```
|
||||
|
||||
#### 降级次数
|
||||
```
|
||||
指标:fallback_count
|
||||
计算:每分钟降级到老库的次数
|
||||
阈值:< 10 次/分钟
|
||||
告警:> 100 次/分钟 立即告警
|
||||
```
|
||||
|
||||
#### 灰度流量占比
|
||||
```
|
||||
指标:gray_traffic_percent
|
||||
计算:新库读取次数 / 总读取次数 * 100%
|
||||
预期:应该等于配置的灰度比例
|
||||
告警:偏差 > 10%
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. 业务指标监控
|
||||
|
||||
#### 订单量
|
||||
```
|
||||
指标:order_count
|
||||
计算:每分钟订单数
|
||||
基线:迁移前的平均值
|
||||
告警:下降 > 20%
|
||||
```
|
||||
|
||||
#### 支付成功率
|
||||
```
|
||||
指标:payment_success_rate
|
||||
计算:支付成功次数 / 支付总次数 * 100%
|
||||
基线:迁移前的平均值
|
||||
告警:下降 > 5%
|
||||
```
|
||||
|
||||
#### 接口错误率
|
||||
```
|
||||
指标:api_error_rate
|
||||
计算:错误次数 / 总次数 * 100%
|
||||
基线:迁移前的平均值
|
||||
告警:上升 > 2 倍
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 监控大盘
|
||||
|
||||
### 阶段3:双写阶段
|
||||
```
|
||||
核心指标:
|
||||
- 双写成功率
|
||||
- 补偿队列长度
|
||||
- 新库 QPS
|
||||
- 新库响应时间
|
||||
|
||||
次要指标:
|
||||
- 老库 CPU/连接数
|
||||
- 新库 CPU/连接数
|
||||
```
|
||||
|
||||
### 阶段4:数据迁移阶段
|
||||
```
|
||||
核心指标:
|
||||
- 迁移进度
|
||||
- 迁移失败率
|
||||
- 老库 CPU/IO
|
||||
- 新库 CPU/IO
|
||||
|
||||
次要指标:
|
||||
- 双写成功率
|
||||
- 慢查询数
|
||||
```
|
||||
|
||||
### 阶段6:灰度读取阶段
|
||||
```
|
||||
核心指标:
|
||||
- 新库读取成功率
|
||||
- 降级次数
|
||||
- 新库响应时间
|
||||
- 业务指标(订单量、支付成功率)
|
||||
|
||||
次要指标:
|
||||
- 灰度流量占比
|
||||
- 新库慢查询
|
||||
```
|
||||
|
||||
### 阶段7:完全切换阶段
|
||||
```
|
||||
核心指标:
|
||||
- 新库 QPS
|
||||
- 新库响应时间
|
||||
- 新库错误率
|
||||
- 业务指标
|
||||
|
||||
次要指标:
|
||||
- 新库 CPU/连接数
|
||||
- 慢查询数
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 告警规则
|
||||
|
||||
### P0 告警(立即处理)
|
||||
- 新库读取成功率 < 99%
|
||||
- 双写成功率 < 99%
|
||||
- 业务指标下降 > 20%
|
||||
- 新库 CPU > 90%
|
||||
|
||||
### P1 告警(30分钟内处理)
|
||||
- 补偿队列长度 > 10000
|
||||
- 降级次数 > 100 次/分钟
|
||||
- 新库响应时间 > 100ms
|
||||
- 数据不一致率 > 1%
|
||||
|
||||
### P2 告警(1小时内处理)
|
||||
- 慢查询数 > 100 次/分钟
|
||||
- 新库连接数 > 90%
|
||||
- 迁移失败率 > 1%
|
||||
|
||||
---
|
||||
|
||||
## 监控工具
|
||||
|
||||
### 推荐工具
|
||||
- **Prometheus + Grafana**:指标采集和展示
|
||||
- **ELK**:日志分析
|
||||
- **Skywalking / Zipkin**:链路追踪
|
||||
- **钉钉/企业微信**:告警通知
|
||||
|
||||
### 自定义监控
|
||||
```java
|
||||
// 示例:记录双写成功率
|
||||
@Component
|
||||
public class MigrationMetrics {
|
||||
|
||||
@Autowired
|
||||
private MeterRegistry meterRegistry;
|
||||
|
||||
public void recordDualWriteSuccess() {
|
||||
meterRegistry.counter("dual_write_success").increment();
|
||||
}
|
||||
|
||||
public void recordDualWriteFail() {
|
||||
meterRegistry.counter("dual_write_fail").increment();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 监控检查清单
|
||||
|
||||
### 每日检查
|
||||
- [ ] 双写成功率是否正常
|
||||
- [ ] 补偿队列是否堆积
|
||||
- [ ] 新库性能是否正常
|
||||
- [ ] 业务指标是否正常
|
||||
|
||||
### 每周检查
|
||||
- [ ] 数据一致性抽样校验
|
||||
- [ ] 慢查询分析和优化
|
||||
- [ ] 告警规则是否合理
|
||||
- [ ] 监控大盘是否完善
|
||||
|
||||
### 灰度前检查
|
||||
- [ ] 所有监控指标是否正常
|
||||
- [ ] 告警规则是否配置
|
||||
- [ ] 回滚方案是否准备好
|
||||
- [ ] 值班人员是否到位
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **监控要全面**:覆盖数据库、应用、业务各个层面
|
||||
2. **告警要及时**:关键指标异常立即告警
|
||||
3. **大盘要直观**:一眼看出系统状态
|
||||
4. **数据要留存**:至少保留 30 天,用于问题排查
|
||||
209
docs/1亿数据迁移/配置开关设计.md
Normal file
209
docs/1亿数据迁移/配置开关设计.md
Normal file
@@ -0,0 +1,209 @@
|
||||
# 配置开关设计
|
||||
|
||||
## 配置项
|
||||
|
||||
```yaml
|
||||
migration:
|
||||
# 双写配置
|
||||
dual-write:
|
||||
enabled: false # 双写开关,默认关闭
|
||||
|
||||
# 读取配置
|
||||
read:
|
||||
from-new: false # 是否从新库读取,默认关闭
|
||||
gray-percent: 0 # 灰度比例 0-100,默认 0
|
||||
|
||||
# 补偿配置
|
||||
compensation:
|
||||
enabled: false # 补偿任务开关,默认关闭
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 各阶段配置
|
||||
|
||||
### 阶段0-2:准备阶段
|
||||
```yaml
|
||||
migration:
|
||||
dual-write:
|
||||
enabled: false
|
||||
read:
|
||||
from-new: false
|
||||
gray-percent: 0
|
||||
compensation:
|
||||
enabled: false
|
||||
```
|
||||
|
||||
**说明**:所有开关关闭,线上逻辑不变
|
||||
|
||||
---
|
||||
|
||||
### 阶段3:开启双写
|
||||
```yaml
|
||||
migration:
|
||||
dual-write:
|
||||
enabled: true # 开启双写
|
||||
read:
|
||||
from-new: false # 仍从老库读
|
||||
gray-percent: 0
|
||||
compensation:
|
||||
enabled: true # 开启补偿任务
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- 写入同时写两个库
|
||||
- 读取仍从老库
|
||||
- 补偿任务处理失败的写入
|
||||
|
||||
---
|
||||
|
||||
### 阶段4-5:数据迁移和校验
|
||||
```yaml
|
||||
migration:
|
||||
dual-write:
|
||||
enabled: true # 双写继续
|
||||
read:
|
||||
from-new: false # 仍从老库读
|
||||
gray-percent: 0
|
||||
compensation:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
**说明**:配置不变,后台运行迁移脚本
|
||||
|
||||
---
|
||||
|
||||
### 阶段6:灰度读取
|
||||
|
||||
#### 第1天:1% 流量
|
||||
```yaml
|
||||
migration:
|
||||
dual-write:
|
||||
enabled: true
|
||||
read:
|
||||
from-new: true # 开启新库读取
|
||||
gray-percent: 1 # 1% 流量
|
||||
compensation:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
#### 第3天:10% 流量
|
||||
```yaml
|
||||
migration:
|
||||
read:
|
||||
gray-percent: 10 # 调整为 10%
|
||||
```
|
||||
|
||||
#### 第5天:50% 流量
|
||||
```yaml
|
||||
migration:
|
||||
read:
|
||||
gray-percent: 50 # 调整为 50%
|
||||
```
|
||||
|
||||
#### 第7天:100% 流量
|
||||
```yaml
|
||||
migration:
|
||||
read:
|
||||
gray-percent: 100 # 调整为 100%
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- 逐步放开新库读取流量
|
||||
- 双写继续,保证数据一致性
|
||||
- 观察新库性能和稳定性
|
||||
|
||||
---
|
||||
|
||||
### 阶段7:完全切换
|
||||
```yaml
|
||||
migration:
|
||||
dual-write:
|
||||
enabled: true # 继续双写(反向,主写新库)
|
||||
read:
|
||||
from-new: true
|
||||
gray-percent: 100 # 100% 从新库读
|
||||
compensation:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- 新库成为主库
|
||||
- 老库作为备份继续双写
|
||||
- 观察 1-2 周
|
||||
|
||||
---
|
||||
|
||||
### 阶段8:下线老库
|
||||
```yaml
|
||||
migration:
|
||||
dual-write:
|
||||
enabled: false # 关闭双写
|
||||
read:
|
||||
from-new: true
|
||||
gray-percent: 100
|
||||
compensation:
|
||||
enabled: false # 关闭补偿
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- 完全使用新库
|
||||
- 老库可以下线
|
||||
|
||||
---
|
||||
|
||||
## 配置管理建议
|
||||
|
||||
### 1. 使用配置中心
|
||||
- Nacos / Apollo / Spring Cloud Config
|
||||
- 配置实时生效,无需重启
|
||||
- 支持灰度发布
|
||||
|
||||
### 2. 配置版本管理
|
||||
- 每次修改配置记录版本
|
||||
- 支持快速回滚到上一个版本
|
||||
|
||||
### 3. 配置变更审批
|
||||
- 配置变更需要审批
|
||||
- 重要配置(如灰度比例)需要多人确认
|
||||
|
||||
### 4. 配置监控告警
|
||||
- 监控配置变更
|
||||
- 配置异常及时告警
|
||||
|
||||
---
|
||||
|
||||
## 回滚策略
|
||||
|
||||
### 从阶段3回滚到阶段2
|
||||
```yaml
|
||||
migration:
|
||||
dual-write:
|
||||
enabled: false # 关闭双写
|
||||
compensation:
|
||||
enabled: false
|
||||
```
|
||||
|
||||
### 从阶段6回滚到阶段5
|
||||
```yaml
|
||||
migration:
|
||||
read:
|
||||
from-new: false # 关闭新库读取
|
||||
gray-percent: 0
|
||||
```
|
||||
|
||||
### 从阶段7回滚到阶段6
|
||||
```yaml
|
||||
migration:
|
||||
read:
|
||||
gray-percent: 50 # 降低灰度比例
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **配置变更要谨慎**:每次变更前确认影响范围
|
||||
2. **灰度要渐进**:不要一次性放开太多流量
|
||||
3. **监控要及时**:配置变更后立即观察监控指标
|
||||
4. **回滚要快速**:发现问题立即回滚,不要犹豫
|
||||
Reference in New Issue
Block a user