如何以最低停机时间将DynamoDB表迁移至Global DynamoDB表?
无停机迁移DynamoDB到全局表的实操经验与工具建议
我之前参与过好几次大规模DynamoDB到全局表的无停机迁移,你的方案其实已经踩中了AWS推荐的最佳实践路径(双写+渐进式流量切换),我来分享一些实操细节、简化双读的技巧和工具建议:
你的迁移方案的优化细节
先给你的方案点个赞,这是最稳妥的无停机迁移模式,补充几个关键注意点:
- 创建全局表时:必须保证和原表的Schema完全一致——包括GSI/LSI、加密配置、TTL设置、读写容量模式(建议用按需模式避免迁移期间吞吐量瓶颈),全局表创建后会自动同步后续写入,但历史数据需要单独迁移。
- 双写双读阶段:你的读写逻辑非常合理:写操作只指向全局表,读操作优先全局表、无数据则降级到原表。这个逻辑能避免迁移期间的新旧数据不一致,同时保证业务无感知。
简化双表读取操作的封装方案
基于boto3的通用封装
可以写一个全局的读取工具函数,把双读逻辑封装起来,业务代码不用重复写判断:
import boto3 from botocore.exceptions import ClientError def get_dual_table_item(global_table_name, original_table_name, key, region="us-east-1"): dynamodb = boto3.client("dynamodb", region_name=region) # 优先读取全局表 try: response = dynamodb.get_item(TableName=global_table_name, Key=key) if "Item" in response: return response["Item"] except ClientError as e: # 全局表读取失败(比如暂未同步到该区域),直接降级 pass # 降级读取原表 response = dynamodb.get_item(TableName=original_table_name, Key=key) return response.get("Item") # 使用示例 user_item = get_dual_table_item( "Global-Users", "Original-Users", {"user_id": {"S": "user_123"}} )
如果需要批量读取,可以封装batch_get_item的双表处理逻辑,注意合并两个表的返回结果。
基于PynamoDB的Mixin封装
如果用PynamoDB做ORM,可以写一个Mixin类来实现自动双读,业务代码几乎不用修改:
from pynamodb.models import Model from pynamodb.exceptions import DoesNotExist class DualReadModelMixin: @classmethod def get(cls, hash_key, range_key=None, consistent_read=False, original_model=None): try: # 先尝试全局表模型 return super().get(hash_key, range_key, consistent_read) except DoesNotExist: # 无数据则降级到原表模型 if original_model: return original_model.get(hash_key, range_key, consistent_read) raise # 定义全局表模型 class GlobalUserModel(DualReadModelMixin, Model): class Meta: table_name = "Global-Users" region = "us-east-1" user_id = UnicodeAttribute(hash_key=True) username = UnicodeAttribute() # 定义原表模型 class OriginalUserModel(Model): class Meta: table_name = "Original-Users" region = "us-east-1" user_id = UnicodeAttribute(hash_key=True) username = UnicodeAttribute() # 业务代码调用(完全不用关心双读逻辑) try: user = GlobalUserModel.get("user_123", original_model=OriginalUserModel) except DoesNotExist: # 处理真正的不存在逻辑 pass
高效迁移原表历史数据的工具
针对大数据量的表,推荐几个比手动导出导入更高效的方案:
- AWS Glue ETL任务:适合TB级别的超大表,支持并行扫描和写入,能自动处理吞吐量限制,还可以过滤不需要迁移的数据。只需要配置源表为原DynamoDB表,目标表为全局表,运行任务即可。
- 自定义批量复制脚本:用boto3的
scan+batch_write_item,配合重试机制避免限流:
import boto3 from tenacity import retry, stop_after_attempt, wait_exponential dynamodb = boto3.resource("dynamodb") original_table = dynamodb.Table("Original-Users") global_table = dynamodb.Table("Global-Users") @retry(stop=stop_after_attempt(5), wait=wait_exponential(multiplier=1, min=2, max=10)) def batch_write_items(items): with global_table.batch_writer() as batch: for item in items: batch.put_item(Item=item) def copy_historical_data(): last_evaluated_key = None while True: scan_params = {"ExclusiveStartKey": last_evaluated_key} if last_evaluated_key else {} response = original_table.scan(**scan_params) items = response.get("Items", []) if items: batch_write_items(items) last_evaluated_key = response.get("LastEvaluatedKey") if not last_evaluated_key: break
注意迁移期间要开启原表和全局表的自动扩缩容,避免出现限流导致迁移停滞。
其他可选迁移思路
- DynamoDB Streams补漏:如果原表已经开启了Streams,可以在双写阶段用Streams同步遗漏数据——比如写全局表失败的请求,可以通过Streams重新写入全局表,保证数据一致性。
- 蓝绿部署式切换:如果你的应用是微服务架构,可以先部署一批只读写全局表的实例,逐步把流量切过去,最后下线读写原表的实例。这种方式适合能做流量灰度的场景。
迁移后的验证与收尾
不要急着删除原表:
- 监控全局表的CloudWatch指标,确认所有读写流量都已经切换到全局表。
- 抽样对比原表和全局表的数据,或者用
describe_table的ItemCount做近似校验。 - 保留原表1-2周作为备份,确认业务完全稳定后再删除。
内容的提问来源于stack exchange,提问作者Raf
相关产品推荐
相关产品推荐

