我用ZF3和DB模块编程搜索。 每次我使用超过1个短关键字 - 如“49”和“am”或“1”和“是”我得到此错误:
Statement could not be executed (HY000 - 2006 - MySQL server has gone away)
只要我不使用2个或更多短关键字,使用更长的关键字就可以完美运行。 问题只发生在实时服务器上,它在本地测试服务器上工作正常。
项目表有~2200行,各种数据,project_search表有17000行,每个项目有多个条目,每个看起来像:
id, projectid, searchtext
searchtext列是全文。这里是php代码的相关部分:
$sql = new Sql($this->db);
$select = $sql->select(['p'=>'projects']);
if(isset($filter['search'])) {
$keywords = preg_split('/\s+/', trim($filter['search']));
$join = $sql->select('project_search');
$join->columns(['projectid' => new Expression('DISTINCT(projectid)')]);
$join->group("projectid");
foreach($keywords as $keyword) {
$join->having(["LOCATE('$keyword', GROUP_CONCAT(searchtext))"]);
}
$select->join(
["m" => $join],
"m.projectid = p.id",
['projectid'],
\Zend\Db\Sql\Select::JOIN_RIGHT
);
}
这里得到的查询:
SELECT p.*, m.projectid
FROM projects AS p
INNER JOIN (
SELECT projectid
FROM project_search
GROUP BY projectid
HAVING LOCATE('am', GROUP_CONCAT(searchtext))
AND LOCATE('49', GROUP_CONCAT(searchtext))
) AS m
ON m.projectid = p.id
GROUP BY p.id
ORDER BY createdAt DESC
我使用“MATCH(searchtext)AGAINST('$ keyword)”和“searchtext LIKE'%keyword%'重写了查询,结果相同。
问题似乎是关于live mysql服务器我该怎么调试呢?
[编辑]
注意到错误只发生在具有其他搜索相关查询的特殊视图中 - 每个使用多个连接(1个连接/关键字) - 我合并了这些查询,错误消失了。查询量似乎会扼杀服务器。
答案 0 :(得分:1)
尝试重构内部查询。
SELECT a.projectid
FROM (
SELECT DISTINCT projectid
FROM projectsearch
WHERE searchtext LIKE '%am%'
) a
JOIN (
SELECT DISTINCT projectid
FROM projectsearch
WHERE searchtext LIKE '%49%'
) b ON a.projectid = b.projectid
它应该为您提供与内部查询相同的projectid
值集。它会为两个搜索字词提供匹配projectid
的每个searchtext
值,即使这些字词显示在project_search
的不同行中也是如此。这是您的查询通过搜索GROUP_CONCAT()
输出所做的事情。
尝试在(searchtext, projectid)
上创建索引。 column LIKE '%sample'
的使用意味着您无法随机访问该索引,但是联接中的两个查询仍然可以扫描索引,这比扫描表更快。要添加该索引,请使用此命令。
ALTER TABLE project_search ADD INDEX project_search_text (searchtext, projectid);
尝试在MySQL客户端程序(例如phpmyadmin)中执行此操作,而不是直接从您的php程序执行此操作。
然后,使用MySQL客户端,测试内部查询。看看需要多长时间。使用EXPLAIN SELECT ....
来解释MySQL如何处理查询。
你的短关键词可能会返回一个非常多的匹配,并以某种方式压倒你的系统。在这种情况下,您可以在内部查询的末尾放置LIMIT 1000
子句或某些此类内容。但这不太可能。 17千里不是一个很大的数字。
如果这不能帮助您的生产MySQL服务器可能配置错误或损坏。如果我是你,我会打电话给你的托管服务技术支持,以某种方式通过前线支持代理(除了&#34之外什么都不知道;重新启动计算机"以及其他如此愚蠢),并告诉他们确切地说你已经离开了#34;#34;信息。他们可以查看日志。
专业提示:我确定您知道使用LIKE '%text%'
作为搜索字词的陷阱。它不具备可扩展性,因为它不是sargable:它无法随机访问索引。如果您可以重新设计系统,那么值得花时间和精力。
答案 1 :(得分:0)
您可以尝试/捕捉以检查是否有更具体的错误:
BEGIN TRY
BEGIN TRANSACTION
--Insert Your Queries Here--
COMMIT
END TRY
BEGIN CATCH
DECLARE @ErrorMessage NVARCHAR(4000);
DECLARE @ErrorSeverity INT;
DECLARE @ErrorState INT;
SELECT
@ErrorMessage = ERROR_MESSAGE(),
@ErrorSeverity = ERROR_SEVERITY(),
@ErrorState = ERROR_STATE();
IF @@TRANCOUNT > 0
ROLLBACK
RAISERROR (@ErrorMessage, -- Message text.
@ErrorSeverity, -- Severity.
@ErrorState -- State.
);
END CATCH
虽然因为你在谈论简短的单词和全文,但在我看来它必须与StopWords有关。
尝试从开发服务器和生产服务器运行此查询,并检查是否存在任何差异:
SELECT * FROM INFORMATION_SCHEMA.INNODB_FT_DEFAULT_STOPWORD;
如果设置为:
,请检查my.ini(如果这是配置文件)文本文件ft_stopword_file = ""
ft_min_word_len = 1
答案 2 :(得分:0)
正如我的编辑中所述,问题不是来自原始问题的查询,而是使用搜索参数的一些其他查询。每个查询都有如下部分:
if(isset($filter['search'])) {
$keywords = preg_split('/\s+/', trim($filter['search']));
$field = 1;
foreach($keywords as $keyword) {
$join = $sql->select('project_search');
$join->columns(["pid$field" => 'projectid']);
$join->where(["LOCATE('$keyword', searchtext)"]);
$join->group("projectid");
$select->join(
["m$field" => $join],
"m$field.pid$field = p.id"
);
$field++;
}
}
这导致了很多查询,最终导致很多结果导致mysql服务器被杀死。我将这些查询合并到第一个中,错误消失了。