连接池 - 数据库短连接&长连接&连接池 - 学习/实践

1.应用场景

主要用于学习数据库连接,包括短连接和长连接,以及连接池的本质,以及如何实现,优缺点和应用场景。

2.学习/操作

1.文档阅读

池化技术 - 学习/实践_穿素白衫的少年的博客-CSDN博客

https://blog.csdn.net/william_n/article/details/124065450

php和mysql连接方式(短 长 池) - Thomas_188 - 博客园

MySQL 数据库连接原理和性能优化 - 学院君

【性能优化】MySQL 数据库连接原理和性能优化 - 学习/实践

为什么需要数据库连接池 - 知乎

2.整理输出

环境

Mac + LNMP「Docker」

这里以PHP连接MySQL为例,至于其他语言连接各种数据库作为参考。

一个「Nginx + PHP-FPM」PHP Work进程同一时刻只能处理一个请求,当完成一个请求了,才能处理下一次的请求。

2.1 短链接

执行到PHP关闭MySQL连接的代码时,就断开,否则在处理本次请求结束的时候,释放MySQL连接「即PHP脚本执行完毕,也就是脚本的生命周期结束的时,会释放所有的资源」。

Code

<?php

$dbhost = '172.18.0.4';
$dbdatabase = 'app.test';
$username = 'root';
$userpass = 'root';

$dsn = 'mysql:host=' . $dbhost . ';dbname=' . $dbdatabase . ';';
$dbh = new PDO($dsn, $username, $userpass);
$stmt = $dbh->query('SELECT * FROM test');
$row = $stmt->fetch();

sleep(5); // 模拟复杂的业务操作,这里主要是为了避免压力测试很快结束,为了后面的数据查看「线程/连接」

$dbh = null; // release resource 即 close conn

var_dump($row);

并发请求: ab -c 100 -n 100 http://app.test:8080/pdo.php

结果:

查看MySQL连接数: show full processlist
这里我截了PHP Work进程,和两个时刻的MySQL连接情况的图片

一共五个PHP-FPM进程,MySQL有五个连接,连接的ID,会递增,说明每次请求完成,MySQL连接消失,新的请求,PHP会重新连接MySQL。

补充:个人实践

ab -c 100 -n 100 http://app.test:8080/pdo.php
ps aux | grep php-fpm
ps aux | grep nginx
show full processlist;

压力测试 & Nginx & PHP_FPM

数据库的连接列表 「这里使用的Navicat GUI工具

由上可知,「个人分析,仍需确认证实

1. 默认PHP_FPM配置最大的进程池大小为5,也就是同一时间最多的PHP_FPM进程数量为5个「pm.max_children」,而且在执行测试之前「即刚启动时」,PHP_FPM进程数量为2「start_servers」 同时可以看到下图,在测试之后一段时间内,PHP_FPM进程数量变为了3「pm.max_spare_servers」

查看PHP-FPM的配置文件,内容如下,可以证实:

2. 尽管PHP_FPM进程数为5时,Nginx的进程数只有两个,master 和 work进程,各一个。

说明,Nginx的work进程跟PHP_FPM进程数量对应关系并不是要一对一关系,至于具体多少,谁多谁少,需要根据具体场景进行分析,确认.「这里则是前者对后者数量比1:5

通过查看Nginx配置,以及查看nginx进程数

可以看到Nginx的worker进程变为了4个,而且即便没有请求时,也依然是4个worker进程

3. 在请求并发很大时,PHP_FPM与MySQL似乎保持了一对一的连接关系,

但是未必一定是这样,这需要验证确认。

同时,可以看到MySQL的连接ID时刻发生变化,说明当PHP_FPM采用短连接时,它与数据库之间的连接确实在查询或者写入操作完毕后,开始断开销毁。

当压测过后,再次执行 show full processlist;

发现,数据库的连接数变少了,只保留最先的几个连接,分析过后就知道,仅仅保持了两个空闲连接,注意,由PHP_FPM连接创建的连接都被销毁了。

原先的命令行界面「终端客户端

新开的命令行界面「终端客户端

再新开一个命令列界面 

ID为35的连接被使用

继续开两个命令列界面 

可以发现,不同的命令列界面,占用的是不同的连接,MySQL应是默认预留两个连接,供命令行连接

如果有多余的命令行连接,则继续创建新的连接即可。

同时看到,尽管我已经关闭了数据库的连接,重新打开后,发现,连接数没有销毁「初步判断这应该是Navicat关掉连接,并没有销毁连接的原因,TBD」 

2.2 长连接

