← 返回首页
🏎️

连接池架构:HikariCP与Druid

📂 architecture ⏱ 3 min 544 words

连接池架构:HikariCP与Druid

HikariCP配置与调优

HikariCP是高性能的JDBC连接池,以轻量和高速著称。

// HikariCP配置
@Configuration
public class HikariCPConfig {
    
    @Bean
    @ConfigurationProperties("spring.datasource.hikari")
    public HikariDataSource dataSource() {
        HikariConfig config = new HikariConfig();
        
        // 基础配置
        config.setPoolName("MyAppPool");
        config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
        config.setUsername("user");
        config.setPassword("password");
        
        // 连接池大小
        config.setMinimumIdle(10);
        config.setMaximumPoolSize(100);
        
        // 连接超时
        config.setConnectionTimeout(30000); // 30秒
        config.setIdleTimeout(600000); // 10分钟
        config.setMaxLifetime(1800000); // 30分钟
        
        // 连接验证
        config.setConnectionTestQuery("SELECT 1");
        config.setValidationTimeout(5000);
        
        //泄漏检测
        config.setLeakDetectionThreshold(60000); // 60秒
        
        // 数据源属性
        config.addDataSourceProperty("cachePrepStmts", "true");
        config.addDataSourceProperty("prepStmtCacheSize", "250");
        config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
        config.addDataSourceProperty("useServerPrepStmts", "true");
        config.addDataSourceProperty("useLocalSessionState", "true");
        config.addDataSourceProperty("rewriteBatchedStatements", "true");
        config.addDataSourceProperty("cacheResultSetMetadata", "true");
        config.addDataSourceProperty("cacheServerConfiguration", "true");
        config.addDataSourceProperty("elideSetAutoCommits", "true");
        config.addDataSourceProperty("maintainTimeStats", "false");
        
        return new HikariDataSource(config);
    }
}

// HikariCP监控
@Component
public class HikariCPMonitor {
    
    private final HikariDataSource dataSource;
    private final MetricsExporter metrics;
    
    @Scheduled(fixedRate = 5000)
    public void monitor() {
        HikariPoolMXBean poolMXBean = dataSource.getHikariPoolMXBean();
        
        if (poolMXBean != null) {
            // 连接池状态
            int activeConnections = poolMXBean.getActiveConnections();
            int idleConnections = poolMXBean.getIdleConnections();
            int totalConnections = poolMXBean.getTotalConnections();
            int threadsAwaiting = poolMXBean.getThreadsAwaitingConnection();
            
            // 导出指标
            metrics.gauge("hikari.active", activeConnections);
            metrics.gauge("hikari.idle", idleConnections);
            metrics.gauge("hikari.total", totalConnections);
            metrics.gauge("hikari.waiting", threadsAwaiting);
            
            // 计算使用率
            double utilization = (double) activeConnections / 
                dataSource.getMaximumPoolSize() * 100;
            metrics.gauge("hikari.utilization", utilization);
            
            // 告警检测
            checkAlerts(activeConnections, idleConnections, threadsAwaiting, utilization);
        }
    }
    
    private void checkAlerts(int active, int idle, int waiting, double utilization) {
        if (waiting > 0) {
            alertService.send(new Alert(
                "连接池等待",
                "有线程在等待获取连接,等待数: " + waiting,
                AlertLevel.WARNING
            ));
        }
        
        if (utilization > 90) {
            alertService.send(new Alert(
                "连接池使用率过高",
                "当前使用率: " + utilization + "%",
                AlertLevel.CRITICAL
            ));
        }
        
        if (idle == 0 && active == dataSource.getMaximumPoolSize()) {
            alertService.send(new Alert(
                "连接池已满",
                "所有连接都在使用中",
                AlertLevel.CRITICAL
            ));
        }
    }
}

Druid配置与监控

Druid是阿里巴巴开源的数据库连接池,提供强大的监控和SQL分析功能。

// Druid配置
@Configuration
public class DruidConfig {
    
    @Bean
    @ConfigurationProperties("spring.datasource.druid")
    public DataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        
        // 基础配置
        dataSource.setUrl("jdbc:mysql://localhost:3306/mydb");
        dataSource.setUsername("user");
        dataSource.setPassword("password");
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        
        // 连接池配置
        dataSource.setInitialSize(10);
        dataSource.setMinIdle(10);
        dataSource.setMaxActive(100);
        dataSource.setMaxWait(60000);
        
        // 连接检测
        dataSource.setValidationQuery("SELECT 1");
        dataSource.setTestOnBorrow(true);
        dataSource.setTestWhileIdle(true);
        dataSource.setTimeBetweenEvictionRunsMillis(60000);
        dataSource.setMinEvictableIdleTimeMillis(300000);
        
        // PSCache
        dataSource.setPoolPreparedStatements(true);
        dataSource.setMaxPoolPreparedStatementPerConnectionSize(20);
        
