数据层设计

返回 高并发

高并发场景下,数据层应扛持久化、对账与复杂查询,而不是扛每一次用户点击。核心是:写路径异步、批量、幂等、可分区


设计原则

原则说明
写少读多写合并后批量入库
最终一致实时态在 Redis,DB 滞后分钟~小时可接受
幂等业务键 (user_id, step_date) UPSERT
可分区按日期 / 用户 ID 分表,避免单表过大
可回溯MQ / Binlog 保留重放能力

写路径演进

❌ 同步:API → UPDATE mysql → 返回
✅ 推荐:API → MQ → 聚合 → Redis → 异步批量 → MySQL
阶段存储延迟
实时Redisms
近线MQ 持久化秒级 lag
持久MySQL / TiDB分钟~T+1

表结构设计(步数示例)

CREATE TABLE user_daily_steps (
  user_id     BIGINT UNSIGNED NOT NULL,
  step_date   DATE            NOT NULL,
  steps       INT UNSIGNED    NOT NULL DEFAULT 0,
  source      VARCHAR(16)     NULL COMMENT 'ios/android',
  updated_at  DATETIME(3)     NOT NULL,
  PRIMARY KEY (user_id, step_date),
  KEY idx_date_steps (step_date, steps)
) ENGINE=InnoDB
PARTITION BY RANGE (TO_DAYS(step_date)) (
  PARTITION p202406 VALUES LESS THAN (TO_DAYS('2024-07-01')),
  PARTITION p202407 VALUES LESS THAN (TO_DAYS('2024-08-01'))
);
字段说明
联合主键天然幂等 UPSERT
step_date日历日,与 Redis key 对齐
分区历史分区可归档、删除

批量写入

INSERT INTO user_daily_steps (user_id, step_date, steps, updated_at)
VALUES (?,?,?,?), (?,?,?,?)
ON DUPLICATE KEY UPDATE
  steps = GREATEST(steps, VALUES(steps)),
  updated_at = VALUES(updated_at);

消费者从 Redis 或 MQ 每 500~5000 条一批,控制单事务大小。


读写分离

历史榜单、运营报表批量入库任务
从库主库

实时榜不读从库(延迟)。见 数据库架构


分库分表

单表日增量亿级 或 QPS 超单机:

维度策略
user_id 分库db = uid % 16
step_date 分表与分区配合
路由ShardingSphere、应用内路由

排行榜实时读仍在 Redis;分库分表解决持久化与历史查询

详见 ShardingSphere分库分表


分布式数据库选项

产品特点
TiDBMySQL 协议,自动分片,HTAP
PolarDB-X云原生分布式
单机 + 分表运维简单,多数业务够用

对账与补偿

定时任务:
  扫描 Redis step:date:* 与 DB 不一致
  以 Redis 为准修复 DB(或人工规则)
MQ 重放:
  按 offset 重新消费某日上报
不一致原因处理
消费失败重试 + 死信队列
DB 批量失败记录失败批次,重跑
Redis 丢数据从 MQ 重放(需 MQ 保留期足够)

CDC 同步其它系统

MySQL Binlog → Canal / Debezium → Kafka → ES / 数仓

搜索、BI 不走业务写路径。见 Canal


数据层容量估算(示意)

估算
1 亿 DAU 上报 1 次/日1 亿行/日
行 50B~5GB/日 裸数据
保留 1 年需分区归档 + 冷存储

结论:必须分区 + 归档;实时态不放 MySQL。


相关