当我们创建的数据连接为PDO持久化连接时,该连接在使用完毕或者PHP脚本结束后并不会被关闭,而是被PHP缓存起来。

当另一个使用相同凭证(主机、端口、数据库名、用户名、密码等信息完全一致)的PHP脚本请求建立连接时,PHP将直接返回之前被缓存起来的连接,从而达到连接重用。持久连接缓存可以避免每次访问数据库都要建立一个新连接的开销。

Code

<?php

$dbhost = '172.18.0.4';
$dbdatabase = 'app.test';
$username = 'root';
$userpass = 'root';

$dsn = 'mysql:host=' . $dbhost . ';dbname=' . $dbdatabase . ';';
$dbh = new PDO($dsn, $username, $userpass, [PDO::ATTR_PERSISTENT => true]);
$stmt = $dbh->query('SELECT * FROM test');
$row = $stmt->fetch();

sleep(5);

var_dump($row);
 

并发请求: ab -c 100 -n 100 http://app.test:8080/pdo.php

结果:

查看mysql连接数: show full processlist
这里我截了PHP Work进程,和两个时刻的MySQL连接情况的图片

个人实践

压力测试 & Nginx & PHP_FPM

数据库的连接列表

一共五个PHP-FPM进程,MySQL有五个连接,连接的ID不会变动,说明每次请求完成,MySQL连接不会消失,在每个PHP-FPM进程中处理新的请求,PHP会重用以前MySQL连接, 请求都结束了,每个PHP-FPM进程和MySQL的连接还是不会断的,一直保持着。

个人补充:

1. 除了在并发请求处理过程中,数据库的连接不会变,即销毁,复用连接,其他跟短连接保持一致。

2. 同时可以看到,一旦并发结束后,没有请求时,持久连接也是销毁一定数量的连接,并非全部持久化。

3. 连接ID即使已有的连接关闭销毁,新创建的连接也是保持递增,推测是为了考虑性能和简单,不需要检索之前的连接是否有销毁,只要记住最后一个ID即可。

4. PHP_FPM的进程数也是在测试完毕之后,也下降到了3,跟短链接是一致的。

问题:

PHP中PDO关闭连接的问题 - 知乎

官方说明

要想关闭连接,需要销毁对象以确保所有剩余到它的引用都被删除,可以赋一个 NULL 值给对象变量。如果不明确地这么做,PHP 在脚本结束时会自动关闭连接。

$pdo = new PDO('mysql:host=127.0.0.1;port=3306;dbname=blog_test', 'root', '');
$pdo = null;

官方文档中说得很明白,那就是给 PDO 对象赋值为 NULL 即可。但是事情真的有那么简单吗?

实际测试

我们来这样进行一下测试,正常情况下,我们打开数据库连接后都不会直接就关闭,而是要进行一些操作。

$pdo = new PDO('mysql:host=127.0.0.1;port=3306;dbname=blog_test', 'root', '');

$stmt = $pdo->prepare('SELECT * FROM zyblog_test_user');
$stmt->execute();

$pdo = null;
sleep(60);

 

运行上述代码后,我们在数据库使用 show full processlist;

查看连接进程,会发现当前的连接并没有马上关闭,而是等到 60 秒之后,也就是页面执行完成之后才会关闭。似乎 $pdo = null; 这句并没有执行成功。

其实,在官方文档中已经说明了这个情况,只是大家可能不太会注意。

需要销毁对象以确保所有剩余到它的引用都被删除」。

在上面的代码中,$stmt 预编译 SQL 语句的功能调用的是 $pdo 对象中的方法,它们之间产生了引用依赖的关系,这样的情况下,直接给 $pdo = null; 是没有效果的,我们需要将 $stmt 也赋值为 null 。

$pdo = new PDO('mysql:host=127.0.0.1;port=3306;dbname=blog_test', 'root', '');

$stmt = $pdo->prepare('SELECT * FROM zyblog_test_user');
$stmt->execute();

$stmt = null;
$pdo = null;
sleep(60);

如果手动关闭连接,能否关闭长连接?

实践如下:

继续执行脚本进行压力测试,代码方面添加上了如下一行

$dbh = null; // 根据官方文档说明,这样是关闭不了连接的 

整体代码如下

<?php

$dbhost = '172.18.0.4';
$dbdatabase = 'app.test';
$username = 'root';
$userpass = 'root';

$dsn = 'mysql:host=' . $dbhost . ';dbname=' . $dbdatabase . ';';
$dbh = new PDO($dsn, $username, $userpass, [PDO::ATTR_PERSISTENT => true]);
$stmt = $dbh->query('SELECT * FROM test');
$row = $stmt->fetch();

