zoukankan      html  css  js  c++  java
  • 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 applibrary;
    
    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

  • 相关阅读:
    django ---解决跨域的问题
    python-isinstance函数
    python每日一学-os模块常用函数
    调用父类方法super
    fiddler小运用-断点
    劝告
    Django model字段
    Jenkins自动化部署前端
    解决react使用antd table组件固定表头后,表头和表体列不对齐以及配置fixed固定左右侧后行高度不对齐
    高德地图判断点的位置是否在浏览器可视区域内
  • 原文地址:https://www.cnblogs.com/liuyublog/p/11585865.html
Copyright © 2011-2022 走看看