乐趣区

关于php:PHP中的数据库连接持久化

数据库的优化是咱们做 web 开发的重中之重,甚至很多状况下其实咱们是在面向数据库编程。当然,用户的所有操作、行为都是以数据的模式保留下来的。在这其中,数据库的连贯创立过程有没有什么能够优化的内容呢?答案当然是有的,Java 等语言中有连接池的设定,而 PHP 在一般开发中并没有连接池这种货色,在牵涉到多线程的状况下往往才会应用连接池的技术,所以 PHP 每次运行都会创立新的连贯,那么这种状况下,咱们如何来优化数据连贯呢?

什么是数据库连贯长久化

咱们先来看下数据库连贯长久化的定义。

长久的数据库连贯是指在脚本完结运行时不敞开的连贯。当收到一个长久连贯的申请时。PHP 将查看是否曾经存在一个(后面曾经开启的)雷同的长久连贯。如果存在,将间接应用这个连贯;如果不存在,则建设一个新的连贯。所谓“雷同”的连贯是指用雷同的用户名和明码到雷同主机的连贯。

对 web 服务器的工作和分布负载没有齐全了解的读者可能会谬误地了解长久连贯的作用。特地的,长久连贯不会在雷同的连贯上提供建设“用户会话”的能力,也不提供无效建设事务的能力。实际上,从严格意义上来讲,长久连贯不会提供任何非长久连贯无奈提供的非凡性能。

这就是 PHP 中的连贯长久化,不过它也指出了,长久连贯不会提供任何非长久连贯无奈提供的非凡性能。这就很让人纳闷了,不是说好了这个计划能够带来性能的晋升吗?

连贯长久化有什么用?

没错,从上述定义中指出的非凡性能来看,长久化连贯的确没有带来新的或者更高级的性能,然而它最大的用途正是晋升了效率,也就是性能会带来晋升。

当 Web Server 创立到 SQL 服务器的连贯消耗 (Overhead) 较高(如耗时较久,耗费长期内存较多)时,长久连贯将更加高效。

也就是说连贯消耗高的时候,创立数据库连贯的老本开销也会越大,工夫当然也越长。应用长久化连贯之后,使得每个子过程在其生命周期中只做一次连贯操作,而非每次在解决一个页面时都要向 SQL 服务器提出连贯申请。这也就是说,每个子过程将对服务器建设各自独立的长久连贯。

例如,如果有 20 个不同的子过程运行某脚本建设了长久的 SQL 服务器长久连贯,那么实际上向该 SQL 服务器建设了 20 个不同的长久连贯,每个过程占有一个。

效率比照

话不多说,咱们间接通过代码来比照。首先,咱们定义好一个统计函数,用来返回以后的毫秒工夫。另外,咱们还要筹备好数据的连贯参数。

function getmicrotime()
{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 = new mysqli($db["server"], $db["user"], $db["password"], $db["database"]); // 长久连贯
    $mysqli->close();}
echo bcsub(getmicrotime(), $startTime, 10), PHP_EOL;
// 6.5814000000

在 1000 次的循环创立数据库的连贯过程中,咱们耗费了 6 秒多的工夫。接下来咱们应用长久化连贯的形式进行这 1000 次的数据库连贯创立。只须要在 mysqli 的 $host 参数前加上一个 p: 即可。

$startTime = getmicrotime();
for ($i = 0; $i < 1000; $i++) {$mysqli = new mysqli('p:' . $db["server"], $db["user"], $db["password"], $db["database"]); // 长久连贯
    $mysqli->close();}
echo bcsub(getmicrotime(), $startTime, 10), PHP_EOL;
// 0.0965000000

从 mysqli 的连贯上来看,效率晋升非常明显。当然,PDO 形式的数据库连贯也提供了建设长久连贯的属性。

$startTime = getmicrotime();
for ($i = 0; $i < 1000; $i++) {$pdo = new PDO("mysql:dbname={$db['database']};host={$db['server']}", $db['user'], $db['password']);
}
echo bcsub(getmicrotime(), $startTime, 10), PHP_EOL;
// 6.6171000000

$startTime = getmicrotime();
for ($i = 0; $i < 1000; $i++) {$pdo = new PDO("mysql:dbname={$db['database']};host={$db['server']}", $db['user'], $db['password'], [PDO::ATTR_PERSISTENT => true]); // 长久连贯
}
echo bcsub(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

各自媒体平台均可搜寻【硬核项目经理】

退出移动版