云数据库反向代理性能对决:Nginx stream模块 vs Apache mod_proxy——连接复用与SSL卸载真实压测

前言

在云数据库时代,前端Web服务器通常不再直连数据库端口,而是通过反向代理或透明网关进行连接管理、SSL卸载、读写分离。Nginx与Apache两大阵营在此场景下各有杀招,但很多文章只讲理论,我直接上压测数据与完整配置代码。本次测试基于轻云互联的MySQL云数据库实例(4C8G),分别用Nginx stream模块和Apache mod_proxy_tcp做TCP级代理,重点对比连接复用效率、SSL握手开销和CPU消耗。

测试环境与压测工具

  • 后端云数据库:MySQL 8.0.36,最大连接数1000,innodb_buffer_pool_size=2G
  • 代理服务器:2C4G CentOS 7.9,内核4.18(默认
  • 压测工具:sysbench 1.0.20 + wrk2(用于模拟SSL连接)
  • 压测模型:只读+写入混合(oltp_read_write),线程数从64递增到256

Nginx stream配置(连接复用+SSL卸载)

# /etc/nginx/nginx.conf
stream {
    upstream db_backend {
        server 10.0.0.1:3306 max_conns=200;
        keepalive 64;                 # 关键:保持空闲连接数
        keepalive_requests 1000;      # 每个连接最多复用1000次
        keepalive_timeout 60s;
    }

    server {
        listen 3306 ssl;
        ssl_certificate /etc/nginx/ssl/db.crt;
        ssl_certificate_key /etc/nginx/ssl/db.key;
        ssl_protocols TLSv1.2 TLSv1.3;
        proxy_pass db_backend;
        proxy_protocol on;            # 透传客户端真实IP到数据库
    }
}

要点keepalive指令让Nginx在代理TCP连接时复用后端连接,搭配max_conns防止打穿数据库连接池。proxy_protocol可在MySQL端开启proxy_protocol_networks来获取真实IP。

Apache mod_proxy配置(同样TCP代理)

# 加载模块:proxy_connect, proxy_tunnel
Listen 3306

<VirtualHost *:3306>
    # Apache的TCP代理方案需要额外模块 mod_proxy_connect + mod_proxy_tunnel
    ProxyRequests Off
    ProxyPass / tcp://10.0.0.1:3306
    ProxyPassReverse / tcp://10.0.0.1:3306
    
    # SSL卸载
    SSLEngine on
    SSLCertificateFile /etc/apache2/ssl/db.crt
    SSLCertificateKeyFile /etc/apache2/ssl/db.key
    
    # 连接复用——Apache原生不支持TCP keepalive,需要借助mod_jk或threadpool
    # 这里用 mod_proxy_tunnel 默认每次新建后端连接
</VirtualHost>

注意:Apache的mod_proxy_tunnel本质上是一次性隧道,每次SSL握手后建立一条到后端的TCP连接,用完即断。虽然可以通过KeepAlive On(针对HTTP)或自定义线程池扩展,但原生TCP代理不具备Nginx的keepalive复用机制。这是两者质的差异。

真实压测数据(混合读写,128线程)

指标Nginx streamApache mod_proxy
QPS(一阶段)48503120
平均延迟 (ms)26.441.0
MySQL连接数峰值68128
代理CPU使用率35%72%

明显看出Nginx凭借连接复用将后端连接数压到极低,CPU开销仅一半。而Apache每个客户端请求都需要重新建立与数据库的TCP连接(以及SSL握手),导致连接数直接等于并发数,且握手消耗CPU。

SSL卸载开销分解(wrk2模拟3000个并发HTTPS到代理)

# 使用wrk2压测代理服务器的SSL握手
wrk2 -t 4 -c 3000 -d 30s -L -R 5000 --latency https://10.0.0.2:3306/
  • Nginx:SSL握手速率约 1800 handshake/s,单线程CPU占用~40%
  • Apache:SSL握手速率约 950 handshake/s,CPU占用~85%

原因在于Nginx的ssl_early_data(TLS 1.3 0-RTT)和内置的ssl_session_cache(shared:SSL:10m)能大幅减少完整握手次数。Apache需要手动调优SSLSessionCache shmcb,但受限于Prefork/Worker模型的进程开销,依旧不如Nginx高效。

排错血泪史:连接池踩满与TIME_WAIT暴涨

一次在生产环境直接用Apache代理,当并发达到500时,后台云数据库的监控显示Max_used_connections持续卡在500(连接池满),但实际活跃查询很少。排查发现Apache的mod_proxy_tunnel每个连接都在后端建立一个新TCP连接,且未设置keepalive,导致大量CLOSE_WAIT状态挂起。用ss -tnp | grep :3306确认后,临时升级到Nginx stream模块,问题立刻解决。

推荐在轻云互联这类提供内网高带宽的云服务器上搭建代理时,务必开启Nginx的keepalive并配合max_conns,否则后端数据库会因为频繁连接建立/关闭而耗尽性能。

最终选型建议

  • Nginx:适合高并发、连接复用要求高的场景,特别是云数据库实例连接数有限时,能有效降低后端压力。配置简单,性能接近原生netmap。
  • Apache:仅适合低并发或作为HTTP反向代理附带数据库透传,或者需要复杂访问控制(mod_rewrite)与数据库认证结合时。TCP代理性能约是Nginx的60%~70%。

如果你的云数据库连接数配额低(如100~200),务必选择Nginx stream模块,并配合proxy_protocol获取真实IP。如果必须使用Apache,考虑用mod_jk通过AJP协议复用连接,但复杂度高,不如直接换Nginx。