數(shù)據(jù)庫連接池連接數(shù)10?100?1000?的困惑

1. 開篇閑談

閑暇之余看到一篇博文: 如何正確設(shè)置數(shù)據(jù)庫連接池的大小?我的天,原來之前都設(shè)置錯(cuò)了!

基本上來說,大部分項(xiàng)目都需要跟數(shù)據(jù)庫做交互,那么,數(shù)據(jù)庫連接池的大小設(shè)置成多大合適呢?
一些開發(fā)老鳥可能還會告訴你:沒關(guān)系,盡量設(shè)置的大些,比如設(shè)置成 200,這樣數(shù)據(jù)庫性能會高些,吞吐量也會大些!

的確如此,每次看到application.properties配置數(shù)據(jù)庫連接池的時(shí)候,總是想要不再搞大一點(diǎn)連接數(shù)。

#整它個(gè)2000試試
spring.datasource.hikari.minimum-idle=2000
spring.datasource.hikari.maximum-pool-size=2000

然后發(fā)現(xiàn),業(yè)務(wù)頁面點(diǎn)點(diǎn) 也沒啥區(qū)別,看看數(shù)據(jù)庫的連接數(shù)的確上去了,但是發(fā)現(xiàn)大部分都在sleep:

1.png

2. 試驗(yàn)

探索真理的捷徑莫過于動(dòng)手,搞個(gè)demo,ab壓測下看看,到底線程池連接數(shù)對請求響應(yīng)會不會產(chǎn)生大影響。

2.1. 環(huán)境準(zhǔn)備

一般mysql最大連接數(shù)比較少,提前設(shè)置下最大連接數(shù)

SET GLOBAL max_connections=2100

構(gòu)建項(xiàng)目,新建Spring-boot web項(xiàng)目,添加如下Pom依賴

      <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-undertow</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.dropwizard.metrics</groupId>
            <artifactId>metrics-core</artifactId>
            <version>4.1.0</version>
        </dependency>

排除tomcat內(nèi)嵌服務(wù)器,改用undertow,避免內(nèi)嵌服務(wù)器性能影響。
配置數(shù)據(jù)庫和連接池,采用牛逼的一匹的號稱世界最快連接池:hikariCP

# 數(shù)據(jù)庫連接配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/demo?serverTimezone=GMT%2b8&useUnicode=true&characterEncoding=utf8&useSSL=true
spring.datasource.username=root
spring.datasource.password=123456

# Hikari 連接池配置
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.maximum-pool-size=5
spring.datasource.hikari.auto-commit=true
spring.datasource.hikari.idle-timeout=30000
spring.datasource.hikari.pool-name=DatebookHikariCP
spring.datasource.hikari.max-lifetime=1800000
spring.datasource.hikari.connection-timeout=3000000
spring.datasource.hikari.connection-test-query=SELECT 1

當(dāng)然提前得建一個(gè)庫demo,和一張表up_user,且手工添加5000行數(shù)據(jù)

CREATE TABLE `up_user` (
            `id` INT (11) PRIMARY KEY NOT NULL AUTO_INCREMENT,
            `code` VARCHAR (36) NOT NULL,
            `name` VARCHAR (60) NOT NULL,
            `card_no` VARCHAR (100),
            `email` VARCHAR (135),
            `phone` VARCHAR (135),
             `birthday` DATETIME ,
             `gender` SMALLINT (6) DEFAULT 0,
             `create_user` VARCHAR (36),
             `create_time` DATETIME ,
             `update_user` VARCHAR (36),
             `update_time` DATETIME ,
             `status` INT (2) DEFAULT 0,
             `is_deleted` INT (2) DEFAULT 0,
             `remark` VARCHAR(500) NULL
      );

2.2. 測試服務(wù)編寫

  • 內(nèi)置指標(biāo)配置類
@Configuration
public class MetricsConfig {
    @Bean
    public MetricRegistry metrics() {
        return new MetricRegistry();
    }

    @Bean
    public Meter requestMeter(MetricRegistry metrics) {
        return metrics.meter("request");
    }

    @Bean
    public Timer responses(MetricRegistry metrics) {
        return metrics.timer("executeTime");
    }


    @Bean
    public ConsoleReporter consoleReporter(MetricRegistry metrics) {
        return ConsoleReporter.forRegistry(metrics)
                .convertRatesTo(TimeUnit.SECONDS)
                .convertDurationsTo(TimeUnit.MILLISECONDS)
                .build();
    }
}

  • 測試服務(wù)
@RestController
public class SqlTestController {

    @Autowired
    private HikariDataSource dataSource;

    @Autowired
    private Timer responses;

    @RequestMapping("/one")
    public String getSomeUser() throws SQLException, InterruptedException {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        final Timer.Context context = responses.time();
        String sql = "select * from up_user where id>"+new Random().nextInt(5000)+" limit 0,100";
        try {
            connection = dataSource.getConnection();
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.executeQuery();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if(connection !=null){
                connection.close();
            }
            if(preparedStatement !=null) {
                preparedStatement.close();
            }
            TimeUnit.MILLISECONDS.sleep(200);
            context.stop();
        }

        return "ok";
    }
}
  • 啟動(dòng)類
@SpringBootApplication
public class MetricsDemoApplication {

    public static void main(String[] args) {
        ApplicationContext ctx = SpringApplication.run(MetricsDemoApplication.class, args);
        ConsoleReporter reporter = ctx.getBean(ConsoleReporter.class);
        reporter.start(1, TimeUnit.SECONDS);
    }

}

  • 工具apache bench (安裝略)

搞一臺其他機(jī)器,進(jìn)行壓測,200線程并發(fā),一共訪問10000次
ab -c 200 -n 10000 http://10.30.21.8:9527/one?id=3

  • 測試方案
    線程池連接數(shù)5、100、1000

  • 服務(wù)器機(jī)器 2核四線程

2.3. 測試結(jié)果

5個(gè)連接數(shù):

5.png

100個(gè)連接數(shù):

100.png

1000個(gè)連接數(shù):

1000.png

3. 總結(jié)

對比發(fā)現(xiàn)三種情況下每秒請求個(gè)數(shù),每個(gè)請求平均處理時(shí)間,整體1000個(gè)請求響應(yīng)時(shí)間,其實(shí)3個(gè)差別不大。細(xì)微的變化:

  • 3次測試請求的方差(stddev),0.98 ->2.32->3.08
  • 3次試驗(yàn)80%請求的響應(yīng)時(shí)間,1299->1327->1357

由此得出:連接池的確不是越大越好,某些情況下差別不大,那么為什么這次實(shí)驗(yàn)性能差這么不明顯呢,估計(jì)影響有兩個(gè):

  • 筆者電腦為2核4線程PC,cpu對連接的處理切換,在多連接情況下不會太明顯,沒有8核16核那么大
  • 壓測量不夠大,如果增大估計(jì)效果會更明顯
    所以,一般的服務(wù)器配置,其實(shí)10~20個(gè)連接數(shù),其實(shí)就可以了,沒有必要浪費(fèi)了MySql的連接資源。

4. 后記

既然很多實(shí)戰(zhàn)經(jīng)驗(yàn)豐富的項(xiàng)目都推薦 連接池連接數(shù):2*CUP+硬盤數(shù),那么筆者心血來潮試了一把,一個(gè)連接數(shù)結(jié)果會怎么樣:

1.png

方差(stddev):1.27 ,80%請求的響應(yīng)時(shí)間:1318
顯然和5個(gè)連接數(shù)比,的確性能有所下降。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容