dbaplus社群 9小时前
从0到百万用户:后悔当初架构没这样设计,现在全是坑……
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文分享了从零到百万用户的架构演进历程,作者详细介绍了在不同用户规模下,架构面临的挑战和解决方案,包括单体应用、微服务拆分、缓存、消息队列、数据库扩展、可观测性以及CDN等。通过总结经验教训,强调了提前规划、逐步优化和关注性能瓶颈的重要性,为开发者提供了宝贵的架构设计参考。

🚀 早期架构面临的挑战:最初的单体架构在用户量快速增长时暴露出性能瓶颈,数据库锁、GC停顿等问题导致系统可用性下降,扩展性成为首要问题。

💡 缓存的重要性:引入Redis缓存后,数据库负载降低60%,缓存命中的读请求响应时间大幅缩短,证明了缓存对提升性能的关键作用。

🛠️ 微服务与异步处理:通过拆分微服务、使用消息队列Kafka进行异步处理,解决了链式REST调用导致的延迟叠加问题,提升了系统整体性能。

📊 数据库扩展与可观测性:读写分离、分片、归档等数据库扩展策略,以及分布式追踪、集中式日志、监控面板等可观测性工具,帮助应对用户增长带来的挑战,快速定位问题。

🌐 CDN与边缘缓存:将静态资源迁移至CDN,有效降低了源站负载,提升了用户访问速度,是应对大规模用户访问的有效手段。

原创 Latency Gambler 2025-05-29 07:15 广东

架构设计初期,一定要给后续留够余地。

项目上线时,我们仅有100名用户。但几个月后,用户数迅速突破 10,000,接着是 100,000。此时,扩展性问题比用户增长来得更快。

我们的目标是支持 100 万用户,但原本支撑 1,000 用户的架构已不堪重负。回顾过去,以下是我希望从第一天就建立起来的架构,以及在高压下扩展的宝贵经验。

阶段 1:单体的成功(直到它失败)

