一、背景与挑战
在社交网络场景中,用户、视频、商品等实体可抽象为图模型中的点,而点赞、收藏、关注等交互关系则构成边。早期系统采用 MySQL 存储这些关系数据,但随着业务快速增长,面临以下核心问题:
-
读写性能瓶颈:社交场景下读请求占比高达 99%,传统关系型数据库难以支撑高并发读压力。
-
缺乏图模型语义:业务需要自己做点边相关语义转化,迭代与维护成本极高。
-
一致性难题:双向关系存储、度数计数的需求等业务需求引入了数据不一致风险。
为应对上述挑战,参考 Facebook TAO 架构设计理念,采用Redis+MySQL的两级写穿透架构,在DAU增长数倍的情况下显著降低了总核数的开销,实现了性能与成本的双重突破。
二、核心架构设计
1. 分层存储模型
采用三级分层写穿透架构,通过缓存层屏蔽存储层复杂性。
路由层:负责数据分片,将请求按照分片键进行哈希运算,如crc16%16383,后转发给对应槽位所在的Redis进程。
缓存层:
- 从节点:一级缓存,提供低延迟读服务,响应大部分读请求,同时与主节点组成replica。
- 主节点:二级缓存,通过Redis cluster组织数据分片,主从节点间通过gossip实现高可用。
存储层:
- MySQL Proxy :封装分片路由与事务协调逻辑,支持简单的单机事务。
- MySQL 集群:采用分库分表存储全量数据,通过 CDC 链路同步跨 AZ 数据。
2. 常见缓存问题应对
雪崩防护:
- 缓存对象创建时加入随机 TTL 偏移,避免数据同时大批量过期。
- 多活部署备机集群,当主节点不可用时秒级切换。
缓存穿透:
- 在初始化阶段检查请求,若含非法参数则直接返回报错。
- 引入缓存对象状态机,如已删除,已添加等拦截无效请求。
热点击穿:
- 设置更长的过期时间,例如天,周级别的TTL。
- 结合协程,引入对象粒度的队列,大量请求到来时做限流与等待。
三、历史遗留问题与重构方案
1. 度数维护
在社区的业务场景中,存在着读写点与边的需求,为了保证读取效率点与边分表存储,两者存在一致性问题;此外度数请求QPS达到百万级,若频繁淘汰数据负担将极大。
解决方案
- 热点隔离:将度数数据迁移至 Redis 独立 DB,并设置周级 TTL,避免 LRU 淘汰。
- 单机事务:在分表键一致的场景下,实现可重复读隔离级别的单机事务,容忍幻读以换取性能。
- CDC 同步:带版本号的 CDC 链路保证缓存层与存储层的最终一致性。
2. 双向同步
跨分片挑战
双向边因分片策略分布在不同数据分片,无法原子化更新。
解决方案
采用异步队列 + CDC 兜底策略:
- 正边写入成功后,将反边写入任务加入异步队列,超时则通过 CDC 订阅 binlog 触发幂等操作。
- 网络异常时熔断队列,依赖 CDC 保证最终一致性。
3. 更新类型
乱序问题
若由业务自主增删对象,会导致对象间时间上乱序,体现为用户的收藏夹内乱序问题。
解决方案
由于分片逻辑的存在,此问题类似于双向同步,均需要跨缓存分片,此时由于涉及新旧两个对象,需要约定全局顺序来避免死锁问题。
五、未来架构演进方向
1. 分层存储优化
引入 RocksDB 作为冷热数据中间层,替代部分 Redis 存储,热数据驻留 Redis,冷数据落盘 RocksDB,利用顺序写优势提升写入性能,适用于更多的工作负载场景。
4. 数据模型重构
利用宽表等数据结构,将点与边数据合并存储,减少跨表查询,提升多跳查询效率;进一步支持动态字段扩展,降低业务迭代成本。
5. 中心化管控与弹性扩展
根据负载扩缩容主从比,提升资源利用率;通过中心化服务管理分片、路由等信息,简化运维复杂度。