解决“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_packet 以适应我的查询和 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 服务器已消失”错误的主要内容,如果未能解决你的问题,请参考以下文章