第七章 数据与缓存架构
数据架构决定平台能跑多远。早期系统可以靠 MySQL 单库支撑,但产品矩阵一旦出现多应用、多租户、内容增长、行为分析和 AI 调用,数据会迅速分层。
建议把数据分成四类:
| 类型 | 存储 | 特点 |
|---|---|---|
| 交易数据 | MySQL / PostgreSQL | 强一致、可审计 |
| 缓存数据 | Redis / Ristretto | 低延迟、可过期 |
| 搜索数据 | ElasticSearch / OpenSearch | 倒排索引、全文检索 |
| 分析数据 | ClickHouse / Hive | 大宽表、聚合查询 |
7.1 数据库分层架构
基础形态:
graph TD
A[Application] --> B[Repository]
B --> C[Primary DB]
B --> D[Read Replica]
B --> E[Redis Cache]
C --> F[Binlog / CDC]
F --> G[Search Index]
F --> H[Analytics Store]
读写分离要谨慎。并不是所有读请求都能走从库,例如刚创建订单后立即查询订单详情,如果读到延迟从库,会出现“下单成功但看不到订单”的体验。
推荐策略:
| 场景 | 读取位置 |
|---|---|
| 用户刚写入后的强一致查询 | 主库 |
| 后台列表、报表预览 | 从库 |
| 内容详情、商品详情 | 缓存优先 |
| BI 聚合 | ClickHouse |
7.2 ORM Hook 实践
GORM Hook 可以增强一致性,但要控制边界。
适合放在 Hook 的逻辑:
| 能力 | 原因 |
|---|---|
tenant_id 自动填充 | 横切规则,避免漏写 |
created_by、updated_by | 审计字段统一 |
| 软删除限制 | 防止误删 |
| 更新时间 | 标准化 |
不适合放在 Hook 的逻辑:
| 逻辑 | 原因 |
|---|---|
| 发送短信 | 外部副作用,不可控 |
| 创建订单后扣库存 | 业务流程应显式 |
| 复杂权限判断 | 依赖上下文,难测试 |
| 调用第三方 API | 事务中调用风险大 |
Hook 应该像安全带,而不是方向盘。
7.3 缓存策略
常见缓存模式:
| 模式 | 写法 | 适用 |
|---|---|---|
| Cache Aside | 应用读缓存,未命中读库再写缓存 | 最常用 |
| Write Through | 写库同时写缓存 | 配置、字典 |
| Write Behind | 先写缓存,异步落库 | 计数、日志,需谨慎 |
| Refresh Ahead | 过期前刷新 | 热点数据 |
Cache Aside 的关键不是“加 Redis”,而是处理缓存击穿、穿透和雪崩。
| 问题 | 表现 | 防护 |
|---|---|---|
| 击穿 | 热点 key 失效瞬间大量打库 | 单飞锁、提前刷新 |
| 穿透 | 查询不存在数据反复打库 | 空值缓存、布隆过滤器 |
| 雪崩 | 大量 key 同时过期 | TTL 加随机抖动 |
7.4 多级缓存设计
多级缓存适合高读场景,例如配置、权限、商品详情、内容详情。
Local Cache -> Redis -> Database
本地缓存可以用 Ristretto,Redis 用于跨实例共享。配置变更后通过 Pub/Sub 或事件通知各实例失效本地缓存。
缓存 key 设计:
tenant:{tenant_id}:app:{app_id}:content:{content_id}:v{version}
key 中带版本号,可以让发布和回滚更可控。
7.5 数据一致性策略
产品矩阵平台中,强一致只保留给关键交易链路。其他场景优先采用最终一致。
| 场景 | 一致性要求 | 推荐方案 |
|---|---|---|
| 支付状态 | 强一致 + 对账兜底 | 事务、幂等、主动查询 |
| 库存扣减 | 强一致或近强一致 | 锁库、乐观锁 |
| 通知发送 | 最终一致 | 事件 + 重试 |
| 搜索索引 | 最终一致 | CDC / Outbox |
| BI 报表 | 延迟一致 | 批处理 |
分布式事务不要轻易引入。多数业务可以通过本地事务 + Outbox + 补偿任务解决。
7.6 搜索与分析系统
搜索和分析是两个不同系统:
| 系统 | 目标 | 查询特点 |
|---|---|---|
| ElasticSearch | 找到相关内容 | 全文检索、模糊匹配、排序 |
| ClickHouse | 统计和分析 | 聚合、分组、时间窗口 |
不要用 ElasticSearch 做长期 BI,也不要用 ClickHouse 承担实时全文搜索。工具选错,会让维护成本持续上升。
7.7 大数据与埋点管道
埋点事件建议使用统一结构:
{
"event": "content_viewed",
"tenant_id": "t_001",
"app_id": "app_001",
"user_id": "u_001",
"anonymous_id": "anon_001",
"timestamp": "2025-10-16T16:28:36+08:00",
"properties": {
"content_id": "c_001",
"source": "feed"
}
}
事件进入 Kafka 后,按用途分流:
| 流向 | 用途 |
|---|---|
| ClickHouse 明细表 | 行为分析 |
| 用户画像服务 | 标签更新 |
| 推荐服务 | 实时反馈 |
| 风控服务 | 异常行为检测 |
7.8 数据脱敏与隐私保护
敏感数据要先分类:
| 级别 | 示例 | 处理 |
|---|---|---|
| 公开 | 昵称、公开文章 | 正常展示 |
| 内部 | 用户 ID、订单号 | 权限控制 |
| 敏感 | 手机号、邮箱、身份证 | 脱敏、加密 |
| 高敏 | 支付凭证、密钥 | 独立存储、严格审计 |
常见脱敏:
手机号:138****5678
邮箱:le***@example.com
身份证:110***********1234
日志中禁止直接打印高敏数据。对调试确实需要的字段,应使用 token 化后的引用 ID。
7.9 数据架构落地清单
| 检查项 | 标准 |
|---|---|
| 表设计 | 多租户字段、索引、审计字段齐全 |
| Repository | 不泄露 ORM 细节 |
| 缓存 | 每类 key 有 TTL、失效策略、命名规范 |
| 事件 | 关键数据同步不丢事件 |
| 搜索 | 索引构建可重放 |
| 分析 | 埋点结构稳定,字段有字典 |
| 隐私 | 敏感字段加密、脱敏、审计 |
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。