php mysql 内存溢出_PDO之MySql持久化自动重连导致内存溢出

前言

最近项目需要一个常驻内存的脚本来执行队列程序,脚本完成后发现Mysql自动重连部分存在内存溢出,导致运行一段时间后,会超出PHP内存限制退出

排查

发现脚本存在内存溢出后排查了一遍代码,基本确认内存溢出在Mysql查询部分.

如果不确认也可以把不必要的模块去掉,从最基本的业务开始测,如果没有内存泄露,则继续增加模块,基本很快就能定位到内存泄露处的代码

解决

我使用的是PHP7.2,所以mysql查询部分使用了PDO,但是PDO并没有提供关闭连接的方法,只是说将连接connection赋值为Null,PHP便会进行垃圾回收,销毁连接.下面是PHP官方手册中的一段话

连接数据成功后,返回一个 PDO 类的实例给脚本,此连接在 PDO 对象的生存周期中保持活动。要想关闭连接,需要销毁对象以确保所有剩余到它的引用都被删除,可以赋一个 NULL 值给对象变量。如果不明确地这么做,PHP 在脚本结束时会自动关闭连接

然而事情没有这么简单,赋值NULL后PHP并没有将PDO实例回收,依然存在.因为还需要将PDOStatement同样赋值为NULL,否则PHP还是不会去执行垃圾回收.

修改后的代码

namespace app\library;

use \PDO;

class Mysql

{

/** @var string 域名 */

private $host = '127.0.0.1';

/** @var int 端口 */

private $port = 3306;

/** @var string 数据库 */

private $dataBase = 'test';

/** @var string 用户名 */

private $userName = 'root';

/** @var string 密码 */

private $password = 'root';

/** @var PDO 数据库连接 */

public $connection;

/** @var Mysql 单例 */

private static $instance;

/** @var \PDOStatement */

protected $PDOStatement;

private function __construct()

{

}

/**

* 连接数据库

*/

protected function connection():void

{

$this->connection = new \pdo(

"mysql:host={$this->host}:{$this->port};dbname={$this->dataBase}",

$this->userName,

$this->password,

[PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING]

);

//设置PDO抛出异常

$this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

}

/**

* 获取数据库连接

* @return PDO

*/

private function getConnection(): PDO

{

if ($this->connection instanceof PDO) {

return $this->connection;

}

$this->connection();

return $this->connection;

}

/**

* 获取实例

* @return Mysql

*/

public static function getInstance()

{

if (is_null(self::$instance)) {

self::$instance = new Mysql();

}

return self::$instance;

}

/**

* 重置链接

*/

protected function resetConnection():void

{

$this->connection = null;

//将此赋值操作屏蔽会造成内存溢出

$this->PDOStatement = null;

}

/**

* 重连

* @return PDO

*/

protected function reconnection(): PDO

{

$this->resetConnection();

return $this->getConnection();

}

/**

* 查询

* @param string $sql sql文本

* @return mixed

*/

public function query(string $sql)

{

try {

$this->PDOStatement = @$this->getConnection()->query($sql);

return $this->PDOStatement->fetch();

} catch (\PDOException $e) {

if (!$this->isMysqlAlive()) {

$this->PDOStatement = $this->reconnection()->query($sql);

return $this->PDOStatement->fetch();

}

throw new $e;

}

}

/**

* mysql连接连接是否有效

* @return bool

*/

protected function isMysqlAlive(): bool

{

$errorInfo = $this->connection->errorInfo();

if ($errorInfo[1] != 2006 or $errorInfo[1] != 2013) {

return false;

}

return true;

}

/**

* 关闭数据库连接

*/

public function close():void

{

$this->connection = null;

$this->PDOStatement = null;

}

public function __destruct():void

{

$this->connection = null;

$this->PDOStatement = null;

}

/**

* 禁止clone

* @throws \Exception

*/

public function __clone()

{

throw new \Exception('deny clone');

}

}

WIKI

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值