"老板说双11要抗住10万QPS,我看着现在1000 QPS就卡的系统陷入了沉思..." 😰
📖 什么是吞吐量?
吞吐量(Throughput):系统在单位时间内能处理的请求数量。
核心指标
ini
QPS (Queries Per Second)
→ 每秒查询数
→ 适用于读操作
TPS (Transactions Per Second)
→ 每秒事务数
→ 适用于写操作
并发数 (Concurrency)
→ 同时处理的请求数
关系公式:
QPS = 并发数 / 平均响应时间
例如:
100个并发用户
平均响应时间 0.1秒
QPS = 100 / 0.1 = 1000
吞吐量等级划分
yaml
低吞吐量: QPS < 1000
中吞吐量: QPS 1000-10000
高吞吐量: QPS 10000-100000 ← 我们的目标
超高吞吐量: QPS > 100000 ← 互联网大厂
实际场景:
博客网站: 100 QPS
电商平台: 10000 QPS
社交网络: 50000 QPS
搜索引擎: 100000+ QPS
春节抢红包: 1000000+ QPS 😱
🎯 提升吞吐量 vs 降低响应时间
ini
两者的区别:
降低响应时间(RT):
→ 让每个请求更快完成
→ 关注单个请求的速度
→ 用户体验更好
提升吞吐量(QPS):
→ 让系统处理更多请求
→ 关注系统的整体能力
→ 系统容量更大
就像餐厅:
降低RT = 让每桌客人吃得更快(提升翻台率)
提升QPS = 增加更多桌子(扩大容量)
理想情况:
RT 低 + QPS 高 = 完美!✨
🔥 优化技巧一:无锁化编程
为什么要无锁?
objectivec
有锁(Synchronized):
线程1:获取锁 → 执行 → 释放锁
线程2:等待... 等待... 获取锁 → 执行 → 释放锁
线程3:等待... 等待... 等待... 获取锁 → 执行
特点:串行执行,吞吐量低
无锁(CAS):
线程1:CAS 操作(原子性)
线程2:CAS 操作(原子性)
线程3:CAS 操作(原子性)
特点:并行执行,吞吐量高
示例:计数器优化
java
// ❌ 使用锁(慢!)
public class Counter {
private int count = 0;
public synchronized void increment() {
count++; // 串行执行
}
public synchronized int getCount() {
return count;
}
}
// 100线程并发:约 10000 QPS
// ✅ 使用 AtomicInteger(快!)
public class Counter {
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet(); // CAS 无锁
}
public int getCount() {
return count.get();
}
}
// 100线程并发:约 50000 QPS(快5倍!)⚡
// ✅✅ 使用 LongAdder(最快!)
public class Counter {
private LongAdder count = new LongAdder();
public void increment() {
count.increment(); // 热点分离
}
public long getCount() {
return count.sum();
}
}
// 100线程并发:约 200000 QPS(快20倍!)⚡⚡
性能对比:
| 实现方式 | 100线程QPS | 提升倍数 |
|---|---|---|
| synchronized | 10,000 | 1x |
| AtomicInteger | 50,000 | 5x ⚡ |
| LongAdder | 200,000 | 20x ⚡⚡ |
🔥 优化技巧二:批量操作
为什么批量更快?
ini
单条操作:
插入1条 → 1次网络往返 + 1次事务 = 100ms
插入100条 = 100次 × 100ms = 10000ms(10秒)
批量操作:
插入100条 → 1次网络往返 + 1次事务 = 150ms
性能提升:66倍!⚡
数据库批量插入
java
// ❌ 逐条插入(慢!)
for (User user : users) {
userMapper.insert(user); // 1000次 SQL
}
// 1000条数据:约 10秒
// ✅ 批量插入(快!)
userMapper.insertBatch(users); // 1次 SQL
// 1000条数据:约 0.15秒
// 吞吐量:
// 单条:100 TPS
// 批量:6666 TPS(快66倍!)
Redis 批量操作
java
// ❌ 逐个设置(慢!)
for (String key : keys) {
redisTemplate.opsForValue().set(key, value); // 1000次网络往返
}
// 1000个key:约 1000ms
// ✅ Pipeline 批量(快!)
redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
for (String key : keys) {
connection.set(key.getBytes(), value.getBytes());
}
return null;
});
// 1000个key:约 20ms
// 性能提升:50倍!⚡
🔥 优化技巧三:并行处理
单线程 vs 多线程
java
// ❌ 单线程处理(慢!)
public void processOrders(List<Order> orders) {
for (Order order : orders) {
processOrder(order); // 每个100ms
}
}
// 1000个订单 = 100秒
// ✅ 多线程并行(快!)
public void processOrdersParallel(List<Order> orders) {
ExecutorService executor = Executors.newFixedThreadPool(10);
List<CompletableFuture<Void>> futures = new ArrayList<>();
for (Order order : orders) {
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
processOrder(order);
}, executor);
futures.add(future);
}
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
}
// 1000个订单 = 10秒(10个线程并行)
// 吞吐量:
// 单线程:10 TPS
// 10线程:100 TPS(快10倍!)
使用并行Stream
java
// ❌ 串行Stream
List<Integer> result = list.stream()
.map(this::expensiveOperation) // 耗时操作
.collect(Collectors.toList());
// 1000个元素:10秒
// ✅ 并行Stream
List<Integer> result = list.parallelStream()
.map(this::expensiveOperation)
.collect(Collectors.toList());
// 1000个元素:2秒(4核CPU,快5倍)
// 注意:只适用于 CPU 密集型操作
🔥 优化技巧四:连接池与线程池
数据库连接池优化
yaml
# application.yml
# ❌ 默认配置(连接数太少)
spring:
datasource:
hikari:
maximum-pool-size: 10 # 只有10个连接
# 问题:
# 100并发请求
# 只有10个数据库连接
# 90个请求在等待
# → 吞吐量低!
# ✅ 优化后配置
spring:
datasource:
hikari:
maximum-pool-size: 50 # 增加到50个连接
minimum-idle: 10
connection-timeout: 3000
吞吐量提升:
- 10个连接:1000 QPS
- 50个连接:5000 QPS(快5倍!)
线程池优化
java
@Configuration
public class ThreadPoolConfig {
// ❌ 不好的配置
@Bean
public ThreadPoolExecutor badExecutor() {
return new ThreadPoolExecutor(
2, // 核心线程数太少
5, // 最大线程数太少
60L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10), // 队列太小
new ThreadPoolExecutor.AbortPolicy() // 直接拒绝
);
}
// ✅ 好的配置
@Bean
public ThreadPoolExecutor goodExecutor() {
int coreSize = Runtime.getRuntime().availableProcessors() * 2;
return new ThreadPoolExecutor(
coreSize, // 核心线程数 = CPU核心数 × 2
coreSize * 4, // 最大线程数
60L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1000), // 队列容量足够大
new ThreadPoolExecutor.CallerRunsPolicy() // 调用者执行
);
}
}
// 吞吐量对比:
// 2核心线程:500 QPS
// 16核心线程:4000 QPS(快8倍!)
🔥 优化技巧五:异步化
同步 vs 异步
java
// ❌ 同步处理(阻塞线程)
@PostMapping("/order")
public Result<String> createOrder(@RequestBody OrderDTO dto) {
// 1. 创建订单(100ms)
Order order = orderService.create(dto);
// 2. 扣减库存(50ms)
inventoryService.deduct(order.getProductId());
// 3. 发送通知(200ms)
notificationService.send(order.getUserId());
return Result.success("创建成功");
}
// 总耗时:350ms
// 单线程吞吐量:1000 / 0.35 ≈ 2857 QPS
// ✅ 异步处理(不阻塞线程)
@PostMapping("/order")
public Result<String> createOrderAsync(@RequestBody OrderDTO dto) {
// 1. 创建订单(100ms)
Order order = orderService.create(dto);
// 2. 发送MQ消息(1ms,异步)
orderMQ.send(order);
return Result.success("创建成功");
}
// MQ 消费者异步处理
@RocketMQMessageListener(topic = "order-topic")
public class OrderConsumer {
public void onMessage(Order order) {
inventoryService.deduct(order.getProductId());
notificationService.send(order.getUserId());
}
}
// 总耗时:101ms
// 单线程吞吐量:1000 / 0.101 ≈ 9900 QPS
// 提升:3.5倍!⚡
🔥 优化技巧六:读写分离
为什么读写分离能提升吞吐量?
ini
不分离:
所有读写都打到主库
主库:1000 QPS(瓶颈)
读写分离:
写操作:主库(20% 流量)
读操作:3个从库(80% 流量)
主库:200 QPS
从库1:266 QPS
从库2:266 QPS
从库3:266 QPS
总吞吐量:200 + 266×3 = 998 ≈ 1000 QPS
再加2个从库:
主库:200 QPS
5个从库:每个160 QPS
总吞吐量:200 + 160×5 = 1000 QPS
总吞吐量不变?!
实际上:
主库只处理写操作,压力减轻 → 能处理更多写
从库分担读操作 → 读操作吞吐量翻倍
总吞吐量:可以提升 3-5 倍!
实现方式
java
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
// 写操作:主库
@Transactional
public void updateUser(User user) {
DataSourceContextHolder.set("master");
userMapper.updateById(user);
}
// 读操作:从库(随机选择)
public User getUser(Long id) {
DataSourceContextHolder.set("slave");
return userMapper.selectById(id);
}
}
🔥 优化技巧七:缓存提升吞吐量
缓存 vs 数据库
ini
查询数据库:
1次查询 = 100ms
单线程吞吐量 = 1000/100 = 10 QPS
查询Redis:
1次查询 = 1ms
单线程吞吐量 = 1000/1 = 1000 QPS
提升:100倍!⚡⚡
多级缓存
java
@Service
public class ProductService {
// L1:本地缓存(0.1ms)
private LoadingCache<Long, Product> localCache = Caffeine.newBuilder()
.maximumSize(10000)
.build(this::loadFromRedis);
// L2:Redis缓存(1ms)
@Autowired
private RedisTemplate<String, Product> redisTemplate;
// L3:数据库(100ms)
@Autowired
private ProductMapper productMapper;
public Product getProduct(Long id) {
return localCache.get(id);
}
private Product loadFromRedis(Long id) {
String key = "product:" + id;
Product product = redisTemplate.opsForValue().get(key);
if (product == null) {
product = productMapper.selectById(id);
redisTemplate.opsForValue().set(key, product, 1, TimeUnit.HOURS);
}
return product;
}
}
// 吞吐量对比:
// 只用数据库:10 QPS
// Redis缓存:1000 QPS(快100倍)
// 本地缓存:10000 QPS(快1000倍!)⚡⚡
🔥 优化技巧八:性能瓶颈分析
找到瓶颈才能优化
markdown
性能分析工具:
1. JProfiler / YourKit
→ 找出CPU占用最高的方法
2. Arthas
→ 实时监控线上应用
$ watch com.example.UserService getUser "{params,returnObj,throwExp}" -x 3
3. 火焰图(Flame Graph)
→ 可视化性能瓶颈
4. JMeter 压测
→ 发现系统瓶颈在哪里
案例:定位瓶颈
erlang
压测结果:
100并发:5000 QPS
200并发:7000 QPS
300并发:7500 QPS(不再增长)← 瓶颈!
分析:
CPU使用率:30%(不是瓶颈)
内存使用率:50%(不是瓶颈)
数据库连接池:50/50(满了!)← 找到瓶颈!
优化:
增加数据库连接池到100
再次压测:
300并发:12000 QPS(提升60%!)
500并发:15000 QPS
700并发:16000 QPS(又遇到新瓶颈)
继续分析...
📊 完整优化方案对比
优化前
markdown
商品查询接口:
配置:
- 单体应用
- 数据库连接池:10
- 无缓存
- 同步处理
性能:
响应时间:100ms
吞吐量:1000 QPS
瓶颈:
- 数据库连接数不足
- 每次都查数据库
- 无并行处理
优化后
markdown
优化措施:
1. 增加 Redis 缓存(命中率90%)
2. 增加本地缓存(命中率95%)
3. 数据库连接池:10 → 50
4. 读写分离:1主 + 2从
5. 无锁化:synchronized → LongAdder
6. 异步化:非核心功能异步处理
7. 批量操作:批量查询、批量写入
性能:
响应时间:5ms(快20倍)
吞吐量:50000 QPS(快50倍!)⚡⚡
瓶颈移到:
- 网络带宽(可以加CDN)
- 应用服务器CPU(可以横向扩展)
📊 优化效果汇总
| 优化技巧 | 吞吐量提升 | 难度 | 推荐指数 |
|---|---|---|---|
| 无锁化 | 5-20倍 | ⭐⭐⭐ | ⭐⭐⭐⭐ |
| 批量操作 | 10-100倍 | ⭐⭐ | ⭐⭐⭐⭐⭐ |
| 并行处理 | 2-10倍 | ⭐⭐⭐ | ⭐⭐⭐⭐ |
| 连接池优化 | 2-5倍 | ⭐ | ⭐⭐⭐⭐ |
| 异步化 | 3-10倍 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 读写分离 | 3-5倍 | ⭐⭐⭐ | ⭐⭐⭐⭐ |
| 多级缓存 | 10-1000倍 | ⭐⭐ | ⭐⭐⭐⭐⭐ |
💡 面试加分回答模板
面试官:"如何提升系统的吞吐量?"
标准回答:
"我会从以下几个方面提升吞吐量:
1. 缓存优化(最有效):
- 多级缓存:本地缓存 + Redis,减少数据库压力
- 缓存命中率优化到 95%以上
- 效果:吞吐量提升 10-100 倍
2. 无锁化编程:
- 使用 CAS、AtomicXxx、LongAdder 代替 synchronized
- 避免锁竞争
- 效果:吞吐量提升 5-20 倍
3. 批量操作:
- 数据库批量插入、Redis Pipeline
- 减少网络往返
- 效果:吞吐量提升 10-100 倍
4. 异步化:
- 非核心功能异步处理(MQ)
- 释放线程,提高并发
- 效果:吞吐量提升 3-10 倍
5. 并行处理:
- 多线程、并行Stream
- 充分利用多核CPU
- 效果:吞吐量提升 2-10 倍
6. 读写分离:
- 主库写、多个从库读
- 分担数据库压力
- 效果:吞吐量提升 3-5 倍
7. 水平扩展:
- 增加服务器节点
- 负载均衡
- 效果:线性提升
实际案例: 我们的订单系统原来吞吐量 1000 QPS,通过加 Redis 缓存 + 读写分离 + 批量操作 + 异步化,优化到 50000 QPS,提升了 50 倍。"
🎉 总结
提升吞吐量的核心思想:
markdown
1. 减少等待(无锁化、异步化)
→ 让线程不要闲着
2. 批量处理(批量操作)
→ 减少网络往返
3. 并行处理(多线程、分布式)
→ 充分利用资源
4. 分散压力(读写分离、缓存)
→ 不要把鸡蛋放在一个篮子里
记住这个公式:
markdown
吞吐量 = 并发数 / 平均响应时间
提升方向:
- 增加并发数 ↑(连接池、线程池)
- 减少响应时间 ↓(缓存、索引、异步)
最后一句话:
markdown
吞吐量优化的顺序:
1. 先加缓存(最有效)
2. 再优化数据库(索引、读写分离)
3. 然后异步化(解耦、削峰)
4. 最后横向扩展(加机器)
不要一上来就加机器,先优化代码!💻
祝你的系统吞吐量飙升! 📈🚀
📚 扩展阅读