高并发系统设计:从理论到实践(零基础教程)

二分查找猫
2025-06-24 10:14
阅读 498

一、开篇:什么是高并发?我们为什么需要它?

一、开篇:什么是高并发?我们为什么需要它?

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

  • 抢红包时总是“失败”?
  • “双十一”下单卡顿、甚至报错?
  • 突发热点导致网站直接宕机?

这些问题的背后,都是一个关键指标——并发能力不够

那什么是高并发呢?

简单理解:同时有成千上万的人在访问你的系统,系统要能扛得住,并且快速响应每一个人的请求。

想象一下你开了一个小小的奶茶店,平时只接待一个人点单没有问题。但到了中午,100个人同时冲进来点单,如果你只有一个服务员、一台收银机,那就会乱套了。

高并发系统设计就是教你把这家小店升级成大型连锁,让成百上千顾客都能顺利买到他们想要的奶茶。


二、环境准备:搭建开发环境

二、环境准备:搭建开发环境

所需软件清单(Windows/macOS/Linux通用)

软件 版本 作用
Java JDK 17+ 编写服务端代码
Maven 3.8+ 管理项目依赖
MySQL 8.x 数据库存储
Redis 6.x+ 缓存加速
Spring Boot 2.7+ 快速构建后端服务

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

  1. 安装JDK
    下载地址:https://openjdk.java.net/
    设置环境变量JAVA_HOME

  2. 安装Maven
    下载zip包并解压
    添加MAVEN_HOME环境变量并配置PATH

  3. 安装MySQL和Redis

    • MySQL推荐使用XAMPP一键安装
    • Redis可以下载Windows版本压缩包或通过WSL运行Linux版
  4. IDE建议使用IntelliJ IDEA社区版
    免费好用,支持Spring Boot插件自动生成项目结构

✅ 小贴士:所有工具都建议选择稳定版本,避免兼容性问题。新手建议使用Docker方式部署环境,减少配置时间。


三、核心概念:轻松理解专业术语

三、核心概念:轻松理解专业术语

学习高并发系统,我们要先认识几个最重要的“角色”。

1. 请求(Request)

当用户点击网页或APP按钮时,浏览器或客户端就会发送一个HTTP请求到服务器。

示例:用户想查看商品详情 → 发起GET请求 /products/123

@RestController
public class ProductController {
    @GetMapping("/products/{id}")
    public String getProduct(@PathVariable Long id) {
        return "产品详情:" + id;
    }
}

2. 并发(Concurrency)

同一时间有多个请求到达,比如:

  • 1秒钟有100个用户访问商品详情页 → 这叫QPS=100
  • 如果服务器只能处理50个请求,剩下的就要排队或者拒绝访问

3. 响应时间(RT)

服务器处理一个请求所花的时间。

比如我们加一点延迟来模拟数据库查询慢的情况:

@GetMapping("/slow")
public String slowTest() throws InterruptedException {
    Thread.sleep(200); // 模拟耗时操作
    return "完成";
}

这个接口的平均RT是200毫秒。

4. TPS/QPS

  • QPS:每秒处理的请求数量
  • TPS:每秒处理的事务数量(比如下单、支付等完整流程)

这两个数据越高,说明系统越高效。


四、实战项目:动手做一个高并发小系统

我们将做一个简单的“商品库存秒杀”场景,逐步讲解如何让它变得更快、更稳。

场景需求:

  • 商品ID为1的库存初始值为100
  • 用户发起请求 /buy?productId=1,扣除库存并返回成功或失败

我们按照以下步骤演进这个项目:


Step 1:最简单的实现(低并发)

@RestController
public class BuyController {

    private int stock = 100;

    @GetMapping("/buy")
    public String buy(@RequestParam Long productId) {
        if (stock <= 0) {
            return "库存不足";
        }

        stock--;
        return "购买成功,剩余库存:" + stock;
    }
}

⚠️ 问题:多线程下会出现超卖(多个线程读到的stock都是正数)


Step 2:加入锁机制(解决超卖)

private final Object lock = new Object();

@GetMapping("/buy")
public String buy(@RequestParam Long productId) {
    synchronized (lock) {
        if (stock <= 0) {
            return "库存不足";
        }

        stock--;
        return "购买成功,剩余库存:" + stock;
    }
}

✅ 加入synchronized关键字后,确保每次只有一个请求进入库存判断逻辑。