最初的架构非常简单:

    Spring Boot 应用

    MySQL 数据库

    NGINX 负载均衡器

    所有组件部署在一台虚拟机(VM)上

    [ Client ] → [ NGINX ] → [ Spring Boot App ] → [ MySQL ]

    该架构轻松应对 500 并发用户。但当并发用户达到 5,000 时:

      CPU 满载

      查询变慢

      系统可用性跌破 99%

    监控显示问题根源:数据库锁、GC 停顿、线程竞争。

    阶段 2:增加服务器(却忽略了真正的瓶颈)

    我们在 NGINX 后增加了多个应用服务器:

      [ Client ] → [ NGINX ] → [ App1 | App2 | App3 ] → [ MySQL ]

      读请求 扩展良好,但 写请求 仍集中在 单 MySQL 实例。

      负载测试结果:

        | Users | Avg Response Time |

        | ----- | ----------------- |

        | 1000  | 120ms             |

        | 5000  | 480ms             |

        | 10000 | 3.2s              |

        瓶颈并非 CPU,而是 数据库。

        阶段 3:引入缓存层

        我们添加 Redis 作为读密集型查询的缓存:

          public User getUser(String id) {

              User cached = redisTemplate.opsForValue().get(id);

              if (cached != nullreturn cached;

              User user = userRepository.findById(id).orElseThrow();

              redisTemplate.opsForValue().set(id, user, 10, TimeUnit.MINUTES);

              return user;

          }

          效果显著:

            数据库负载减少 60%

            缓存命中的读请求响应时间降至 200ms 以内

          1,000个并发用户请求的基准测试:

            | Approach   | Avg Latency | DB Queries |

            | ---------- | ----------- | ---------- |

            | No Cache   | 150ms       | 1000       |

            | With Cache | 20ms        | 50         |

            阶段 4:拆分单体架构

            将核心功能拆分为 微服务:

              用户服务

              帖子服务

              动态流服务

            每个服务拥有独立数据库表(初期共享同一数据库实例)。

            服务间通过 REST API 通信:

              @RestController

              public class FeedController {

                  @GetMapping("/feed/{userId}")

                  public Feed getFeed(@PathVariable String userId) {

                      User user = userService.getUser(userId);

                      List<Post> posts = postService.getPostsForUser(userId);

                      return new Feed(user, posts);

                  }

              }

              但链式 REST 调用导致 延迟叠加:一个请求可能触发 3-4 次内部调用。

              用户量激增时,性能急剧下降。

              阶段 5:消息队列与异步处理

              引入 Kafka 处理异步任务:

                用户注册触发 Kafka 事件

                下游服务通过消费事件替代同步 REST 调用

                // Publish

                kafkaTemplate.send("user-signed-up", newUserId);

                // Consume

                @KafkaListener(topics = "user-signed-up")

                public void handleSignup(String userId) {

                    recommendationService.prepareWelcomeRecommendations(userId);

                }

                使用Kafka后,注册延迟从1.2秒降至300毫秒,因为昂贵的下游任务超出了带宽。

                阶段 6:数据库扩展

                当用户数达到 50 万时,即使有缓存,单 MySQL 实例仍无法支撑。

                解决方案:

                  读写分离 → 分离读/写流量

                  分片 → 按用户 ID 分区(例如 0-999k、100 万-200 万等)

                  归档表 → 将冷数据移出主表

                分片查询路由示例:

                  if (userId < 1000000) {

                      return jdbcTemplate1.query(...);

                  else {

                      return jdbcTemplate2.query(...);

                  }

                  效果:减少 写竞争,各分片查询时间显著优化。

                  阶段 7:可观测性

                  用户量突破 10 万后,缺乏监控导致故障排查困难。

                  我们引入:

                    分布式追踪(Jaeger + OpenTelemetry)

                    集中式日志(ELK 技术栈)

                    Prometheus + Grafana 监控面板

                  Grafana 面板示例:

                    | Metric         | Value   |

                    | -------------- | ------- |

                    | P95 latency    | 280ms   |

                    | DB connections | 120/200 |

                    | Kafka lag      | 0       |

                    改进前,定位延迟峰值需 数小时;改进后仅需 几分钟。

                    阶段 8:CDN 与边缘缓存

                    用户量达 100 万时,40% 流量来自 静态资源(图片、头像、JS 文件)。

                    我们将静态资源迁移至 Cloudflare CDN,并设置强缓存策略:

                      Asset              | Origin Latency | CDN Latency |

                      | ------------------ | -------------- | ----------- |

                      /static/app.js     | 400ms          | 40ms        |

                      /images/avatar.png | 300ms          | 35ms        |

                      效果:源站流量减少 70%。

                      若重头再来:更早构建的最终架构

                      如果重新开始,我会跳过中间阶段,直接采用以下架构:

                        [ Client ]  

                           ↓  

                        [ CDN + Edge Caching ]  

                           ↓  

                        [ API Gateway → Service Mesh ]  

                           ↓  

                        [ Microservices + Kafka + Redis Cache ]  

                           ↓  

                        [ Sharded Database + Read Replicas ]

                        关键经验:

                          缓存是必选项,而非可选项

                          数据库扩展需提前设计

                          异步处理至关重要

                          可观测性越早投入回报越大

                        扩展的核心不是“堆服务器”,而是 逐层消除瓶颈。

                        最终基准测试(100 万用户,1,000 RPS)

                          Metric             | Value  |

                          | ------------------ | ------ |

                          P95 API Latency    | 210ms  |

                          Error Rate         | <0.1%  |

                          | Cache Hit Ratio    | 85%    |

                          | DB Query Rate      | 50 qps |

                          | Kafka Consumer Lag | 0      |

                          总结

                          扩展到 100 万用户的关键不是追逐新技术,而是按正确顺序解决正确问题。

                          支撑前 1,000 用户的架构,无法支撑下一个百万。

                          在问题爆发前,提前设计应对方案。

                          -

                          你在扩展过程中踩过哪些架构大坑?欢迎分享。

                          作者丨The Latency Gambler    编译丨Rio

                          来源丨网址:https://medium.com/@kanishks772/scaling-to-1-million-users-the-architecture-i-wish-i-knew-sooner-39c688ded2f1

                          dbaplus社群欢迎广大技术人员投稿,投稿邮箱:editor@dbaplus.cn

                          阅读原文

                          跳转微信打开

                          Fish AI Reader

                          Fish AI Reader

                          AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

                          FishAI

                          FishAI

                          鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

                          联系邮箱 441953276@qq.com

                          相关标签

                          架构演进 微服务 缓存 数据库扩展 可观测性
                          相关文章