票务预订系统架构:从API到可扩展性
票务预订系统必须支持活动浏览、参数化搜索和可靠的预订,避免重复销售。重点在于读取的数据可用性和写入的严格一致性。预期负载——热门活动高达1000万用户,搜索延迟低于500毫秒,读写比例为100:1。
关键实体:活动(含日期、描述、类型)、用户、表演者、场馆(含座位图)、票(含座位、价格、状态)、预订(含票列表和状态)。
设计API端点
API围绕三个优先级功能构建。从简单端点开始,随着设计进展逐步优化。
查看活动:
GET /events/:id -> 活动 & 场馆 & 表演者 & 票[]
返回活动详情、场馆、表演者和票列表,用于在客户端渲染座位图。
搜索:
GET /events/search?keyword={关键词}&start={开始日期}&end={结束日期}&pageSize={页面大小}&page={页码} -> 活动[]
按关键词、日期过滤,分页处理高吞吐量。
预订(初始版本):
POST /bookings/:eventId -> 预订ID
{
"ticketIds": 字符串[],
"paymentDetails": ...
}
后续拆分为预订和确认,以确保事务可靠性。
高层架构
系统组件
- API网关:路由、认证、速率限制、日志记录。
- 活动服务:从数据库读取活动、场馆、表演者数据。
- 搜索服务:处理带过滤的搜索查询。
- 预订服务:管理带事务的预订。
- 数据库:PostgreSQL用于ACID事务(预订、票),NoSQL或缓存用于读取(活动、场馆)。
查看流程:客户端 → 网关 → 活动服务 → 数据库 → 返回带座位图的响应。
负载下的扩展
针对每个活动1000万日活用户,使用:
- 缓存:Redis用于频繁读取(活动、搜索)。
- 分片:按eventId在票/预订中分片。
- CQRS:分离读取(读副本)和写入(主库)。
- 负载均衡器:在服务间分配流量。
100:1的读写比例决定了优先使用读副本和CDN处理静态数据(座位图)。
预订详情与一致性
关键领域——避免重复预订。PostgreSQL配合事务和可序列化隔离,或票版本上的乐观并发控制(OCC)。
流程:
- 客户端选择票ID。
- 预订服务开始事务:在票上执行SELECT FOR UPDATE。
- 检查状态(可用),更新为已预订。
- 创建预订记录。
- 支付确认 → 状态确认为已确认。
票表示例:
| id | event_id | section | row | seat | price | status | version |
|----|----------|---------|-----|------|-------|--------|---------|
| 1 | 123 | A | 1 | 5 | 100 | available | 1 |
OCC:更新时检查版本,冲突时拒绝。
针对高并发——在Redis中设置临时保留(TTL 10分钟)。
搜索优化
基本SQL过滤无法扩展到1000万。过渡到:
- Elasticsearch用于按关键词、表演者、场馆进行全文搜索。
- 索引活动字段:名称、日期范围、地点。
- 分面搜索用于过滤(日期、类型)。
流程:搜索服务 → ES查询 → 聚合 → 缓存到Redis。
延迟<500毫秒通过预加载热门活动实现。
可扩展性深入探讨
数据库
- 主PostgreSQL(预订、票)——按eventId分片。
- 读副本——10+个用于活动查看。
- Redis——会话、临时保留、热门活动。
- Elasticsearch——搜索、分析。
数据迁移:活动服务写入Kafka → CDC → 副本/ES。
监控与容错
- 服务中的断路器。
- 健康检查、自动扩展。
- 备份:WAL归档到S3。
关键要点
- 严格一致性仅用于预订事务,搜索采用最终一致性。
- OCC + Redis临时锁防止99%的冲突,无需阻塞。
- CQRS/ES确保在100:1负载下搜索延迟<500毫秒。
- 按eventId分片可扩展到1000万+日活用户。
- API网关集中安全性和限流。
— Editorial Team
暂无评论。