高并发系统设计:从理论到实践(面向零基础初学者的详细教程)

Vim孤独患者
2025-06-29 09:44
阅读 362

一、开篇:什么是高并发系统?它用来做什么?

一、开篇:什么是高并发系统?它用来做什么?

你是否遇到过这样的情况:

  • 在淘宝双十一时,页面半天打不开?
  • 在微博热门话题下,发不出评论?
  • 在抢票软件上,总是“人太多,请稍后再试”?

这些问题背后的原因,其实都与“并发访问量太高”有关。为了应对这种情况,工程师们开发了一套叫作“高并发系统设计”的技术方案。

📌 什么是高并发系统?

简单来说,高并发系统就是能同时处理大量用户请求的系统。比如:

  • 淘宝在双十一能同时处理几十万用户的下单
  • 抖音能同时播放数十万个视频而不停止

📌 高并发系统的用途

  1. 提升用户体验(不卡顿、响应快)
  2. 支持大流量活动(如秒杀、直播、抢红包)
  3. 系统更加稳定可靠

好了,现在我们来正式开始学习吧!


二、环境准备:搭建我们的开发环境

二、环境准备:搭建我们的开发环境

为了动手实践,我们需要以下工具和环境:

✅ 工具清单

工具 版本建议 安装方式
Java 开发环境(JDK) JDK 17 下载安装包 或 使用 SDKMAN!
Spring Boot 3.x Maven 依赖管理
Redis 最新版本 Docker 运行或本地安装
MySQL 8.x XAMPP / Docker / 云数据库
Postman - 下载客户端
Maven / Gradle - 内置在 IDE 中

🔧 安装步骤简述(以 Windows 为例)

  1. 下载并安装 JDK 17

  2. 安装 IntelliJ IDEA(IDE)

  3. 启动 MySQL 和 Redis

    • 使用 Docker 命令:
      docker run --name mysql -e MYSQL_ROOT_PASSWORD=123456 -p 3306:3306 -d mysql:8.0
      docker run --name redis -p 6379:6379 -d redis
      
  4. Postman 安装


三、核心概念:用最简单的语言解释关键技术

三、核心概念:用最简单的语言解释关键技术

高并发系统涉及的概念很多,但我们先重点介绍几个新手最需要了解的核心概念:


💡 1. 并发(Concurrency) vs 并行(Parallelism)

  • 并发是指多个任务交替执行,看起来是“同时进行”,但实际上是轮流做。
  • 并行是指多个任务真的在不同的CPU上同时运行。

🎯 初学者常问:为什么要区分并发和并行? 答:因为系统资源有限,我们要学会“让多个请求看起来都在处理”,而不是真正一起处理。


💡 2. 请求(Request)是什么?

你可以把请求理解为:

  • 用户点击了一个按钮
  • 浏览器向服务器发送了请求数据
  • 比如:你搜索一个商品,这就是一次 HTTP 请求

每个请求都需要服务器花时间处理。如果1万人同时访问,服务器可能会崩溃。


💡 3. 线程池(Thread Pool)

线程池就像是快递公司的送货员团队。

  • 不是一来订单就招一个快递员,而是提前安排好一定数量的员工(线程)来处理任务
  • 当前任务完成后继续处理下一个

这样可以避免系统资源被耗尽。


💡 4. 缓存(Cache):Redis 的作用

缓存就像是一张临时记事本。

比如你经常看某篇文章,每次都去数据库查会很慢。

这时候我们就把内容存在 Redis 中,下次直接读取,速度快!


💡 5. 异步处理:不是马上完成的任务

举个例子:

你点了外卖后,不会一直盯着商家等出餐。你可以去做别的事情,等好了系统再通知你。

异步就是这个道理,任务不阻塞主线程,提高整体效率。


💡 6. 负载均衡(Load Balance)

负载均衡就像饭店门口的领班,负责安排客人坐哪个桌。

服务器也一样,请求太多,就要分给不同的服务器处理。

常见的策略有:

  • 轮询(Round Robin)
  • 加权轮询
  • 最少连接数优先等

四、实战项目:从零构建一个简单的高并发系统

🎯 项目目标:模拟一个“限时秒杀”功能(比如:秒杀一个商品)

我们将实现:

  • 多个用户并发抢购
  • 使用 Redis 预减库存,防止超卖
  • 使用线程池处理请求
  • 返回抢购成功与否的结果

1️⃣ 创建 Spring Boot 项目

使用 Spring Initializr 创建项目:

  • Language: Java
  • Spring Boot Version: 3.0+
  • Dependencies:
    • Spring Web
    • Spring Data JPA
    • Spring Boot Starter Cache
    • Lombok(方便写代码)
    • Redis

然后解压导入 IntelliJ IDEA。


2️⃣ 初始化数据库表:product

CREATE TABLE product (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(255),
    stock INT DEFAULT 0
);

INSERT INTO product (name, stock) VALUES ('iPhone手机', 10);

3️⃣ 编写实体类 Product.java

import jakarta.persistence.*;
import lombok.Data;

