7

我们最近将 Magento 应用程序从私有主机迁移到 AWS Web 服务。我们注意到 Magento 的一些内部功能在迁移后执行的时间过长,因此开始调查。

有问题的查询之一是一个简单的客户选择查询,大约有 9-10 个常规连接到属性表以获取属性。

我们对查询进行了一些测试,发现旧主机和 AWS 之间的区别在于,在旧主机上,MySQL 优化器似乎使用了正确的索引,而在 AWS 中它求助于使用文件排序,而忽略了索引。

UsingFORCE INDEX(index_name)使查询在 AWS 中正​​确执行,但是我们不想走这条路,宁愿修复数据库配置中的问题,也不愿在整个 Magento 应用程序中进行手动破解。需要明确的是,不是我们的索引的问题,它们设置正确。

背景:

  • 我已将旧主机的 my.cnf 文件中的所有 MySQL 参数复制到 RDS 中的参数组,但没有任何区别
  • 这些表都是 InnoDB
  • 我已经运行了分析、修复和优化查询等
  • 在 RDS 上完成查询大约需要 45 秒
  • 查询在旧主机上完成大约需要 2 秒,或者当我使用FORCE INDEX()强制 RDS 以与旧主机相同的方式运行时

旧的 MySQL 服务器运行的是5.1.61版本,而我们运行的 AWS RDS 实例是5.6.19。一个咨询小组向我们建议我们将 RDS 实例降级到 5.1.61,但是我们再次不想这样做,因为它不是一个可持续的解决方案。

有问题的查询如下(为了空间,通过从选择中删除字段来缩短):

SELECT
    `e`.*
    -- various field names here, removed
FROM `customer_entity` AS `e`
LEFT JOIN `customer_entity_int` AS `at_default_billing` ON (`at_default_billing`.`entity_id` = `e`.`entity_id`) AND (`at_default_billing`.`attribute_id` = '13')
LEFT JOIN `customer_address_entity_varchar` AS `at_billing_postcode` ON (`at_billing_postcode`.`entity_id` = `at_default_billing`.`value`) AND (`at_billing_postcode`.`attribute_id` = '30')
LEFT JOIN `customer_address_entity_varchar` AS `at_billing_city` ON (`at_billing_city`.`entity_id` = `at_default_billing`.`value`) AND (`at_billing_city`.`attribute_id` = '26')
LEFT JOIN `customer_address_entity_varchar` AS `at_billing_telephone` ON (`at_billing_telephone`.`entity_id` = `at_default_billing`.`value`) AND (`at_billing_telephone`.`attribute_id` = '31')
LEFT JOIN `customer_address_entity_varchar` AS `at_billing_regione` ON (`at_billing_regione`.`entity_id` = `at_default_billing`.`value`) AND (`at_billing_regione`.`attribute_id` = '28')
LEFT JOIN `customer_address_entity_varchar` AS `at_billing_country_id` ON (`at_billing_country_id`.`entity_id` = `at_default_billing`.`value`) AND (`at_billing_country_id`.`attribute_id` = '27')
LEFT JOIN `core_store` AS `at_store_name` ON (at_store_name.`store_id`=e.store_id)
LEFT JOIN `customer_entity_varchar` AS `firstname` ON e.entity_id = firstname.entity_id AND firstname.attribute_id = 5
LEFT JOIN `customer_entity_varchar` AS `lastname` ON e.entity_id = lastname.entity_id AND lastname.attribute_id = 7 

WHERE (`e`.`entity_type_id` = '1') 

ORDER BY `e`.`entity_id`
DESC LIMIT 20;

EXPLAIN执行此查询时服务器之间的差异摘要如下:

RDS - MySQL v5.6.19:

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: e
         type: ref
possible_keys: IDX_CUSTOMER_ENTITY_ENTITY_TYPE_ID
          key: IDX_CUSTOMER_ENTITY_ENTITY_TYPE_ID
      key_len: 2
          ref: const
         rows: 653990
        Extra: Using temporary; Using filesort

旧主机 - MySQL v5.1.61,或FORCE INDEX在 RDS 上使用时:

*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: e
         type: ref
possible_keys: IDX_CUSTOMER_ENTITY_ENTITY_TYPE_ID
          key: IDX_CUSTOMER_ENTITY_ENTITY_TYPE_ID
      key_len: 2
          ref: const
         rows: 644775
        Extra: Using where

我知道这些数据库版本的差异很可能改变了查询优化器的工作方式,我不知道这些版本的差异是什么,但我正在寻找一种可以帮助的解决方案我们来解决这些差异。


编辑:这是我从旧主机复制的参数与此 MySQL 版本的默认参数的 RDS 比较列表。这些参数都没有影响上面的结果,无论它们是否存在,或者我是否在 RDS 中使用了标准参数:

在此处输入图像描述

4

3 回答 3

1

我的赌注是 MySQL conf:

  • 元数据统计:从 5.6 innodb_stats_on_metadata 默认值更改为关闭。它真的可以改变查询计划。
  • 既然您已经在新安装上运行了一堆查询,您仍然可以使用 mysqltuner 脚本,这总是很好的尝试。
  • 请注意,5.6 将默认 innodb_file_per_table 更改为 1,并且禁用了查询缓存。您可以在此处找到其他默认更改:https ://blogs.oracle.com/supportingmysql/entry/server_defaults_changes_in_mysql
于 2014-11-04T23:51:39.820 回答
1

查看您提供的查询,您可能会Bug #74030 所困扰。为更高版本(5.6.20 和 5.7.4)提供了补丁,但尚未应用,请查看发行说明。登录并为该错误投票可能会很好。

现在,尽管让我很痛苦,但您的顾问小组可能是正确的(也许是偶然的)..直到应用补丁。

于 2014-11-08T20:58:50.690 回答
0

这看起来像文档中提到的关于优化器无法找出正确索引的情况,因为用于选择数据的键与用于对数据进行排序的键不同。

我无法谈论版本之间的任何变化,但这里是关于这种情况的简介:

在某些情况下,MySQL 不能使用索引来解析 ORDER BY,尽管它仍然使用索引来查找与 WHERE 子句匹配的行。这些案例包括:

用于获取行的键与 ORDER BY 中使用的键不同:

SELECT * FROM t1 WHERE key2=constant ORDER BY key1;

这是有问题的文档,它提供了一些纠正问题的建议。我不确定你已经尝试过什么。

于 2014-11-04T15:47:20.620 回答