持久的数据库连接是指在脚本结束运行时不关闭的连接。当收到一个持久连接的要求时。PHP 将检讨是否已经存在一个(前面已经开启的)相同的持久连接。如果存在,将直策应用这个连接;如果不存在,则建立一个新的连接。所谓“相同”的连接是指用相同的用户名和密码到相同主机的连接。
对 web 做事器的事情和分布负载没有完备理解的读者可能会缺点地理解持久连接的浸染。特殊的,持久连接不会在相同的连接上供应建立“用户会话”的能力,也不供应有效建立事务的能力。实际上,从严格意义上来讲,持久连接不会供应任何非持久连接无法供应的分外功能。
这便是PHP中的连接持久化,不过它也指出了,持久连接不会供应任何非持久连接无法供应的分外功能。这就很让人迷惑了,不是说好了这个方案可以带来性能的提升吗?
连接持久化有什么用?
没错,从上述定义中指出的分外功能来看,持久化连接确实没有带来新的或者更高等的功能,但是它最大的用途正是提升了效率,也便是性能会带来提升。
当Web Server创建到SQL做事器的连接耗费(Overhead)较高(如耗时较久,花费临时内存较多)时,持久连接将更加高效。
也便是说连接耗费高的时候,创建数据库连接的本钱开销也会越大,韶光当然也越长。利用持久化连接之后,使得每个子进程在其生命周期中只做一次连接操作,而非每次在处理一个页面时都要向SQL 做事器提出连接要求。这也便是说,每个子进程将对做事器建立各自独立的持久连接。
例如,如果有 20 个不同的子进程运行某脚本建立了持久的 SQL 做事器持久连接,那么实际上向该 SQL 做事器建立了 20 个不同的持久连接,每个进程霸占一个。
效率比拟话不多说,我们直接通过代码来比拟。首先,我们定义好一个统计函数,用来返回当前的毫秒韶光。其余,我们还要准备好数据的连接参数。
functiongetmicrotime(){list($usec,$sec)=explode("",microtime());return((float)$usec+(float)$sec);}$db=['server'=>'localhost:3306','user'=>'root','password'=>'','database'=>'blog_test',];
接下来,我们先利用普通的 mysqli 进行测试。
$startTime=getmicrotime();for($i=0;$i<1000;$i++){$mysqli=newmysqli($db["server"],$db["user"],$db["password"],$db["database"]);//持久连接$mysqli->close();}echobcsub(getmicrotime(),$startTime,10),PHP_EOL;//6.5814000000
在 1000 次的循环创建数据库的连接过程中,我们花费了6秒多的韶光。接下来我们利用持久化连接的办法进行这 1000 次的数据库连接创建。只须要在 mysqli 的 $host 参数前加上一个 p: 即可。
$startTime=getmicrotime();for($i=0;$i<1000;$i++){$mysqli=newmysqli('p:'.$db["server"],$db["user"],$db["password"],$db["database"]);//持久连接$mysqli->close();}echobcsub(getmicrotime(),$startTime,10),PHP_EOL;//0.0965000000
从 mysqli 的连接上来看,效率提升非常明显。当然,PDO 办法的数据库连接也供应了建立持久连接的属性。
$startTime=getmicrotime();for($i=0;$i<1000;$i++){$pdo=newPDO("mysql:dbname={$db['database']};host={$db['server']}",$db['user'],$db['password']);}echobcsub(getmicrotime(),$startTime,10),PHP_EOL;//6.6171000000$startTime=getmicrotime();for($i=0;$i<1000;$i++){$pdo=newPDO("mysql:dbname={$db['database']};host={$db['server']}",$db['user'],$db['password'],[PDO::ATTR_PERSISTENT=>true]);//持久连接}echobcsub(getmicrotime(),$startTime,10),PHP_EOL;//0.0398000000
PDO 办法连接时,须要给一个 PDO::ATTR_PERSISTENT 参数并设置为 true 。这样就让 PDO 建立的连接也成为了持久化的连接。
把稳既然数据库的持久化连接这么强大,为什么不默认便是这种持久化的连接形式,而须要我们手动增加参数来实现呢?PHP 的开拓者们当然还是有顾虑的。
如果持久连接的子进程数目超过了设定的数据库连接数限定,系统将会产生一些问题。如果数据库的同时连接数限定为 16,而在繁忙会话的情形下,有 17 个线程试图连接,那么有一个线程将无法连接。如果这个时候,在脚本中涌现了使得连接无法关闭的缺点(例如无限循环),则该数据库的 16 个连接将迅速地受到影响。
同时,表锁和事务也有须要把稳的地方。
在持久连接中利用数据表锁时,如果脚本不管什么缘故原由无法开释该数据表锁,其随后利用相同连接的脚本将会被持久的壅塞,使得须要重新启动 httpd 做事或者数据库做事
在利用事务处理时,如果脚本在事务壅塞产生前结束,则该壅塞也会影响到利用相同连接的下一个脚本
以是,在利用表锁及事务的情形下,最好还是不要利用持久化的数据库连接。不过好在持久连接和普通连接是可以在任何时候互换的,我们定义两种连接形式,在不同的情形下利用不同的连接即可办理类似的问题。
总结事物总有两面性,持久连接一方面带来了效率的提升,但另一方面也可能带来一些业务逻辑上的问题,而且这种问题如果在不理解持久连接的机制的情形下会非常难排查。因此,在日常开拓中我们一定要在理解干系功能特性的情形下再选择适宜的办法来完成所须要的功能开拓。
测试代码:
https://github.com/zhangyue0503/dev-blog/blob/master/php/202004/source/PHP%E4%B8%AD%E7%9A%84%E6%95%B0%E6%8D%AE%E5%BA%93%E8%BF%9E%E6%8E%A5%E6%8C%81%E4%B9%85%E5%8C%96.php
参考文档:
https://www.php.net/manual/zh/features.persistent-connections.php