简介:本文深入解析12306系统在高并发场景下的数据库设计策略,从分库分表、缓存机制、事务管理到数据一致性保障,揭示其支撑亿级流量的技术架构。
12306作为全球最大的铁路票务系统,日均访问量超千万次,春运期间峰值可达数亿次。其数据库设计需解决三大核心问题:高并发写入(如余票更新)、强一致性要求(票务状态必须实时准确)、海量数据存储(覆盖全国铁路网的车次、座位、用户信息)。传统关系型数据库在单库模式下难以满足需求,因此12306采用了分库分表、读写分离、缓存加速等混合架构。
12306将数据库拆分为多个独立库:
代码示例(分库路由逻辑):
// 根据业务类型选择数据库连接public Connection getDBConnection(String businessType) {if ("ticket".equals(businessType)) {return ticketDataSource.getConnection();} else if ("user".equals(businessType)) {return userDataSource.getConnection();}throw new IllegalArgumentException("Unknown business type");}
余票表按车次ID+发车日期哈希分片,例如:
ticket_20231001_G101存储10月1日G101次列车的余票数据。(车次ID % 16 + 日期哈希值 % 4),确保数据均匀分布。优势:
12306通过Redis集群缓存热点数据,具体策略包括:
采用异步刷新+双写一致性策略:
# 伪代码:更新余票时同步缓存def update_ticket_stock(train_id, date, new_stock):# 1. 更新数据库db.execute("UPDATE tickets SET stock=? WHERE train_id=? AND date=?",new_stock, train_id, date)# 2. 异步更新缓存(使用消息队列保证顺序)redis.hset(f"ticket:{train_id}:{date}", "stock", new_stock)# 3. 失效旧缓存(防止脏读)redis.delete(f"ticket:{train_id}:{date}:old")
12306采用TCC(Try-Confirm-Cancel)模式处理跨库操作,例如购票流程:
对非关键数据(如用户积分)采用消息队列+补偿机制:
// 伪代码:异步更新用户积分@Transactionalpublic void completeOrder(Order order) {// 1. 更新订单状态(强一致性)orderDao.updateStatus(order.getId(), "COMPLETED");// 2. 发送积分更新消息(最终一致性)mqSender.send("user.points.update",new PointsUpdate(order.getUserId(), order.getPoints()));}
(train_id, date)复合索引。INSERT INTO ... VALUES (...), (...)减少连接开销。READ_COMMITTED隔离级别提供读服务。CPU核心数 * 2 + 磁盘数量。12306部署了同城双活+异地灾备架构:
采用MySQL Group Replication实现强一致性复制,配置如下:
[mysqld]group_replication_group_name="aaaa-bbbb-cccc-dddd"group_replication_start_on_boot=OFFgroup_replication_local_address="192.168.1.1:2490"group_replication_group_seeds="192.168.1.1:2490,192.168.1.2:2490"
扩展建议:中小型票务系统可参考12306的分层设计,但需根据自身规模调整复杂度。例如,初期可采用单库+Redis缓存,后期逐步引入分片。
本文通过解析12306的数据库设计,揭示了高并发系统在数据架构、缓存策略、事务管理等方面的最佳实践。开发者可结合自身业务场景,灵活应用这些技术方案。