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连接池
后续补充 ... |