【问题标题】:Solving "MySQL server has gone away" errors解决“MySQL 服务器已消失”错误
【发布时间】:2012-01-31 03:46:58
【问题描述】:

我用 PHP 编写了一些代码,这些代码从 .edu 域返回 html 内容。这里简单介绍一下:Errors regarding Web Crawler in PHP

当要抓取的链接数量很少(大约 40 个 URL)时,抓取工具可以正常工作,但在此数字之后出现“MySQL 服务器已消失”错误。

我将 html 内容作为长文本存储在 MySQL 表中,但我不明白为什么在至少 40-50 次插入后出现错误。

非常感谢您在这方面的任何帮助。

请注意,我已经更改了 wait_timeout 和 max_allowed_pa​​cket 以适应我的查询和 php 代码,现在我不知道该怎么做。请在这方面帮助我。

【问题讨论】:

    标签: mysql connection phpmyadmin php


    【解决方案1】:

    您可能倾向于在查询之前通过“ping”mysql 服务器来处理这个问题。这是一个坏主意。有关原因的更多信息,请查看此 SO 帖子:Should I ping mysql server before each query?

    处理此问题的最佳方法是将查询包装在 try/catch 块中并捕获任何数据库异常,以便您可以适当地处理它们。这在长时间运行和/或守护程序类型的脚本中尤其重要。因此,这是一个非常基本的示例,使用“连接管理器”来控制对数据库连接的访问​​:

    class DbPool {
    
        private $connections = array();
    
        function addConnection($id, $dsn) {
            $this->connections[$id] = array(
                'dsn' => $dsn,
                'conn' => null
            );
        }
    
        function getConnection($id) {
            if (!isset($this->connections[$id])) {
                throw new Exception('Invalid DB connection requested');
            } elseif (isset($this->connections[$id]['conn'])) {
                return $this->connections[$id]['conn'];
            } else {
                try {
                    // for mysql you need to supply user/pass as well
                    $conn = new PDO($dsn);
    
                    // Tell PDO to throw an exception on error
                    // (like "MySQL server has gone away")
                    $conn->setAttribute(
                        PDO::ATTR_ERRMODE,
                        PDO::ERRMODE_EXCEPTION
                    );
                    $this->connections[$id]['conn'] = $conn;
    
                    return $conn;
                } catch (PDOException $e) {
                    return false;
                }
            }
        }
    
        function close($id) {
            if (!isset($this->connections[$id])) {
                throw new Exception('Invalid DB connection requested');
            }
            $this->connections[$id]['conn'] = null;
        }
    
    
    }
    
    
    class Crawler {
    
        private $dbPool;
    
        function __construct(DbPool $dbPool) {
            $this->dbPool = $dbPool;
        }
    
        function crawl() {
            // craw and store data in $crawledData variable
            $this->save($crawledData);
        }
    
        function saveData($crawledData) {
            if (!$conn = $this->dbPool->getConnection('write_conn') {
                // doh! couldn't retrieve DB connection ... handle it
            } else {
                try {
                    // perform query on the $conn database connection
                } catch (Exception $e) {
                    $msg = $e->getMessage();
                    if (strstr($msg, 'MySQL server has gone away') {
                        $this->dbPool->close('write_conn');
                        $this->saveData($val);
                    } else {
                        // some other error occurred
                    }
                }
            }
        }
    }
    

    【讨论】:

    • 不,这是一个异常类,您可以自己指定并从saveData() 函数内部抛出。我更新了 saveData 函数并在我的答案中添加了一个自定义 DbException 类以反映这一点...
    【解决方案2】:

    我有another answer 处理我认为类似的问题,并且需要类似的答案。基本上,您可以在插入之前使用mysql_ping() 函数来测试连接。在 MySQL 5.0.14 之前,mysql_ping() 会自动重新连接服务器,但现在您必须构建自己的重新连接逻辑。类似的东西应该适合你:

    function check_dbconn($connection) {
        if (!mysql_ping($connection)) {
            mysql_close($connection);
            $connection = mysql_connect('server', 'username', 'password');
            mysql_select_db('db',$connection);
        } 
        return $connection;
    }
    
    foreach($array as $value) {
        $dbconn = check_dbconn($dbconn);
        $sql="insert into collected values('".$value."')";
        $res=mysql_query($sql, $dbconn);
        //then some extra code.
    }
    

    【讨论】:

    【解决方案3】:

    我在使用Mysql connector 5.X时遇到“Mysql server has gone away”错误,将dll替换为最新版本解决了这个问题。

    【讨论】:

      【解决方案4】:

      您是否打开单个数据库连接并重用它?有可能它是一个简单的超时吗?为每个读/写操作(IE 联系 .edu、获取文本、打开数据库、写入文本、关闭数据库、重复)打开一个新的数据库连接可能会更好地为您服务。

      另外你是如何使用手柄的?是否有可能因为这个原因出现错误并“消失”?

      【讨论】:

      • 我应该为每个查询打开一个新连接,然后在执行该查询后关闭它吗?并对所有查询重复该过程??
      • 为了记录,为每个查询打开一个新连接是非常低效的......
      • 值得补充的是,如果数据库上的线程被杀死(KILL [thread id]),那么您也会收到“服务器已消失”错误。
      • @rdlowrey - 当您谈论像阅读网站这样的缓慢过程时,相比之下,(重新)加载数据库连接的低效率是最小的。我并不是说他会为每个查询建立一个新的连接——但可能是每个线程。 (假设 1 个线程 = 1 个站点和 1 个文件写入数据库)
      • 我认为这个答案只要说“无论出于何种原因,MySQL 的连接都用完了”就足够了。无论如何我都支持你:-)
      【解决方案5】:

      这就是我现在根据 rdlowrey 的建议正在做的事情,我想这也是对的。

      public function url_db_html($sourceLink = NULL, $source) {
          $source = mysql_real_escape_string($source);
      
          $query = "INSERT INTO html (id, sourceLink, sourceCode)
                  VALUES (NULL,('$sourceLink') , ('$source'))";
      
          try {
              if(mysql_query($query, $this->connection)==FALSE) {
                  $msg = mysql_errno($this->connection) . ": " . mysql_error($this->connection);
                  throw new DbException($msg);
              }           
          } catch (DbException $e) {
              echo "<br><br>Catched!!!<br><br>";
              if(strstr($e->getMessage(), 'MySQL server has gone away')) {
                  $this->connection = mysql_connect("localhost", "root", "");
                  mysql_select_db("crawler1", $this->connection);
              }
          }
      }
      

      因此,一旦查询执行失败,脚本将跳过它,但会确保重新建立连接。

      但是,当遇到 .jpg、.bmp、.pdf 等文件时,我的网络爬虫会崩溃。有没有办法跳过那些包含这些扩展的网址。我正在使用 preg_match 并给出了 pdf 和 doc 来匹配。但是我希望该功能跳过所有包含 mp3、pdf 等扩展名的链接。这可能吗??

      【讨论】:

      • 如果您的数据库连接正在关闭,则原因有两个:1)您的代码正在关闭它。 2) 你的系统有一些大问题。我从来没有见过这种重新连接策略,因为我从来没有见过应该需要它的情况。不要在您的 catch 块中重新连接,而是尝试记录异常详细信息并从那里调试问题。
      猜你喜欢
      • 2017-05-15
      • 2012-06-28
      • 2011-12-18
      • 2011-10-12
      • 2013-04-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多