原创

wrk|ab

温馨提示:
本文最后更新于 2026年03月26日,已超过 79 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

wrk测试

 wrk -t32 -c3000 -d300s --timeout 10s -H "Connection: keep-alive" http://127.0.0.1:8017/redis/read?key=test_key
深度思考
```用户需要支持 30w QPS,这是一个高并发场景,需要从系统层面(文件描述符、端口范围、TCP 参数)、Redis 配置、连接池配置和代码优化等多个维度进行全面
```
支持 **30 万 QPS** 是一个高并发场景,需要从**系统层、Redis 层、应用层**三个维度进行全面优化。让我为你提供完整的优化方案。

## 一、系统层优化(CentOS 8.5)

### 1.1 文件描述符优化

```bash
# 执行优化脚本
cat > /tmp/optimize_30w_qps.sh << 'EOF'
#!/bin/bash
echo "========================================"
echo "  30 万 QPS 系统层优化"
echo "========================================"

# 1. 文件描述符限制
echo "? 优化文件描述符限制..."
cat >> /etc/security/limits.conf << 'LIMITS'

# 30 万 QPS 优化
* soft nofile 1000000
* hard nofile 1000000
root soft nofile 1000000
root hard nofile 1000000
LIMITS

# 2. 系统内核参数优化
echo "? 优化内核参数..."
cat >> /etc/sysctl.conf << 'SYSCTL'

# 30 万 QPS 网络优化
# 文件描述符
fs.file-max = 1000000
fs.nr_open = 1000000

# 网络优化
net.core.somaxconn = 65535
net.core.netdev_max_backlog = 250000
net.ipv4.tcp_max_syn_backlog = 262144
net.ipv4.tcp_fastopen = 3

# TCP 连接优化
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 15
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_probes = 3
net.ipv4.tcp_keepalive_intvl = 15

# 端口范围
net.ipv4.ip_local_port_range = 1024 65535

# 内存优化
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216

# 连接跟踪
net.netfilter.nf_conntrack_max = 1000000
net.netfilter.nf_conntrack_buckets = 262144
SYSCTL

# 应用配置
sysctl -p

echo "✅ 系统层优化完成!"
echo "⚠️  需要重启系统或重新登录使 limits 配置生效"
EOF

chmod +x /tmp/optimize_30w_qps.sh
bash /tmp/optimize_30w_qps.sh
```


### 1.2 验证系统配置

```bash
# 验证配置
ulimit -n                    # 应该显示 1000000
sysctl net.core.somaxconn    # 应该显示 65535
sysctl fs.file-max           # 应该显示 1000000
```


## 二、Redis 集群层优化

### 2.1 Redis 服务端优化配置

在你的 Redis 服务器上执行:

```bash
# Redis 配置文件优化(每个节点)
cat > /etc/redis.conf << 'REDIS_CONF'
# 基础配置
bind 0.0.0.0
port 6221
timeout 0
tcp-keepalive 300
daemonize yes
supervised systemd
pidfile /var/run/redis_6221.pid
loglevel notice
logfile /var/log/redis_6221.log
databases 16

# 30 万 QPS 性能优化
maxclients 100000
maxmemory 4gb
maxmemory-policy allkeys-lru

# 持久化优化(关闭以提升性能)
save ""
appendonly no

# 网络优化
tcp-backlog 65535
tcp-nodelay yes

# 慢查询日志
slowlog-log-slower-than 10000
slowlog-max-len 1024

# 内存碎片整理
activedefrag yes
active-defrag-ignore-bytes 100mb
active-defrag-threshold-lower 10
active-defrag-threshold-upper 100

# 集群配置
cluster-enabled yes
cluster-config-file nodes_6221.conf
cluster-node-timeout 5000
cluster-require-full-coverage no
cluster-replica-validity-factor 10
REDIS_CONF
```


### 2.2 Redis 集群架构建议

对于 30 万 QPS,建议:

```
方案 1:增加主节点数量(推荐)
- 当前:3 主 3 从(每个主节点承担 10 万 QPS)
- 升级:6 主 6 从(每个主节点承担 5 万 QPS)

方案 2:优化现有配置
- 保持 3 主 3 从
- 每个节点 maxmemory 提升到 8GB
- 优化网络参数
```


## 三、应用层优化

### 3.1 优化 application.yml

