解决“MySQL 服务器已消失”错误

Posted

技术标签:

【中文标题】解决“MySQL 服务器已消失”错误【英文标题】:Solving "MySQL server has gone away" errors 【发布时间】: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 代码,现在我不知道该怎么做。请在这方面帮助我。

【问题讨论】:

【参考方案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.

【讨论】:

在这种情况下,Pinging 不是一个好策略......有关原因的更多信息,请查看此 SO 帖子:Should I ping mysql server before each query?【参考方案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 块中重新连接,而是尝试记录异常详细信息并从那里调试问题。

以上是关于解决“MySQL 服务器已消失”错误的主要内容,如果未能解决你的问题,请参考以下文章

MySQL 服务器已消失 haProxy 不断出现错误

MAMP ";#2006的解决方案-MySQL服务器已消失";错误

MySQL导入导致“服务器已消失”错误[重复]

安装迁移期间mysql服务器已消失错误(laravel)

SQLAlchemy 错误 MySQL 服务器已消失

错误 2006 (HY000): MySQL 服务器已消失