【Feed 高并发架构实战】:雪花 ID + 三级缓存 + 计数旁路设计详解
🔥你好我是fengxin_rou这是我的个人主页fengxin_rou的主页
❄️欢迎查看我的专栏我的专栏
《Java后端学习》、《JAVASE基础》、《JUC并发》、《redis》、《JVM虚拟机》、《MYSQL》、《黑马点评》、《rabbitmq》、《JavaWeb+AI的talis学习系统》、《苍穹外卖》
目录
前言
一、雪花算法 ID 生成器:分布式唯一 ID 设计与实现
1.1 ID 结构设计
1.2 核心代码实现
1.3 关键特性
二、Feed 流三级缓存架构:高并发读性能优化
2.1 缓存层级定义
2.2 缓存读取流程
2.3 hasMore 软缓存设计
2.4 核心优势
三、计数旁路更新与反向索引:实时计数精准失效
3.1 核心设计思路
3.2 计数监听核心代码
3.3 反向索引与清理机制
四、工程化实践要点与架构价值
4.1 关键实践原则
4.2 架构整体价值
结语
前言
在高并发内容平台中,分布式 ID 生成、多级缓存架构、实时计数更新是支撑海量请求的核心技术。本文基于知文业务实战,深度解析雪花算法 ID 生成器、Feed 流三级缓存设计、计数旁路更新与反向索引精准失效方案,覆盖原理、代码实现与工程化实践,可直接用于分布式内容系统架构设计。补充可以观看我前一篇feed文Feed 三级缓存架构详解:分层设计、缓存一致性与高性能实战
一、雪花算法 ID 生成器:分布式唯一 ID 设计与实现
在分布式系统中,数据库自增 ID 存在性能瓶颈、易暴露业务量、不支持多机房等缺陷,雪花算法通过内存生成 ID,单机 TPS 可达千万级,完美解决上述问题。
1.1 ID 结构设计
雪花 ID 为64 位长整型,结构固定:
- 1 位符号位:固定为 0,保证 ID 为正数
- 41 位时间戳:相对自定义纪元,支持约 69 年
- 5 位数据中心 ID:支持 32 个机房
- 5 位工作节点 ID:单机房支持 32 台服务器
- 12 位序列号:单毫秒支持 4096 个 ID
1.2 核心代码实现
@Component public class SnowflakeIdGenerator { // 自定义纪元:2024-01-01 00:00:00 UTC private static final long EPOCH = 1704067200000L; // 各部分位数定义 private static final long WORKER_ID_BITS = 5L; private static final long DATACENTER_ID_BITS = 5L; private static final long SEQUENCE_BITS = 12L; // 最大值计算 private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS); private static final long MAX_DATACENTER_ID = ~(-1L << DATACENTER_ID_BITS); // 位移偏移量 private static final long WORKER_ID_SHIFT = SEQUENCE_BITS; private static final long DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS; private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS; private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS); private final long datacenterId; private final long workerId; private long lastTimestamp = -1L; private long sequence = 0L; // 构造器与参数校验 public SnowflakeIdGenerator(long datacenterId, long workerId) { if (workerId > MAX_WORKER_ID || workerId < 0) { throw new IllegalArgumentException("workerId越界"); } if (datacenterId > MAX_DATACENTER_ID || datacenterId < 0) { throw new IllegalArgumentException("datacenterId越界"); } this.datacenterId = datacenterId; this.workerId = workerId; } // 线程安全生成ID public synchronized long nextId() { long timestamp = System.currentTimeMillis(); // 时钟回拨处理 if (timestamp < lastTimestamp) { long offset = lastTimestamp - timestamp; if (offset <= 5) { // 小幅度回拨:等待时钟追回 try { Thread.sleep(offset); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new IllegalStateException("线程中断"); } timestamp = System.currentTimeMillis(); if (timestamp < lastTimestamp) { throw new IllegalStateException("时钟仍回拨"); } } else { // 大幅度回拨:拒绝生成 throw new IllegalStateException("时钟回拨过大"); } } // 序列号自增 if (lastTimestamp == timestamp) { sequence = (sequence + 1) & SEQUENCE_MASK; if (sequence == 0) { timestamp = waitNextMillis(lastTimestamp); } } else { sequence = 0L; } lastTimestamp = timestamp; // 组装ID return ((timestamp - EPOCH) << TIMESTAMP_LEFT_SHIFT) | (datacenterId << DATACENTER_ID_SHIFT) | (workerId << WORKER_ID_SHIFT) | sequence; } private long waitNextMillis(long lastTimestamp) { long timestamp = System.currentTimeMillis(); while (timestamp <= lastTimestamp) { timestamp = System.currentTimeMillis(); } return timestamp; } }1.3 关键特性
- 线程安全:nextId () 加 synchronized,保证并发安全
- 时钟回拨防护:≤5ms 等待追回,>5ms 直接抛异常,避免 ID 重复
- 高并发:单毫秒支持 4096 个 ID,内存计算无 IO 开销
二、Feed 流三级缓存架构:高并发读性能优化
知文 Feed 采用L1 本地缓存 + L2 Redis 分片缓存 + L3 数据库三级架构,解决高并发下缓存击穿、命中率低、更新不及时问题。
2.1 缓存层级定义
- L1 Caffeine 缓存:本地内存,存储整页 Feed 数据,访问速度 0.001ms
- L2 Redis 分片缓存:存储文章 ID 列表、单篇文章详情、hasMore 标记,支持批量读取
- L3 MySQL 数据库:数据源头,仅缓存未命中时查询
2.2 缓存读取流程
- 请求进入,优先查询 L1,命中直接返回
- L1 未命中,查询 L2,数据完整则组装并回填 L1
- L2 数据不完整,触发 L3 查询,全量覆盖 L2 后回填 L1
- 前端展示并结束流程
2.3 hasMore 软缓存设计
hasMore 标记是否有下一页,采用软缓存 + 兜底逻辑:
- 优先使用缓存中的 hasMore 值
- 缓存缺失时,按
当前页数量==页大小判断 - 兜底不影响最终准确性,下一页请求会修正为真实值
2.4 核心优势
- 防缓存击穿:单航班锁保证同一页只查一次数据库
- 高命中率:L1+L2 组合命中率可达 99%+
- 状态分离:用户态与公共态分离,避免缓存污染
三、计数旁路更新与反向索引:实时计数精准失效
点赞、收藏等计数变化,需不侵入主流程、精准更新所有相关缓存,采用计数旁路 + 反向索引方案实现。
3.1 核心设计思路
- 旁路更新:计数变更通过事件异步触发,不阻塞主接口
- 反向索引:记录文章 ID→被哪些 Feed 页引用,实现精准失效
- 双缓存更新:同步更新 L1 本地缓存与 L2 Redis 缓存
3.2 计数监听核心代码
@EventListener public void onCounterChanged(CounterEvent event) { // 只处理知文点赞/收藏事件 if (!"knowpost".equals(event.getEntityType())) return; String metric = event.getMetric(); if (!"like".equals(metric) && !"fav".equals(metric)) return; String eid = event.getEntityId(); int delta = event.getDelta(); // 更新创作者总计数 try { KnowPost post = knowPostMapper.findById(Long.valueOf(eid)); if (post != null && post.getCreatorId() != null) { long owner = post.getCreatorId(); if ("like".equals(metric)) { userCounterService.incrementLikesReceived(owner, delta); } if ("fav".equals(metric)) { userCounterService.incrementFavsReceived(owner, delta); } } } catch (Exception ignored) {} // 获取最近两小时反向索引 long hourSlot = System.currentTimeMillis() / 3600000L; Set<String> keys = new LinkedHashSet<>(); Set<String> cur = redis.opsForSet().members("feed:public:index:" + eid + ":" + hourSlot); Set<String> prev = redis.opsForSet().members("feed:public:index:" + eid + ":" + (hourSlot - 1)); if (cur != null) keys.addAll(cur); if (prev != null) keys.addAll(prev); // 遍历更新缓存 for (String key : keys) { // 更新L1本地缓存 FeedPageResponse local = feedPublicCache.getIfPresent(key); if (local != null) { FeedPageResponse updated = adjustPageCounts(local, eid, metric, delta, true); feedPublicCache.put(key, updated); } // 更新L2 Redis缓存 String cached = redis.opsForValue().get(key); if (cached != null) { try { FeedPageResponse resp = objectMapper.readValue(cached, FeedPageResponse.class); FeedPageResponse updated = adjustPageCounts(resp, eid, metric, delta, false); writePageJsonKeepingTtl(key, updated); } catch (Exception ignored) {} } else { // 清理失效索引 redis.opsForSet().remove("feed:public:index:" + eid + ":" + hourSlot, key); } } }3.3 反向索引与清理机制
- 正向索引:页面 Key→包含的文章 ID
- 反向索引:文章 ID→被哪些页面引用
- 自动清理:缓存过期时,监听器自动移除无效索引,避免内存浪费
四、工程化实践要点与架构价值
4.1 关键实践原则
- 参数安全化:接口参数校验,防止恶意请求
- 防御性编程:空值判断、异常捕获,保证系统健壮
- 状态隔离:用户态不写入公共缓存,避免数据污染
- 软缓存兜底:关键标记用软缓存 + 逻辑兜底,提升可用性
4.2 架构整体价值
- 性能提升:接口响应从百毫秒降至毫秒级
- 高可用:缓存 + 数据库降级,支持流量洪峰
- 易扩展:支持多机房、多节点水平扩容
- 低耦合:计数、缓存、业务逻辑分离,便于维护
结语
本文完整呈现知文 Feed 高并发架构的三大核心:雪花 ID 保证分布式唯一、三级缓存支撑高并发读、反向索引 + 旁路更新实现实时计数。整套方案兼顾性能、可靠性与可扩展性,适用于内容 Feed、社交动态、商品流等高并发场景。实际落地中,可根据业务调整节点位数、缓存过期时间、回拨容忍阈值,进一步优化架构适配性。