        // 过滤器
        dataSource.setFilters("stat,wall");
        
        // 监控配置
        StatViewServlet servlet = new StatViewServlet();
        servlet.setLoginUsername("admin");
        servlet.setLoginPassword("admin");
        
        return dataSource;
    }
}

// Druid监控
@Component
public class DruidMonitor {
    
    private final DruidDataSource dataSource;
    
    public DruidStats getStats() {
        return DruidStats.builder()
            .activeCount(dataSource.getActiveCount())
            .PoolingCount(dataSource.getPoolingCount())
            .PoolingPeak(dataSource.getPoolingPeakCount())
            .CreateCount(dataSource.getCreateCount())
            .DestroyCount(dataSource.getDestroyCount())
            .WaitThreadCount(dataSource.getWaitThreadCount())
            .LogicConnectCount(dataSource.getConnectErrorCount())
            .Build();
    }
    
    // SQL监控
    public List<SQLStat> getTopSQL(int limit) {
        List<JdbcSqlStat> sqlStats = new ArrayList<>(dataSource.getStatData().values());
        
        return sqlStats.stream()
            .sorted(Comparator.comparingLong(JdbcSqlStat::getExecuteCount).reversed())
            .limit(limit)
            .map(stat -> SQLStat.builder()
                .sql(stat.getSql())
                .executeCount(stat.getExecuteCount())
                .ExecuteTimeMax(stat.getExecuteMax())
                .ExecuteTimeTotal(stat.getExecuteTimeTotal())
                .build())
            .collect(Collectors.toList());
    }
}

连接池调优策略

# 连接池调优配置
connection_pool:
  hikari:
    # 小型应用(<100并发)
    small:
      minimum_idle: 5
      maximum_pool_size: 20
      connection_timeout: 30000
      idle_timeout: 600000
      max_lifetime: 1800000
    
    # 中型应用(100-500并发)
    medium:
      minimum_idle: 10
      maximum_pool_size: 50
      connection_timeout: 30000
      idle_timeout: 600000
      max_lifetime: 1800000
    
    # 大型应用(500-1000并发)
    large:
      minimum_idle: 20
      maximum_pool_size: 100
      connection_timeout: 30000
      idle_timeout: 600000
      max_lifetime: 1800000
    
    # 超大型应用(>1000并发)
    xlarge:
      minimum_idle: 30
      maximum_pool_size: 200
      connection_timeout: 30000
      idle_timeout: 600000
      max_lifetime: 1800000
  
  druid:
    # 监控配置
    monitoring:
      stat_view_servlet:
        enabled: true
        url_pattern: "/druid/*"
        login_username: "admin"
        login_password: "admin"
      
      web_stat_filter:
        enabled: true
        url_pattern: "/*"
        exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
      
      # SQL监控
      stat:
        enabled: true
        slow_sql_millis: 1000
        log_slow_sql: true
        merge_sql: true
      
      # 防火墙
      wall:
        enabled: true
        config:
          select_allow: true
          select_into_allow: false
          delete_allow: true
          update_allow: true
          insert_allow: true
          drop_table_allow: false
          alter_table_allow: false

连接池问题排查

// 连接池问题诊断
@Component
public class ConnectionPoolDiagnostics {
    
    public ConnectionPoolReport diagnose(DataSource dataSource) {
        ConnectionPoolReport report = new ConnectionPoolReport();
        
        if (dataSource instanceof HikariDataSource) {
            diagnoseHikariCP((HikariDataSource) dataSource, report);
        } else if (dataSource instanceof DruidDataSource) {
            diagnoseDruid((DruidDataSource) dataSource, report);
        }
        
        return report;
    }
    
    private void diagnoseHikariCP(HikariDataSource dataSource, 
                                   ConnectionPoolReport report) {
        HikariPoolMXBean poolMXBean = dataSource.getHikariPoolMXBean();
        
        // 检查连接泄漏
        long leakDetectionThreshold = dataSource.getLeakDetectionThreshold();
        if (leakDetectionThreshold > 0) {
            report.addIssue("连接泄漏检测已启用,阈值: " + 
                leakDetectionThreshold + "ms");
        }
        
        // 检查连接等待
        int threadsAwaiting = poolMXBean.getThreadsAwaitingConnection();
        if (threadsAwaiting > 0) {
            report.addIssue("有线程在等待连接,可能需要增加连接池大小");
        }
        
        // 检查连接使用率
        int activeConnections = poolMXBean.getActiveConnections();
        int maxConnections = dataSource.getMaximumPoolSize();
        double utilization = (double) activeConnections / maxConnections * 100;
        
        if (utilization > 80) {
            report.addIssue("连接池使用率过高: " + utilization + "%");
        }
    }
}

连接池配置需要根据应用的实际负载和特点进行调优,HikariCP适合追求性能的场景,Druid适合需要强大监控功能的场景。