Mysql Server超时查看特定的查找查询

时间:2017-08-25 10:56:30

标签: php mysql query-performance zend-framework3

我用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个连接/关键字) - 我合并了这些查询,错误消失了。查询量似乎会扼杀服务器。

3 个答案:

答案 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服务器被杀死。我将这些查询合并到第一个中,错误消失了。