第一章:Go语言数据库连接池配置反模式的根源剖析
Go 应用中数据库性能瓶颈常被误归因于 SQL 本身,实则大量源于连接池配置的深层反模式。这些反模式并非源于语法错误,而是对 database/sql 包底层机制(如连接复用、空闲超时、最大生命周期)的误解与忽视。
连接池参数与运行时行为的错配
SetMaxOpenConns 被盲目设为 0(无限制)或极小值(如 2),将直接导致请求排队阻塞或资源耗尽。更隐蔽的问题是 SetMaxIdleConns 与 SetConnMaxLifetime 的协同失衡:若空闲连接存活时间远长于数据库侧连接超时(如 MySQL wait_timeout=60s),而 ConnMaxLifetime 未设置或设为 300s,则连接在归还池后仍可能被复用时触发 driver: bad connection 错误。
忽视驱动层的隐式连接管理
pgx/v5 或 mysql 驱动在 Open() 后并不立即建立物理连接,首次 Query() 才触发拨号。若应用启动后未及时执行健康检查,连接池实际处于“惰性未就绪”状态,引发首请求高延迟。应显式调用:
// 初始化后立即验证连接池可用性
if err := db.PingContext(context.Background()); err != nil {
log.Fatal("failed to ping database:", err) // 触发连接建立与验证
}
连接泄漏的静默积累
未正确关闭 *sql.Rows 或 *sql.Tx 是典型泄漏源。即使使用 defer rows.Close(),若 rows.Next() 循环提前 return,defer 不会执行。推荐使用带作用域的迭代模式:
rows, err := db.QueryContext(ctx, "SELECT id FROM users LIMIT 10")
if err != nil {
return err
}
defer rows.Close() // 安全:无论循环是否完成均关闭
for rows.Next() {
var id int
if err := rows.Scan(&id); err != nil {
return err // 立即返回前 defer 已注册,保证关闭
}
}
if err := rows.Err(); err != nil { // 检查扫描末尾错误
return err
}
常见反模式配置对比:
| 参数 | 危险值 | 推荐值(参考) | 风险说明 |
|---|---|---|---|
MaxOpenConns |
0(无限) | CPU 核数 × 4 ~ 8 | 可能压垮数据库并发连接数 |
MaxIdleConns |
0 | Min(10, MaxOpenConns) |
空闲连接为 0 导致每次查询新建连接 |
ConnMaxLifetime |
0(永不过期) | 30m ~ 1h | 与 DB 层超时不一致引发 stale 连接 |
第二章:sql.DB核心配置项的理论误读与实践陷阱
2.1 SetMaxOpenConns()缺失导致连接耗尽的压测复现与根因分析
在高并发压测中,未调用 SetMaxOpenConns() 的 Go 应用迅速耗尽数据库连接,触发 sql: connection limit exceeded 错误。
复现场景关键代码
db, _ := sql.Open("mysql", dsn)
// ❌ 遗漏:db.SetMaxOpenConns(20)
// ❌ 遗漏:db.SetMaxIdleConns(10)
// ❌ 遗漏:db.SetConnMaxLifetime(30 * time.Minute)
该配置缺失导致
sql.DB默认MaxOpenConns = 0(即无上限),连接池持续新建连接直至 DB 层拒绝;同时MaxIdleConns默认为 2,空闲连接快速被回收,加剧新建压力。
连接池行为对比(压测 QPS=500)
| 配置状态 | 平均连接数 | 超时错误率 | 连接建立耗时(ms) |
|---|---|---|---|
| 全部未设置 | 482 | 37.2% | 126 |
| 正确设置三参数 | 18 | 0% | 3.1 |
根因链路
graph TD
A[HTTP 请求] --> B[sql.Query]
B --> C{连接池有空闲连接?}
C -- 否 --> D[新建连接]
C -- 是 --> E[复用连接]
D --> F[DB 拒绝新连?]
F -- 是 --> G[panic: connection limit exceeded]
2.2 SetMaxIdleConns()未调优引发的连接泄漏与GC压力实证
HTTP客户端默认 MaxIdleConns=100,若高并发场景下未显式调优,空闲连接池持续膨胀,导致连接泄漏与频繁GC。
连接泄漏现象复现
client := &http.Client{
Transport: &http.Transport{
MaxIdleConns: 0, // ❌ 危险:禁用空闲连接复用
MaxIdleConnsPerHost: 0,
},
}
// 每次请求新建TCP连接,旧连接未及时关闭,fd耗尽
MaxIdleConns=0 强制禁用连接复用,net/http 不回收底层 *net.Conn,触发 runtime.GC() 频繁扫描未释放的 conn 对象,加剧堆压力。
关键参数对照表
| 参数 | 默认值 | 风险表现 | 推荐值 |
|---|---|---|---|
MaxIdleConns |
100 | 全局空闲连接上限不足 → 泄漏 | ≥500 |
MaxIdleConnsPerHost |
100 | 单域名连接堆积 → TIME_WAIT 爆满 | ≥200 |
GC压力链路
graph TD
A[高频创建Request] --> B[Transport.NewConn]
B --> C[conn未归还idle list]
C --> D[conn对象长期存活]
D --> E[GC标记-清除周期缩短]
E --> F[STW时间上升+内存抖动]
2.3 SetConnMaxLifetime()缺省值引发的DNS漂移与连接僵死现场还原
当 SetConnMaxLifetime(0)(即缺省值)被使用时,连接永不过期,但底层 DNS 解析结果可能已变更。
DNS漂移触发条件
- 服务端 IP 轮转(如 Kubernetes Service ClusterIP 后端 Pod 重建)
- 云厂商 LB VIP 更新(如阿里云 SLB、AWS ALB)
- 客户端缓存旧 A 记录,
net.Dial复用僵死连接
连接僵死表现
db, _ := sql.Open("mysql", "user:pass@tcp(db.example.com:3306)/test")
db.SetConnMaxLifetime(0) // ⚠️ 缺省行为:永不回收连接
db.SetMaxOpenConns(10)
逻辑分析:
表示禁用生命周期管理;连接一旦建立,即使后端 DNS 已指向新 IP,该连接仍持续复用旧 TCP 流,导致i/o timeout或connection refused。参数并非“无限长”,而是“不检查”。
关键参数对照表
| 参数 | 值 | 行为 |
|---|---|---|
SetConnMaxLifetime(0) |
0 | 禁用超时,连接永不主动关闭 |
SetConnMaxLifetime(5 * time.Minute) |
>0 | 每次归还连接池前检查创建时间,超时即关闭 |
还原流程
graph TD
A[应用发起Query] --> B{连接池返回conn?}
B -->|复用旧连接| C[TCP发包至过期IP]
C --> D[SYN timeout / RST]
D --> E[应用层报错:dial tcp: i/o timeout]
2.4 SetConnMaxIdleTime()配置不当导致的空闲连接过早回收与重连风暴
当 SetConnMaxIdleTime(30 * time.Second) 被误设为过短值(如 5s),连接池在空闲5秒后即强制关闭健康连接,引发下游服务频繁重建连接。
连接生命周期异常表现
- 客户端日志高频出现
dial tcp: lookup...或connection reset - 数据库侧观察到大量
CONNECT/DISCONNECT事件,但无对应业务峰值
典型错误配置示例
db.SetConnMaxIdleTime(5 * time.Second) // ❌ 危险:远低于网络RTT+TLS握手耗时
db.SetMaxOpenConns(100)
db.SetMaxIdleConns(50)
逻辑分析:
5s小于多数云数据库TLS握手(通常800ms–3s)与内网RTT(10–50ms)之和,导致空闲连接未被复用即销毁。SetConnMaxIdleTime控制单个连接最大空闲时长,非连接池整体存活策略。
推荐参数对照表
| 场景 | MaxIdleTime | 说明 |
|---|---|---|
| 内网直连 PostgreSQL | 30m | 避免TCP keepalive干扰 |
| 跨AZ MySQL | 5m | 平衡超时探测与复用率 |
| Serverless函数调用 | 30s | 匹配冷启动窗口 |
连接回收与重建流程
graph TD
A[连接空闲] --> B{空闲时长 ≥ MaxIdleTime?}
B -->|是| C[标记为可关闭]
B -->|否| D[继续复用]
C --> E[异步关闭底层TCP]
E --> F[下次GetConn触发新拨号]
F --> G[重连风暴风险]
2.5 配置项组合失效:并发场景下四参数协同失效的火焰图追踪
当 timeout=300ms、retry=3、poolSize=8 与 maxPending=16 四参数在高并发下耦合,JVM 线程栈深度激增,火焰图中出现显著的 BlockingQueue.offer() → AbstractQueuedSynchronizer.acquire() → Unsafe.park() 堆叠热点。
数据同步机制
// 关键阻塞点:maxPending 与 poolSize 不匹配导致任务积压
if (!taskQueue.offer(task, timeout, TimeUnit.MILLISECONDS)) { // timeout=300ms
throw new RejectedExecutionException("Queue full: " + taskQueue.size());
}
逻辑分析:offer() 在 maxPending=16 下超时返回失败,但重试逻辑(retry=3)未退避,引发线程反复争抢锁,加剧 AQS 竞争。
失效参数关联表
| 参数 | 值 | 协同失效表现 |
|---|---|---|
timeout |
300ms | 超时过短,触发频繁 reject |
retry |
3 | 无指数退避,放大瞬时压力 |
poolSize |
8 | 无法消化 maxPending=16 的积压 |
执行流关键路径
graph TD
A[Submit Task] --> B{queue.offer?}
B -- Yes --> C[Execute]
B -- No --> D[Retry?]
D -- retry<3 --> A
D -- retry==3 --> E[Reject & park]
第三章:Go官方文档弱化表述的技术成因与历史演进
3.1 Go 1.0–1.13文档中连接池配置的语义模糊与默认行为变迁
默认 http.Transport 行为漂移
Go 1.0 中 MaxIdleConnsPerHost 默认为 2,而 Go 1.7 起悄然升至 100;但文档长期未明确标注该变更时点,导致大量旧代码隐式依赖过时假设。
关键配置语义歧义示例
tr := &http.Transport{
MaxIdleConns: 50,
MaxIdleConnsPerHost: 5,
}
MaxIdleConns:全局空闲连接总数上限(含所有 host)MaxIdleConnsPerHost:单 host 最大空闲连接数(非并发限制)- 若未设
IdleConnTimeout(Go 1.0–1.6 默认为 0,即永不回收),连接可能长期滞留,引发 TIME_WAIT 爆炸。
版本关键变更对照表
| Go 版本 | MaxIdleConnsPerHost 默认值 |
IdleConnTimeout 默认值 |
文档是否明示变更 |
|---|---|---|---|
| 1.0 | 2 | 0(禁用) | 否 |
| 1.7 | 100 | 30s | 否(仅 release note 提及) |
| 1.13 | 100 | 30s | 是(首次写入 pkg doc) |
连接复用决策流程(简化)
graph TD
A[发起请求] --> B{host 是否已有空闲连接?}
B -->|是| C[取最近空闲 conn]
B -->|否| D[新建连接]
C --> E{conn 是否过期?}
E -->|是| F[关闭并新建]
E -->|否| G[复用并标记为 in-flight]
3.2 database/sql包抽象层设计对配置可见性的天然压制机制
database/sql 通过 Driver 接口与 sql.DB 实例解耦,将底层驱动细节(如连接池参数、超时策略、重试逻辑)封装在 *sql.DB 内部,对外仅暴露通用方法。
配置隐匿的典型表现
sql.Open()仅接收 driverName 和 dataSourceName 字符串,不校验参数合法性- 连接池配置(
SetMaxOpenConns等)需显式调用,且无默认值反馈机制 - 驱动特有参数(如 MySQL 的
parseTime=true)被吞入dataSourceName,无法结构化提取
示例:MySQL DSN 解析的不可见性
db, _ := sql.Open("mysql", "user:pass@tcp(127.0.0.1:3306)/test?parseTime=true&loc=Local")
// parseTime、loc 等参数由驱动解析,sql.DB 层完全不可见、不可 introspect
此处
parseTime=true被github.com/go-sql-driver/mysql解析为时间类型自动转换开关,但*sql.DB实例自身无任何字段或方法可反射该配置状态,形成“配置黑洞”。
| 抽象层级 | 可见配置项 | 是否可运行时检查 |
|---|---|---|
sql.DB |
MaxOpenConns, ConnMaxLifetime | ✅(通过 DB.Stats() 间接推断) |
| Driver 实现 | timeout, readTimeout, interpolateParams |
❌(仅能通过 DSN 字符串静态分析) |
graph TD
A[sql.Open] --> B[Driver.Open]
B --> C[驱动专属 DSN 解析]
C --> D[创建 *sql.Conn]
D --> E[配置参数进入闭包/私有字段]
E --> F[对外不可观测]
3.3 官方示例代码中“最小可行配置”范式对生产环境的误导性迁移
官方示例常以 --single-transaction --skip-lock-tables 启动 MySQL dump,追求极致轻量:
mysqldump -u root -p --single-transaction --skip-lock-tables \
--routines --triggers mydb > backup.sql
⚠️ 此配置在高并发写入场景下会引发一致性风险:
--skip-lock-tables跳过表级锁,但--single-transaction仅保障 InnoDB 表的 MVCC 一致性,MyISAM 表仍可能产生逻辑不一致。
数据同步机制失配
- 生产库含混合引擎(InnoDB + MyISAM)
- 示例未声明
--lock-all-tables或引擎隔离策略 - 备份期间 MyISAM 表被更新 → 恢复后数据断裂
关键参数对比
| 参数 | 示例用法 | 生产建议 | 风险等级 |
|---|---|---|---|
--skip-lock-tables |
✅ 默认启用 | ❌ 禁用或替换为 --lock-all-tables |
高 |
--single-transaction |
✅ 启用 | ✅ 保留(仅限纯 InnoDB) | 中(混合引擎时失效) |
graph TD
A[官方示例] --> B[最小配置]
B --> C{引擎类型}
C -->|纯 InnoDB| D[一致性可保障]
C -->|混合引擎| E[MyISAM 无事务保护]
E --> F[备份逻辑损坏]
第四章:企业级数据库连接池配置工程化落地指南
4.1 基于QPS/TP99/连接延迟三维指标的动态配置推导模型
传统静态线程池或超时配置难以适配流量突变与长尾延迟共存场景。本模型将 QPS(每秒查询数)、TP99(99%请求响应时间)与连接建立延迟三者耦合建模,实现服务端资源配置的实时反演。
核心推导逻辑
采用加权几何归一化函数:
def calc_dynamic_pool_size(qps, tp99_ms, conn_delay_ms, base_size=8):
# 归一化因子:QPS拉高并发需求,TP99与连接延迟越长,需更多冗余线程防阻塞
qps_weight = max(1.0, qps / 100) # 每100 QPS为基准单位
latency_penalty = (tp99_ms + conn_delay_ms) / 200.0 # 200ms为容忍阈值
return int(base_size * qps_weight * (1.0 + min(2.0, latency_penalty)))
逻辑分析:
qps_weight线性放大基础并发承载力;latency_penalty将双延迟叠加后映射为冗余系数,上限封顶2.0防止过配;最终结果向下取整保障资源可控性。
配置影响因子对照表
| 指标 | 变化趋势 | 对推荐线程数影响 | 权重贡献 |
|---|---|---|---|
| QPS | ↑ 50% | ↑ 50% | 线性 |
| TP99 | ↑ 100ms | ↑ ~12%(按公式) | 非线性 |
| 连接延迟 | ↑ 50ms | ↑ ~6% | 非线性 |
决策流程
graph TD
A[实时采集QPS/TP99/conn_delay] --> B{是否超阈值?}
B -->|是| C[触发归一化计算]
B -->|否| D[维持当前配置]
C --> E[输出新线程数/超时值]
E --> F[热更新至Netty EventLoopGroup]
4.2 Kubernetes环境下容器资源约束与连接池参数的映射校准
容器内存限制(memory.limit)直接影响JVM堆外缓冲区与数据库连接池的可用空间,需建立量化映射关系。
连接池参数与cgroup内存的协同约束
以HikariCP为例,其maxPoolSize不应超过容器内存限制的15%(预留JVM元空间、Netty缓冲等开销):
# deployment.yaml 片段
resources:
limits:
memory: "2Gi"
cpu: "1000m"
requests:
memory: "1.5Gi"
逻辑分析:2Gi内存限制下,建议
maxPoolSize ≤ 30(按每连接平均占用64MB估算)。若设为50,易触发OOMKilled——因连接池+JVM堆+内核页缓存总和超限。
推荐映射对照表
| 容器内存限制 | 推荐 maxPoolSize | JVM -Xmx 建议 |
|---|---|---|
| 1Gi | 12 | 512m |
| 2Gi | 30 | 1g |
| 4Gi | 60 | 2g |
自动化校准流程
graph TD
A[读取Pod cgroup memory.max] --> B[解析为GiB整数]
B --> C[查表获取推荐maxPoolSize]
C --> D[注入环境变量 HIKARI_MAX_POOL_SIZE]
4.3 多租户SaaS架构中连接池隔离与配额控制的中间件实现
在高并发多租户场景下,数据库连接资源需按租户维度硬隔离并动态配额,避免“嘈杂邻居”效应。
核心设计原则
- 租户ID注入:通过ThreadLocal透传
tenant_id至数据源路由层 - 分层配额:基础连接数(5)+弹性上限(15),基于SLA等级动态调整
- 故障熔断:单租户连接耗尽时自动降级为只读,不阻塞全局池
连接池路由中间件(Java Spring Boot)
public class TenantAwareHikariDataSource extends HikariDataSource {
private final Map<String, HikariDataSource> tenantPools = new ConcurrentHashMap<>();
public Connection getConnection() throws SQLException {
String tenantId = TenantContext.getCurrentTenant(); // 从MDC或JWT解析
return tenantPools.computeIfAbsent(tenantId, this::buildPool).getConnection();
}
private HikariDataSource buildPool(String tenantId) {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:postgresql://db/" + tenantId);
config.setMaximumPoolSize(getTenantMaxPoolSize(tenantId)); // 查配额中心
return new HikariDataSource(config);
}
}
逻辑分析:该中间件以租户ID为键构建独立HikariCP实例,
getTenantMaxPoolSize()从分布式配置中心(如Apollo)实时拉取配额策略;computeIfAbsent确保首次访问时初始化,后续复用,避免重复创建开销。连接获取路径完全透明,业务代码无感知。
配额策略对照表
| 租户等级 | 基础连接数 | 弹性上限 | 超限行为 |
|---|---|---|---|
| 免费版 | 3 | 5 | 拒绝新连接,返回429 |
| 专业版 | 8 | 12 | 自动扩容至12 |
| 企业版 | 20 | 30 | 启用连接复用代理 |
运行时资源调度流程
graph TD
A[HTTP请求] --> B{解析Tenant-ID}
B --> C[查询配额中心]
C --> D{连接池已存在?}
D -->|否| E[按策略创建新池]
D -->|是| F[获取连接]
F --> G{是否超配额?}
G -->|是| H[触发熔断/降级]
G -->|否| I[返回Connection]
4.4 Prometheus+Grafana连接池健康度可观测性体系构建
核心指标采集设计
需暴露连接池关键指标:pool_active_connections、pool_idle_connections、pool_wait_count_total、pool_wait_duration_seconds_sum。Spring Boot Actuator + Micrometer 自动注册 HikariCP 指标。
Prometheus 配置示例
# prometheus.yml 片段
scrape_configs:
- job_name: 'spring-boot-app'
metrics_path: '/actuator/prometheus'
static_configs:
- targets: ['app-service:8080']
该配置启用对 Spring Boot 应用
/actuator/prometheus端点的周期拉取;metrics_path必须与management.endpoints.web.path-mapping.prometheus=/actuator/prometheus保持一致,否则指标无法采集。
Grafana 面板关键视图
| 面板项 | 说明 |
|---|---|
| 活跃连接率曲线 | rate(pool_active_connections[5m]) / (pool_max_size) |
| 等待超时占比 | sum(rate(pool_timeout_count_total[5m])) by (app) / sum(rate(pool_wait_count_total[5m])) by (app) |
健康度判定逻辑(Mermaid)
graph TD
A[采集指标] --> B{活跃连接率 > 95%?}
B -->|是| C[触发等待队列告警]
B -->|否| D{等待耗时 P95 > 200ms?}
D -->|是| E[标记连接池承压]
D -->|否| F[健康]
第五章:面向云原生时代的连接池配置演进趋势
动态扩缩容驱动的连接数自适应策略
在 Kubernetes 环境中,某电商中台服务采用 Spring Boot 3.2 + HikariCP 5.0,通过 Prometheus 指标(hikari_connections_active, jvm_memory_used_bytes)触发 KEDA ScaledObject 自动调整 Pod 副本数。同时,连接池最大连接数 maximumPoolSize 不再硬编码,而是由 ConfigMap 中的 pool.max-per-pod 字段注入,并结合 replicas * pool.max-per-pod 计算全局连接上限。实际压测显示:当 QPS 从 800 突增至 3200 时,连接池能在 42 秒内完成从 20→160 的阶梯式扩容,且无连接拒绝(HikariPool-1 - Connection is not available 日志归零)。
服务网格透明代理下的连接复用优化
Istio 1.21 默认启用 mTLS 后,应用层到数据库的 TLS 握手耗时上升 37ms。团队将 MySQL 连接字符串中的 useSSL=true 改为 useSSL=false&requireSSL=true,并配合 Istio Sidecar 的 traffic.sidecar.istio.io/includeOutboundIPRanges: "10.96.0.0/12,192.168.0.0/16" 白名单,使数据库流量绕过 Envoy TLS 终止,仅依赖 mTLS 保障链路安全。实测单次查询 P95 延迟从 112ms 降至 68ms。
多集群场景下的跨地域连接池隔离
某跨境支付平台部署于北京、新加坡、法兰克福三地集群,MySQL 主库位于北京,只读从库按地域就近部署。通过 Spring Cloud LoadBalancer 集成自定义 ZonePreferenceServiceInstanceListSupplier,结合 spring.cloud.loadbalancer.zone 属性,使每个地域的连接池仅初始化对应区域的从库地址。配置片段如下:
spring:
datasource:
hikari:
jdbc-url: jdbc:mysql://${DB_HOST}:${DB_PORT}/payment?serverTimezone=Asia/Shanghai
cloud:
loadbalancer:
zone: ${REGION:beijing}
故障熔断与连接池健康快照联动
使用 Resilience4j 的 CircuitBreaker 与 HikariCP 的 healthCheckSource 联动:当 CircuitBreaker 进入 OPEN 状态时,通过 JMX 调用 HikariPool#softEvictConnections() 清空无效连接,并触发 HikariConfig#setConnectionInitSql("SELECT 1") 重建健康检查逻辑。该机制在某次 RDS 主备切换期间,将连接恢复时间从 90 秒压缩至 11 秒。
| 场景 | 传统静态配置 | 云原生动态配置 | 性能提升 |
|---|---|---|---|
| 流量突增响应延迟 | 180s(需人工扩Pod) | 42s(KEDA自动扩缩) | 3.3× |
| TLS握手开销 | 37ms | 0ms(Sidecar代理卸载) | 100%消除 |
| 跨地域读请求RTT | 210ms(全走北京) | 48ms(本地从库) | 4.4× |
flowchart LR
A[Prometheus指标采集] --> B{QPS > 阈值?}
B -->|是| C[KEDA触发HPA扩容]
B -->|否| D[维持当前PoolSize]
C --> E[ConfigMap更新max-per-pod]
E --> F[HikariCP reload配置]
F --> G[新Pod启动时加载新连接数]
容器生命周期钩子与连接优雅释放
在 Deployment 的 preStop 钩子中执行 curl -X POST http://localhost:8080/actuator/refresh?pool=primary,触发 Spring Boot Actuator 的 HikariDataSource 刷新,确保 Pod 终止前所有连接被 softEvictConnections() 标记为可回收。配合 terminationGracePeriodSeconds: 60,避免因连接未释放导致 RDS 连接数超限告警。
基于 eBPF 的连接池行为可观测性增强
通过 BCC 工具 tcpconnect 和自定义 eBPF 程序捕获 JVM 进程的 connect() 系统调用,实时统计每秒新建连接数、目标 IP 分布及 TCP 建连耗时。将数据推送至 Grafana,叠加 HikariCP 的 activeConnections 指标,可精准识别连接泄漏模式——例如某次上线后发现 connect() 调用频次稳定在 1200/s,但 activeConnections 持续爬升,最终定位到 MyBatis @Select 方法未配置 fetchSize 导致游标未及时关闭。
Serverless 数据库连接管理实践
阿里云 PolarDB Serverless 实例启用“连接池模式”后,应用侧将 HikariCP 的 connectionTimeout 从 30s 调整为 10s,并设置 leakDetectionThreshold: 60000。当函数实例冷启动时,首次连接由 PolarDB Proxy 自动复用已有连接,实测 100 并发下连接建立成功率从 82% 提升至 99.7%,且无需维护连接池实例生命周期。
