【问题标题】:Mysql Server timing out on specific locate queriesMysql Server 在特定的定位查询上超时
【发布时间】:2017-08-25 10:56:30
【问题描述】:

我正在用 ZF3 和 DB 模块编写搜索程序。 每次我使用超过 1 个短关键字 - 例如“49”和“am”或“1”和“is”时,我都会收到此错误:

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%' 重写了查询,结果相同。

问题似乎出在实时 mysql 服务器上,我该如何调试?

[编辑]

在注意到错误仅发生在具有其他搜索相关查询的特殊视图中 - 每个都使用多个连接(1 个连接/关键字)后 - 我合并了这些查询并且错误消失了。查询的数量似乎要杀死服务器。

【问题讨论】:

  • 可以查看mysql错误日志。您还可以通过 mysql 客户端运行此查询,并检查您从服务器接收到的错误信息

标签: php mysql query-performance zend-framework3


【解决方案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 值集。它为两个搜索词提供与 searchtext 匹配的每个 projectid 值,即使这些词显示在 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 服务器没有帮助,则可能是配置错误或损坏。如果我是你,我会打电话给你的托管服务技术支持,以某种方式绕过前线支持代理(除了“重启你的计算机”和其他类似的愚蠢之外,他们什么都不知道),并告诉他们你得到的确切时间“走开”的消息。他们将能够检查日志。

专业提示:我相信您知道使用LIKE '%text%' 作为搜索词的陷阱。它不可扩展,因为它不是sargable:它不能随机访问索引。如果您可以重新设计您的系统,那么您的时间和精力都是值得的。

【讨论】:

  • 感谢您的提示,我确实使用 LIKE 和 MATCH 重写了查询并进行了一些测试。该错误是由其他原因引起的,请参阅我的 EDIT 1
【解决方案2】:

您可以 TRY / CATCH 来检查您是否收到更具体的错误:

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

【讨论】:

  • 对。 FULLTEXT 搜索不喜欢两个字母的单词。
  • Carlos -- 你正在混合 MyISAM 和 InnoDB 的东西。 Lapskaus - 做SHOW CREATE TABLESHOW VARIABLES LIKE '%ft%';
【解决方案3】:

正如我在编辑中所述,问题不是来自原始问题的查询,而是使用 search - 参数的其他一些查询。每个查询都有如下部分:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-09-08
    • 1970-01-01
    • 2012-05-26
    • 1970-01-01
    • 2021-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多