你好我是fengxin_rou这是我的个人主页fengxin_rou的主页❄️欢迎查看我的专栏我的专栏《Java后端学习》、《JAVASE基础》、《JUC并发》、《redis》、《JVM虚拟机》、《MYSQL》、《黑马点评》、《rabbitmq》、《JavaWebAI的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 个 ID1.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.001msL2 Redis 分片缓存存储文章 ID 列表、单篇文章详情、hasMore 标记支持批量读取L3 MySQL 数据库数据源头仅缓存未命中时查询2.2 缓存读取流程请求进入优先查询 L1命中直接返回L1 未命中查询 L2数据完整则组装并回填 L1L2 数据不完整触发 L3 查询全量覆盖 L2 后回填 L1前端展示并结束流程2.3 hasMore 软缓存设计hasMore 标记是否有下一页采用软缓存 兜底逻辑优先使用缓存中的 hasMore 值缓存缺失时按当前页数量页大小判断兜底不影响最终准确性下一页请求会修正为真实值2.4 核心优势防缓存击穿单航班锁保证同一页只查一次数据库高命中率L1L2 组合命中率可达 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; SetString keys new LinkedHashSet(); SetString cur redis.opsForSet().members(feed:public:index: eid : hourSlot); SetString 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、社交动态、商品流等高并发场景。实际落地中可根据业务调整节点位数、缓存过期时间、回拨容忍阈值进一步优化架构适配性。
【Feed 高并发架构实战】:雪花 ID + 三级缓存 + 计数旁路设计详解
发布时间:2026/5/23 4:26:48
你好我是fengxin_rou这是我的个人主页fengxin_rou的主页❄️欢迎查看我的专栏我的专栏《Java后端学习》、《JAVASE基础》、《JUC并发》、《redis》、《JVM虚拟机》、《MYSQL》、《黑马点评》、《rabbitmq》、《JavaWebAI的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 个 ID1.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.001msL2 Redis 分片缓存存储文章 ID 列表、单篇文章详情、hasMore 标记支持批量读取L3 MySQL 数据库数据源头仅缓存未命中时查询2.2 缓存读取流程请求进入优先查询 L1命中直接返回L1 未命中查询 L2数据完整则组装并回填 L1L2 数据不完整触发 L3 查询全量覆盖 L2 后回填 L1前端展示并结束流程2.3 hasMore 软缓存设计hasMore 标记是否有下一页采用软缓存 兜底逻辑优先使用缓存中的 hasMore 值缓存缺失时按当前页数量页大小判断兜底不影响最终准确性下一页请求会修正为真实值2.4 核心优势防缓存击穿单航班锁保证同一页只查一次数据库高命中率L1L2 组合命中率可达 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; SetString keys new LinkedHashSet(); SetString cur redis.opsForSet().members(feed:public:index: eid : hourSlot); SetString 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、社交动态、商品流等高并发场景。实际落地中可根据业务调整节点位数、缓存过期时间、回拨容忍阈值进一步优化架构适配性。