$dbh = null; // close connect

sleep(5);

var_dump($row);
 

截图

可以发现在请求处理过程中,连接依然是没有保持改变的「ID没有递增」,说明这里并没有关闭连接。

重新修改代码

<?php

$dbhost = '172.18.0.4';
$dbdatabase = 'app.test';
$username = 'root';
$userpass = 'root';

$dsn = 'mysql:host=' . $dbhost . ';dbname=' . $dbdatabase . ';';
$dbh = new PDO($dsn, $username, $userpass, [PDO::ATTR_PERSISTENT => true]);
$stmt = $dbh->query('SELECT * FROM test');
$row = $stmt->fetch();

// close connect

$dbh = null;

$stmt = null;

sleep(5);

var_dump($row);

测试截图

可以知道,即便使用了关闭连接,在并发过程中,长连接也是没办法关闭的「推测,官方认为,如果是这样,直接用短连接就好了,也就是没有人会这样用

临时总结

方式优点缺点
短连及时的释放资源每次得重新建立连接,开销大,耗时大
长连不用现场建立连接,耗时小连接一直保持着,耗费服务器资源

10台PHP机器,每台机器上面有50个PHP-FPM进程,长连接的话, MySQL要有500个线程和500个PHP-FPM进程保持连接,可想MySQL的上下文切换。「MySQL默认支持的最大连接数为151

额外信息:

MySQL要有500个线程,是多还是少,这应该结合机器资源,并且最好测试过才知道,当然可以有参考指标列表

show global variables like 'max_conn%';

插入问题

PHP中PDO扩展,与ODBC还是JDBC或者其他的数据连接层有什么区别?

PHP - PDO - 学习/实践_穿素白衫的中少年的博客-CSDN博客

2.3 连接池

https://blog.csdn.net/william_n/article/details/127287788

补充:

PHP是否支持数据库连接池?什么时候使用数据库连接池?或者说连接迟什么场景使用?

PHP本身不支持,但是PHP可以借助其他工具/扩展实现连接池「缓存连接池或者数据库连接池

其他问题,均参见:

PHP - 连接池相关问题 - 学习/实践_穿素白衫的中少年的博客-CSDN博客_php连接池

后续补充

...

3.问题/补充

1. 关于连接超时

推荐阅读 -- 下面文档很详细

MySQL 的连接时长控制--interactive_timeout和wait_timeout_翔云123456的博客-CSDN博客

4031 - The client was disconnected by the server because of inactivity. See wait_timeout and interactive_timeout for configuring this behavior.

默认是8小时

interactive_timeout

wait_timeout

所有的超时时间列表

文本形式

mysql> show global variables like  'interactive_timeout%';
+---------------------+-------+
| Variable_name       | Value |
+---------------------+-------+
| interactive_timeout | 28800 |
+---------------------+-------+
1 row in set (0.14 sec)
 
mysql> show global variables like  'wait_timeout%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| wait_timeout  | 28800 |
+---------------+-------+
1 row in set (0.01 sec)
 
mysql> show global variables like  '%timeout%';
+-----------------------------------+----------+
| Variable_name                     | Value    |
+-----------------------------------+----------+
| connect_timeout                   | 10       |
| delayed_insert_timeout            | 300      |
| have_statement_timeout            | YES      |
| innodb_flush_log_at_timeout       | 1        |
| innodb_lock_wait_timeout          | 50       |
| innodb_rollback_on_timeout        | OFF      |
| interactive_timeout               | 28800    |
| lock_wait_timeout                 | 31536000 |
| mysqlx_connect_timeout            | 30       |
| mysqlx_idle_worker_thread_timeout | 60       |
| mysqlx_interactive_timeout        | 28800    |
| mysqlx_port_open_timeout          | 0        |
| mysqlx_read_timeout               | 30       |
| mysqlx_wait_timeout               | 28800    |
| mysqlx_write_timeout              | 60       |
| net_read_timeout                  | 30       |
| net_write_timeout                 | 60       |
| replica_net_timeout               | 60       |
| rpl_stop_replica_timeout          | 31536000 |
| rpl_stop_slave_timeout            | 31536000 |
| slave_net_timeout                 | 60       |
| ssl_session_cache_timeout         | 300      |
| wait_timeout                      | 28800    |
+-----------------------------------+----------+
23 rows in set (0.01 sec)
 
mysql> 

4.参考

参见文档阅读列表

后续补充

...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值