```yaml
# ... existing code ...
  data:
    redis:
      cluster:
        nodes: 10.206.16.16:6221,10.206.16.16:6222,10.206.16.16:6223,10.206.16.16:6224,10.206.16.16:6225,10.206.16.16:6226
        max-redirects: 6
      password: ming
      timeout: 3000ms
      lettuce:
        pool:
          max-active: 50000
          max-idle: 10000
          min-idle: 5000
          max-wait: 3000
          test-on-borrow: false
          test-on-return: false
          test-while-idle: true
          time-between-eviction-runs-millis: 10000
          num-tests-per-eviction-run: 3
          min-evictable-idle-time-millis: 30000
        client:
          options:
            timeout:
              connect: 3000ms
              command: 2000ms
            disconnected-behavior: ACCEPT_READS
            cluster:
              refresh:
                adaptive: true
                period: 30000
# ... existing code ...
SSHServer:
  port: 22
  user: root
  password: Xia?123456
  host: 175.27.155.235
# ... existing code ...
```


### 3.2 优化 RedisController - 批量操作优化

```java
// ... existing code ...
    @GetMapping("/redis/batch/pipeline")
    public Mono<String> batchWriteWithPipeline(@RequestParam(defaultValue = "100000") Integer count) {
        int batchSize = Math.min(count, 1000000);
        int batchCount = 5000; // 优化:增大批次大小
        log.info("? 开始使用 Pipeline 批量写入 {} 条数据,每批 {} 条...", batchSize, batchCount);

        return Mono.fromRunnable(() -> {
            long start = System.currentTimeMillis();
            try {
                int totalBatches = (int) Math.ceil((double) batchSize / batchCount);
                for (int b = 0; b < totalBatches; b++) {
                    int currentBatchSize = Math.min(batchCount, batchSize - b * batchCount);
                    
                    redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
                        for (int i = 0; i < currentBatchSize; i++) {
                            String key = "batch:" + RandomUtil.randomString(20) + ":" + System.nanoTime();
                            String value = key;
                            connection.set(key.getBytes(), value.getBytes());
                        }
                        return null;
                    });
                    
                    if ((b + 1) % 10 == 0) {
                        log.info("? 已完成 {} 批次,共 {} 批次", b + 1, totalBatches);
                    }
                }

                long elapsed = System.currentTimeMillis() - start;
                long qps = batchSize * 1000L / Math.max(elapsed, 1);
                log.info("✅ Pipeline 批量写入完成!总数:{}, 耗时:{} ms, QPS:{} 条/秒", batchSize, elapsed, qps);
            } catch (Exception e) {
                log.error("❌ Pipeline 写入失败", e);
                throw new RuntimeException("Pipeline 写入失败:" + e.getMessage(), e);
            }
        }).then(Mono.just("✅ Pipeline 批量写入完成,共 " + batchSize + " 条")).onErrorResume(e -> {
            log.error("❌ Pipeline 写入异常", e);
            return Mono.just("写入失败:" + e.getMessage());
        });
    }
// ... existing code ...
    @GetMapping("/redis/batch/async")
    public Mono<String> asyncWrite(@RequestParam(defaultValue = "100000") Integer count) {
        int batchSize = Math.min(count, 1000000);
        log.info("? 开始异步写入 {} 条 Redis 数据...", batchSize);

        return Mono.fromRunnable(() -> {
            long start = System.currentTimeMillis();
            try {
                for (int i = 0; i < batchSize; i++) {
                    String key = "async:" + RandomUtil.randomString(20);
                    String value = key;
                    redisTemplate.opsForValue().set(key, value, 30, TimeUnit.DAYS);

                    if (i % 10_000 == 0 && i > 0) {
                        log.info("? 已写入 {} 条数据", i);
                    }
                }
                long elapsed = System.currentTimeMillis() - start;
                long qps = batchSize * 1000L / Math.max(elapsed, 1);
                log.info("✅ 异步写入完成!总数:{}, 耗时:{} ms, QPS:{} 条/秒", batchSize, elapsed, qps);
            } catch (Exception e) {
                log.error("❌ 异步写入失败", e);
                throw new RuntimeException("异步写入失败:" + e.getMessage(), e);
            }
        }).then(Mono.just("✅ 异步写入完成,共 " + batchSize + " 条")).onErrorResume(e -> {
            log.error("❌ 异步写入异常", e);
            return Mono.just("写入失败:" + e.getMessage());
        });
    }
// ... existing code ...
```


