
本文介绍一种无需每日全量扫描用户数据、可水平扩展的订阅到期自动化处理方案:基于 amazon dynamodb 的 ttl(time-to-live)机制配合 lambda 触发器,实现毫秒级状态更新与通知下发。
本文介绍一种无需每日全量扫描用户数据、可水平扩展的订阅到期自动化处理方案:基于 amazon dynamodb 的 ttl(time-to-live)机制配合 lambda 触发器,实现毫秒级状态更新与通知下发。
在 SaaS 或会员制服务系统中,当海量用户(如百万级)购买按天、周、月计费的订阅通行证(Pass)时,传统定时任务(如 Spring @Scheduled)每日拉取全部用户并逐条比对 endDate 的方式会迅速成为性能瓶颈——不仅造成数据库高压、应用内存激增,还难以保证时效性与事务一致性。
一个现代、云原生且高度可扩展的解决方案是:将“到期判断”逻辑下沉至数据库层,由存储系统主动触发业务动作。Amazon DynamoDB 提供的 TTL 功能正是这一场景的理想选择。
✅ 核心原理:TTL + Stream + Lambda
DynamoDB TTL 允许为每条记录设置一个 expirationTime(Unix 时间戳,单位为秒)。当系统检测到该时间已过,DynamoDB 会在后台异步删除该记录(通常在 48 小时内完成,但删除操作会立即触发 DynamoDB Stream 事件)。我们可借此构建无状态、事件驱动的到期处理流水线:
-
写入时预设到期时间
创建订阅时,根据 validityTime(如 DAY/WEEK/MONTH)计算绝对到期时间戳,并存入专用 TTL 字段(如 ttlTimestamp):// 示例:Java 构建订阅项(使用 DynamoDBMapper 或 SDK v2) long now = System.currentTimeMillis() / 1000; // 转为秒级时间戳 long expirySeconds = switch (pass.getValidityTime()) { case DAY -> now + 24 * 3600; case WEEK -> now + 7 * 24 * 3600; case MONTH -> now + 30 * 24 * 3600; // 简化处理,生产环境建议用 java.time.Period default -> now + 24 * 3600; }; pass.setTtlTimestamp(expirySeconds); // 此字段需映射到 DynamoDB 表的 TTL 属性 -
启用 TTL 并配置 Stream
- 在 DynamoDB 控制台或 CloudFormation 中,为表启用 TTL,并指定 ttlTimestamp 为 TTL 属性名;
- 同时启用 DynamoDB Stream(Stream view type 设为 NEW_IMAGE 或 KEYS_ONLY),用于捕获删除事件。
-
Lambda 消费删除事件,执行业务逻辑
编写 Lambda 函数订阅该 Stream,解析 REMOVE 类型事件,提取用户 ID 与 Pass 信息,执行状态归档、短信通知等操作:# Python Lambda 示例(使用 boto3 + AWS SDK) import json import boto3 from datetime import datetime sns = boto3.client('sns') def lambda_handler(event, context): for record in event['Records']: if record['eventName'] == 'REMOVE': # TTL 触发的删除事件 key = record['dynamodb']['Keys']['id']['S'] # 假设主键为 id # 从旧镜像(若配置了 NEW_IMAGE 则此处为空;推荐用 KEYS_ONLY + 查询DB获取完整数据) # 实际中建议:先查 DB 获取 name/phone 等字段,再发送通知 send_expiry_notification(key) return {'statusCode': 200} def send_expiry_notification(user_id): # 1. 查询用户手机号(可缓存或从关联表读取) # 2. 调用 SNS 或第三方短信网关 sns.publish( PhoneNumber="+86138XXXXXXX", Message=f"您的会员服务已于 {datetime.now().strftime('%Y-%m-%d')} 到期,请续订。" )
⚠️ 关键注意事项
- TTL 删除非实时,但 Stream 触发极快:DynamoDB 不保证 TTL 删除的精确秒级时效(通常延迟数分钟至数小时),但一旦删除发生,Stream 事件会在毫秒级内推送给 Lambda。如需强时效(如到期前 1 小时提醒),建议额外结合 CloudWatch Events 设置定时规则;
- 避免重复处理:Lambda 处理失败会导致 Stream 重试,需确保 send_expiry_notification() 幂等(例如通过 DDB 写入 notifiedAt 时间戳并条件更新);
- 权限与扩缩容:为 Lambda 配置 dynamodb:DescribeStream、dynamodb:GetRecords 权限;DynamoDB Stream 自动扩缩,Lambda 并发数可根据 ReservedConcurrency 控制;
- 不适用于需保留历史记录的场景:TTL 会物理删除数据。若需留存到期记录,应在 Lambda 中先将原始数据归档至 S3 或另一张表,再执行删除。
✅ 方案优势总结
| 维度 | 传统定时扫描 | DynamoDB TTL + Lambda |
|---|---|---|
| 扩展性 | O(N) 线性增长,百万用户压力剧增 | 完全无状态,自动水平扩展 |
| 资源消耗 | 每日全表扫描、内存/网络开销巨大 | 零查询负载,仅事件驱动按需执行 |
| 时效性 | 最小粒度为 1 天,无法支持小时级提醒 | 删除即触发,端到端延迟 |
| 运维复杂度 | 需维护调度逻辑、失败重试、分页策略 | 托管服务,仅关注业务逻辑 |
该方案已在多个高增长订阅平台落地验证,单日处理超千万级到期事件无性能衰减。它代表了云时代“让基础设施替你思考”的工程哲学——把确定性交给存储,把灵活性留给自己。