Step 3:连接真实数据库(提升稳定性)

使用Spring Data JPA和MySQL替代内存变量。

CREATE TABLE product (
  id BIGINT PRIMARY KEY,
  stock INT NOT NULL
);
INSERT INTO product VALUES (1, 100);

Java代码简化如下:

@PutMapping("/buy/db")
public String buyFromDB(@RequestParam Long id) {
    Product product = productRepository.findById(id).orElseThrow();
    synchronized (lock) {
        if (product.getStock() <= 0) {
            return "库存不足";
        }
        product.setStock(product.getStock() - 1);
        productRepository.save(product);
    }
    return "库存更新成功";
}

⚠️ 注意:这种做法虽然正确,但仍可能因数据库锁导致性能瓶颈。

系统架构设计图-2


Step 4:引入缓存(提升速度)

使用Redis缓存库存信息,避免每次访问数据库。

@Autowired
private RedisTemplate<String, Integer> redisTemplate;

@GetMapping("/buy/cache")
public String buyWithCache(@RequestParam Long id) {
    String key = "product:" + id;

    Integer stock = Objects.requireNonNull(redisTemplate.opsForValue().get(key));
    if (stock == null || stock <= 0) {
        return "库存不足";
    }

    synchronized (lock) {
        stock = redisTemplate.opsForValue().get(key);
        if (stock == null || stock <= 0) {
            return "库存不足";
        }
        redisTemplate.opsForValue().set(key, stock - 1);
    }

    return "购买成功!";
}

✅ 使用Redis缓存可显著提高系统吞吐量。


Step 5:消息队列削峰填谷(异步处理订单)

使用RabbitMQ将订单放入队列,延迟执行库存扣除,减轻系统压力。

伪代码示意:

@Autowired
private RabbitTemplate rabbitTemplate;

@GetMapping("/buy/queue")
public String buyWithQueue(@RequestParam Long id) {
    rabbitTemplate.convertAndSend("order_queue", id);
    return "已提交订单,请稍等通知";
}

后端监听队列进行最终库存扣减,缓解高峰期压力。


五、常见问题:新手容易踩坑的地方

❓1. 为什么用了synchronized还会出错?

答:因为Java中每个对象的synchronized只能锁住当前类实例,如果部署的是集群,锁不共享。解决方案:改用Redis分布式锁。

❓2. 使用缓存会不会丢失数据?

答:有可能。缓存中的数据只是临时存储,需要定时刷新回数据库(如Redis持久化 + 定时任务写库)。

❓3. 多台服务器怎么保证统一库存?

答:必须借助数据库行级锁Redis分布式锁,不能依赖本地变量。

❓4. 如何测试并发?

答:使用Apache JMeter或ab命令测试:

ab -n 1000 -c 50 http://localhost:8080/buy

表示并发50个请求,总共发出1000次请求。


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

恭喜你完成了第一个高并发系统的小项目!

继续深入你可以尝试学习以下几个方向:

🧱 进阶知识图谱

模块 内容
分布式锁 Redis分布式锁、RedLock算法
异步处理 RabbitMQ、Kafka基本用法
缓存策略 LRU、TTL、空值缓存防击穿
负载均衡 Nginx配置、轮询策略
限流降级 Sentinel、Hystrix原理与使用
性能调优 JVM参数优化、数据库索引、SQL优化

🧑‍🏫 推荐学习资源

  • 实战书籍:《高性能网站建设指南》《Java并发编程实战》
  • 视频课程:B站上搜索“Java高并发”,关注播放量高的入门系列
  • GitHub开源项目:搜索关键词 high-concurrency-project,参考优秀代码

七、总结一句话:

API接口文档-1

学高并发不是让你第一天就写出淘宝级别的系统,而是从一个小功能开始,学会思考流量来了怎么办、系统会挂吗、有没有更好的办法

保持好奇心,持续做项目,就能一步步掌握这门硬核技术!


文末互动题(选做练习):

修改上面的例子,要求:

  1. 支持多个商品抢购
  2. 记录每一个成功的下单人(可用Redis Set存储)
  3. 统计每种商品售出的数量

试试你能写出多少?欢迎留言交流!


希望这篇从0讲透“高并发”的教程对你真正有用。祝你学习愉快,未来成为并发高手!🚀

评论 0

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