前言

最近项目需要一个常驻内存的脚本来执行队列程序,脚本完成后发现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

最新文章

  1. [参考]wget下载整站
  2. 1.多线程-NSThread
  3. JS验证身份证号码合法性
  4. 以练代学之shell入门(一)
  5. java 8-7 接口
  6. 基于 URL 的权限控制
  7. 【android】TabLayout文字闪烁问题
  8. Android UDP
  9. Spring MVC 获取前端参数的注解
  10. 贪心:SPOJ Backup Files
  11. [Android] PorterDuff使用实例----实现新浪微博图片下载效果
  12. JAVA用JNI方法调用C代码实现HelloWorld
  13. JQuery - 改变css样式
  14. OCP读书笔记(4) - 配置备份设置
  15. Linux 用 root 用户都无法删除的文件如何删除
  16. Linux 中文输入法安装
  17. android开发(45) 自定义软键盘(输入法)
  18. linux环境中安装NRPE插件执行远程"本地资源"检查?NRPE安装?
  19. DrawDibDraw__ZC测试
  20. gdb 调试(查看运行时数据) 四

热门文章

  1. TEB 系统综合误差
  2. centos 7安装freescale交叉编译工具链
  3. Java Bean(Day_05)
  4. 第14讲 | HTTP协议:看个新闻原来这么麻烦
  5. downloader middleware的三个methods不同返回的情况
  6. [论文阅读笔记] Unsupervised Attributed Network Embedding via Cross Fusion
  7. Python+Selenium自动化-设置等待三种等待方法
  8. Go语言协程并发---timer秒表与定时器
  9. css——圣杯布局
  10. 3D点云重建原理及Pytorch实现