可以优化查询以便不需要运行分析吗?

Posted

技术标签:

【中文标题】可以优化查询以便不需要运行分析吗?【英文标题】:Can a query be optimized so that running analyze isn't necessary? 【发布时间】:2019-09-10 20:29:45 【问题描述】:

我有一个带有 mysql 数据库(所有 innodb 表)的 Drupal 站点,每隔一段时间就会“崩溃”(阅读:运行缓慢)。服务器托管是基于云的,当数据库移动到新的容器时会发生“崩溃”。连接大约 10 个表并返回单行的查询在移动后开始运行极其缓慢(1 分钟),一切都停止了。通常查询执行速度非常快(不到 0.1 秒),这不是问题。

已确定移动会导致“崩溃”,因为 MySQL 服务器丢失了其“分析”信息 - 该信息存储在内存中,并且在移动数据库时丢失。

目前无法在移动后自动运行分析。托管公司建议重新编写查询,以便不需要进行分析。我想问问社区这是否有意义,以及我可能会如何重写它。


这是从 OP 的 pastebin 复制的信息:

Query_time:0.052842 Lock_time:0.000530 Rows_sent:1 Rows_examined:61031

SELECT node__field_last_sl_play.field_last_sl_play_value AS node__field_last_sl_play_field_last_sl_play_value, node_field_data.nid AS nid, node_field_data_node__field_album.nid AS node_field_data_node__field_album_nid, node_field_data_node__field_artist.nid AS node_field_data_node__field_artist_nid, node_field_data_node__field_event.nid AS node_field_data_node__field_event_nid, votingapi_result_node_field_data.id AS votingapi_result_node_field_data_id, node_field_data_node__field_lifestyle.nid AS node_field_data_node__field_lifestyle_nid
FROM
node_field_data node_field_data
LEFT JOIN node__field_album node__field_album ON node_field_data.nid = node__field_album.entity_id AND node__field_album.deleted = '0' AND (node__field_album.langcode = node_field_data.langcode OR node__field_album.bundle = 'track')
LEFT JOIN node_field_data node_field_data_node__field_album ON node__field_album.field_album_target_id = node_field_data_node__field_album.nid
LEFT JOIN node__field_artist node_field_data_node__field_album__node__field_artist ON node_field_data_node__field_album.nid = node_field_data_node__field_album__node__field_artist.entity_id AND node_field_data_node__field_album__node__field_artist.deleted = '0' AND (node_field_data_node__field_album__node__field_artist.langcode = node_field_data_node__field_album.langcode OR node_field_data_node__field_album__node__field_artist.bundle = 'album')
LEFT JOIN node_field_data node_field_data_node__field_artist ON node_field_data_node__field_album__node__field_artist.field_artist_target_id = node_field_data_node__field_artist.nid
LEFT JOIN node__field_event node__field_event ON node_field_data.nid = node__field_event.entity_id AND node__field_event.deleted = '0' AND (node__field_event.langcode = node_field_data.langcode OR node__field_event.bundle = 'track')
LEFT JOIN node_field_data node_field_data_node__field_event ON node__field_event.field_event_target_id = node_field_data_node__field_event.nid
LEFT JOIN votingapi_result votingapi_result_node_field_data ON node_field_data.nid = votingapi_result_node_field_data.entity_id AND (votingapi_result_node_field_data.entity_type = 'node' AND votingapi_result_node_field_data.function = 'vote_sum' AND votingapi_result_node_field_data.type = 'vote')
LEFT JOIN node__field_lifestyle node__field_lifestyle ON node_field_data.nid = node__field_lifestyle.entity_id AND node__field_lifestyle.deleted = '0'
LEFT JOIN node_field_data node_field_data_node__field_lifestyle ON node__field_lifestyle.field_lifestyle_target_id = node_field_data_node__field_lifestyle.nid
LEFT JOIN node__field_last_sl_play node__field_last_sl_play ON node_field_data.nid = node__field_last_sl_play.entity_id AND node__field_last_sl_play.deleted = '0'
WHERE (node_field_data.status = '1') AND (node_field_data.type IN ('track'))
ORDER BY node__field_last_sl_play_field_last_sl_play_value DESC
LIMIT 1 OFFSET 0;

此查询的解释报告:

EXPLAIN
1   SIMPLE  node_field_data index_merge node_field__type__target_id,node__status_type   node_field__type__target_id,node__status_type   34,35   
    NULL
    2288    Using intersect(node_field__type__target_id,node__status_type); Using where; Using index; Using temporary; Using filesort   