### 3.3 新增高性能批量写入接口

```java
// ... existing code ...
    @GetMapping("/redis/batch/highperf")
    public Mono<String> highPerfBatchWrite(@RequestParam(defaultValue = "300000") Integer count) {
        int batchSize = Math.min(count, 1000000);
        int batchCount = 10000; // 超大批次
        log.info("⚡ 开始高性能批量写入 {} 条数据,每批 {} 条...", batchSize, batchCount);

        return Mono.fromRunnable(() -> {
            long start = System.currentTimeMillis();
            try {
                int totalBatches = (int) Math.ceil((double) batchSize / batchCount);
                for (int b = 0; b < totalBatches; b++) {
                    int currentBatchSize = Math.min(batchCount, batchSize - b * batchCount);
                    
                    redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
                        for (int i = 0; i < currentBatchSize; i++) {
                            String key = "highperf:" + System.nanoTime() + ":" + i;
                            String value = "value:" + System.currentTimeMillis();
                            connection.set(key.getBytes(), value.getBytes());
                        }
                        return null;
                    });
                    
                    if ((b + 1) % 5 == 0) {
                        long currentQps = (b + 1) * batchCount * 1000L / Math.max(System.currentTimeMillis() - start, 1);
                        log.info("⚡ 批次 {}/{}, 当前 QPS: {}", b + 1, totalBatches, currentQps);
                    }
                }

                long elapsed = System.currentTimeMillis() - start;
                long qps = batchSize * 1000L / Math.max(elapsed, 1);
                log.info("⚡ 高性能写入完成!总数:{}, 耗时:{} ms, QPS:{} 条/秒", batchSize, elapsed, qps);
            } catch (Exception e) {
                log.error("❌ 高性能写入失败", e);
                throw new RuntimeException("高性能写入失败:" + e.getMessage(), e);
            }
        }).then(Mono.just("⚡ 高性能写入完成,共 " + batchSize + " 条,QPS: " + 
            (batchSize * 1000L / Math.max(System.currentTimeMillis() - start, 1)))).onErrorResume(e -> {
            log.error("❌ 高性能写入异常", e);
            return Mono.just("写入失败:" + e.getMessage());
        });
    }

    @GetMapping("/redis/stats")
    public String getStats() {
        try {
            Long dbSize = redisTemplate.getRequiredConnectionFactory().getConnection().dbSize();
            return "Redis 统计信息:\n" + "总键数:" + dbSize + "\n" + "服务器时间:" + DateUtil.now();
        } catch (org.springframework.data.redis.RedisConnectionException e) {
            log.error(" Redis 连接失败:{}", e.getMessage());
            return "Redis 连接失败:" + e.getMessage();
        } catch (io.lettuce.core.cluster.PartitionSelectorException e) {
            log.error("❌ Redis 集群槽位分配失败:{},请检查集群状态", e.getMessage());
            return "Redis 集群槽位分配失败:" + e.getMessage();
        } catch (Exception e) {
            log.error("❌ 获取统计信息失败", e);
            return "获取统计信息失败:" + e.getMessage();
        }
    }
// ... existing code ...
```


## 四、压测验证

### 4.1 使用 wrk 进行压测

```bash
# 安装 wrk
yum install -y wrk

# 压测命令(30 万 QPS 目标)
wrk -t12 -c400 -d60s --latency http://你的服务器 IP:8017/redis/batch/highperf?count=1000
```


### 4.2 使用 JMeter 压测

创建测试计划:
- 线程数:1000
- Ramp-up:10 秒
- 循环次数:永远
- 持续时间:300 秒

## 五、优化效果验证

```bash
# 1. 系统资源监控
watch -n 1 'cat /proc/$(pidof java)/limits | grep "open files"'

# 2. Redis 性能监控
redis-cli -c -p 6221 -a ming info stats

# 3. 网络监控
iftop -P -n

# 4. 应用监控
jstat -gcutil $(pidof java) 1000
```


## 六、执行步骤总结

**Step 1**: 执行系统层优化脚本
**Step 2**: 优化 Redis 配置
**Step 3**: 修改应用配置
**Step 4**: 重启所有服务
**Step 5**: 压测验证

现在请按照步骤执行,完成后我们可以进行压测验证!需要我帮你创建压测脚本吗?
正文到此结束