@Entity
@Data
public class Product {
    @Id
    private Long id;
    private String name;
    private int stock;
}

4️⃣ 编写 Controller 层

@RestController
@RequestMapping("/api")
public class ProductController {

    @Autowired
    private ProductService productService;

    @GetMapping("/seckill/{productId}")
    public String seckill(@PathVariable Long productId) {
        return productService.handleSeckill(productId);
    }
}

5️⃣ 编写 Service 层(关键逻辑)

这里引入 Redis 控制库存扣减,避免超卖问题。

@Service
@RequiredArgsConstructor
public class ProductService {

    private final ProductRepository productRepository;
    private final RedisTemplate<String, Integer> redisTemplate;

    private static final String STOCK_KEY = "stock:";

    public String handleSeckill(Long productId) {
        String key = STOCK_KEY + productId;

        // 先查 Redis 是否还有库存
        Integer stock = redisTemplate.opsForValue().get(key);
        if (stock == null || stock <= 0) {
            return "秒杀结束";
        }

        // Redis 扣减库存
        Boolean success = redisTemplate.opsForValue().setIfPresent(key, stock - 1);
        if (Boolean.TRUE.equals(success)) {
            // 异步保存数据库扣减库存
            new Thread(() -> {
                Product product = productRepository.findById(productId).orElseThrow();
                product.setStock(product.getStock() - 1);
                productRepository.save(product);
            }).start();

            return "抢购成功!剩余:" + (stock - 1);
        } else {
            return "抢购失败,请重试";
        }
    }
}

6️⃣ 启动 Redis 并预设库存值

使用 Redis CLI 设置初始库存:

redis-cli
SET stock:1 10

7️⃣ 使用 Postman 模拟高并发请求

方法一:手动多次调用 /api/seckill/1

打开 Postman,连续发送几次 GET 请求,观察输出结果:

  • 第一次:剩余9
  • 最后一次:提示“秒杀结束”

方法二:使用 Postman 自带的 Runner 功能模拟并发

  • 使用 Collection Runner
  • 设置循环次数(如100次)
  • 观察最终库存是否保持为 0(表示没有超卖)

✅ 成果展示

如果你按照上面代码完成了操作,恭喜你已经写出了第一个高并发程序!

它可以:

  • 对抗多个用户的同时请求
  • 防止库存超卖(多买)
  • 通过 Redis 快速判断是否有库存
  • 异步处理数据库更新,提高性能

五、常见问题:新手容易遇到的问题 & 解决方法


❓ Q1:为什么不用数据库直接扣库存?

🚫 问题:直接从数据库查询库存 → 扣减 → 更新数据库,会出现并发安全问题(多个请求同时查库存,误以为还有货)。

✅ 正确做法:使用 Redis 做缓存锁,预减库存;最后异步同步数据库。


❓ Q2:Redis 减库存为什么还要异步写入数据库?

🔴 性能瓶颈:如果每次都要写数据库,速度变慢,Redis 优势失去。

✅ 解决方法:使用异步线程处理数据库操作,主流程只依赖 Redis。


❓ Q3:Postman 发送100个请求为啥返回结果混乱?

📌 原因:HTTP 是无状态协议,多个请求之间可能存在竞争资源。

✅ 建议:使用线程池控制并发粒度、增加日志追踪、引入队列机制等高级优化。


❓ Q4:Redis 设置的值为什么有时候获取不到?

📌 错误示例:

Integer stock = (Integer) redisTemplate.opsForValue().get(key);

❌ 强转容易导致 ClassCastException。

✅ 正确写法:

Integer stock = redisTemplate.opsForValue().get(key);

确保使用的是泛型 <String, Integer>,不要强转。


六、学习建议:下一步的学习路径推荐

学会了今天的知识后,你已经掌握了高并发系统的基础能力。

接下来建议继续深入以下几个方向:

🚀 中级进阶主题(适合进阶学习)

主题 相关技术
分布式锁 Redis 分布式锁、Zookeeper、etcd
数据库事务 本地事务、分布式事务(2PC、TCC)
消息队列 RabbitMQ、Kafka、RocketMQ
熔断限流 Hystrix、Sentinel、Nginx 限流
服务降级 微服务调用链中的容错机制
异步编程 CompletableFuture、Reactive Streams
分布式缓存 多层缓存架构、CDN加速
负载均衡实践 Nginx、HAProxy、Ribbon(Spring Cloud)

📘 推荐书籍和资料

  • 《Java并发编程的艺术》
  • 《亿级流量网站架构核心技术》
  • 《Redis实战》
  • 《微服务设计》
  • 《高性能MySQL》

🎉 结束语:

高并发系统听起来复杂,但只要你肯一步步动手实践,就能掌握它的核心思想。

今天的课程只是起点。下一阶段我们可以尝试加入消息队列、分布式事务、服务熔断等进阶模块,打造一个真正的工业级系统。

希望你能坚持学习,并多多动手敲代码!


作者:一位热爱教学的后端讲师 ❤️

欢迎关注公众号【Java成长课】,获取更多免费学习资源!

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