1   SIMPLE  node__field_album   ref PRIMARY,bundle  PRIMARY 5   dbname.node_field_data.nid,const    1   Using where 
1   SIMPLE  node_field_data_node__field_album   ref PRIMARY,node__id__default_langcode__langcode    PRIMARY 4   dbname.node__field_album.field_album_target_id  1   Using where; Using index    
1   SIMPLE  node_field_data_node__field_album__node__field_artist   ref PRIMARY,bundle  PRIMARY 5   dbname.node_field_data_node__field_album.nid,const  1   Using where 
1   SIMPLE  node_field_data_node__field_artist  ref PRIMARY,node__id__default_langcode__langcode    PRIMARY 4   dbname.node_field_data_node__field_album__node__field_artist.field_artist_target_id 1   Using where; Using index    
1   SIMPLE  node__field_event   ref PRIMARY,bundle  PRIMARY 5   dbname.node_field_data.nid,const    1   Using where 
1   SIMPLE  node_field_data_node__field_event   ref PRIMARY,node__id__default_langcode__langcode    PRIMARY 4   dbname.node__field_event.field_event_target_id  1   Using where; Using index    
1   SIMPLE  votingapi_result_node_field_data    ref vote_result_field__type__target_id,vote_result_field__entity_id__target_id  vote_result_field__entity_id__target_id 5   dbname.node_field_data.nid  1   Using where 
1   SIMPLE  node__field_lifestyle   ref PRIMARY PRIMARY 5   dbname.node_field_data.nid,const    1       
1   SIMPLE  node_field_data_node__field_lifestyle   ref PRIMARY,node__id__default_langcode__langcode    PRIMARY 4   dbname.node__field_lifestyle.field_lifestyle_target_id  1   Using where; Using index    
1   SIMPLE  node__field_last_sl_play    ref PRIMARY PRIMARY 5   dbname.node_field_data.nid,const    1       

表定义:

CREATE TABLE `node_field_data` (
 `nid` int(10) unsigned NOT NULL,
 `vid` int(10) unsigned NOT NULL,
 `type` varchar(32) CHARACTER SET ascii NOT NULL COMMENT 'The ID of the target entity.',
 `langcode` varchar(12) CHARACTER SET ascii NOT NULL,
 `title` varchar(255) NOT NULL,
 `uid` int(10) unsigned NOT NULL COMMENT 'The ID of the target entity.',
 `status` tinyint(4) NOT NULL,
 `created` int(11) NOT NULL,
 `changed` int(11) NOT NULL,
 `promote` tinyint(4) NOT NULL,
 `sticky` tinyint(4) NOT NULL,
 `revision_translation_affected` tinyint(4) DEFAULT NULL,
 `default_langcode` tinyint(4) NOT NULL,
 `rh_action` varchar(255) DEFAULT NULL,
 `rh_redirect` varchar(255) DEFAULT NULL,
 `rh_redirect_response` int(11) DEFAULT NULL,
 PRIMARY KEY (`nid`,`langcode`),
 KEY `node__id__default_langcode__langcode` (`nid`,`default_langcode`,`langcode`),
 KEY `node__vid` (`vid`),
 KEY `node_field__type__target_id` (`type`),
 KEY `node_field__created` (`created`),
 KEY `node_field__changed` (`changed`),
 KEY `node__frontpage` (`promote`,`status`,`sticky`,`created`),
 KEY `node__status_type` (`status`,`type`,`nid`),
 KEY `node__title_type` (`title`(191),`type`(4)),
 KEY `node_field__uid__target_id` (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='The data table for node entities.'

【问题讨论】:

顺便说一句,在解析并创建执行计划之前,无法执行查询。 @symcbean 您是说必须在执行查询之前运行分析?如果是这样,为什么这不会自动发生?奇怪的是这已经好 2 年了,现在突然出现问题了。 FWIW 这是一家著名的托管公司,使用 Google 进行容器化。 它会交换吗?如果是这样,你把事情调得太高了。让我们看看 my.cnf 和 VM 的大小。 @RickJames 来自 phpmyadmin 的“变量”是否有效? pastebin.com/jqLBnHU0 表的 innodb stats 应该在重启后第一次打开表时刷新,与 ANALYZE TABLE 完全相同。见dev.mysql.com/doc/refman/5.7/en/… 【参考方案1】:

codesmith,您的查询没有任何问题。当您的表具有合理的索引而不是 1 分钟时,它会在 0.1 秒内运行。既然您和您的托管公司知道移动到另一个容器需要 ANALYZE 来完成该过程,为什么这不是他们过程的一部分? “FWIW 这是一家著名的托管公司,使用 Google 进行容器化。”尝试说服托管公司加倍努力并分析每个表 - 就在他们“移动”之后,以避免在接下来的几天(或几周)出现“异常长时间运行的查询”时出现这种发现。如果时间允许,我仍然想与你进行 Skype TALK。

【讨论】:

以上是关于可以优化查询以便不需要运行分析吗?的主要内容,如果未能解决你的问题,请参考以下文章

运行时查询分析和优化

MySql Query - 使用 varchars、索引进行优化,需要一个多小时才能运行

蜂巢分析查询需要很多时间

mysql性能优化

优化 NHibernate 查询

当负载超过内核时,我还能分析我的代码吗?