如果未添加限制,查询不会结束 - AWS RDS MariaDB

Query doesn't end if limit is not added - AWS RDS MariaDB

我目前正在将一些数据库从物理服务器 运行ning MySQl 迁移到 AWS RDS。当前服务器使用 MySQL 5.5,新的 RDS 使用 MariaDB 10.1。一切都进行得很顺利,直到我尝试 运行 一个针对新服务器的应用程序。以下查询即使在 10 分钟内也不会完成,除非我给它添加一个很大的限制。

SELECT al.*
                FROM US.Products p
                JOIN US.Products_Contributors pc ON p.Product_id = pc.Product_id
                JOIN US.Contributors c ON pc.Contributor_Id = c.Contributor_Id
                JOIN US.Products_Category pca ON pca.Product_id = p.Product_id
                JOIN US.Categories ca ON ca.Category_Id = pca.Category_Id
                JOIN US.Asset_Links al ON (al.Asset_link_Id = p.Product_id) OR (al.Asset_link_Id = c.Contributor_ID)
            WHERE p.Product_ISBN13 is not null
            AND (

                ca.Category_Code_3 in ("JNF","JUV")
            )

                AND al.Asset_Link_Table in ("Contributors","Products")
                AND al.Asset_id != 0
             GROUP BY al.Asset_Links_Id;

旧服务器上的查询在大约 11 秒内完成。如果我将 'LIMIT 900000' 添加到新服务器上的查询中,它将在大约 7 秒内完成。在 GROUP by 之前返回了约 800,000 行,在 group by 之后返回了约 150,000 行。如果我将 LIMIT 设置为任何超过 900000 的值,那么查询将无法完成。

我尝试过的事情:

  1. 通过 DB 参数并增加了新服务器上的缓冲区大小,使其与旧服务器相同
  2. 分析查询:所有时间都花在 'Copying to tmp table' 上,只要低于 900000,这与添加 LIMIT 子句时的时间一致。
  3. EXPLAIN 在两台服务器上的输出相同
  4. 正在修复和优化新服务器上的表
  5. 增加 RDS 实例大小 - 现在是数据库。m4.xlarge
  6. 将 RDS 磁盘设置为 1000 IOPS 的预配置 IOPS
  7. 运行 在不同的 MySQL 5.5 服务器上查询,它 运行 与在我的原始源服务器(不是 RDS)上的查询相同。
  8. 在我的 EC2 上安装 MySQL 5.7,它 运行 查询非常慢,除非添加 LIMIT。

所以在我看来,这是使用的 MySQL 版本的问题?但是为什么添加 LIMIT 可以解决问题呢?为什么它只能工作到 900000。

如有任何帮助,我们将不胜感激!

谢谢

更新:16:19 17/12/2017

原始服务器上的解释 MySQL 5.5:

在 RDS MariaDB 10.1 上进行解释(无限制):

{ "query_block": { "select_id": 1, "filesort": { "temporary_table": { "function": "buffer", "table": { "table_name": "p", "access_type": "ALL", "possible_keys": ["PRIMARY", "ISBN", "Product_Id"], "rows": 120108, "filtered": 99.999, "attached_condition": "(p.Product_ISBN13 is not null)" }, "table": { "table_name": "pc", "access_type": "ref", "possible_keys": ["Products_contributor", "Contributor_Id"], "key": "Products_contributor", "key_length": "4", "used_key_parts": ["Product_Id"], "ref": ["US.p.Product_Id"], "rows": 1, "filtered": 100 }, "table": { "table_name": "c", "access_type": "eq_ref", "possible_keys": ["PRIMARY"], "key": "PRIMARY", "key_length": "4", "used_key_parts": ["Contributor_Id"], "ref": ["US.pc.Contributor_Id"], "rows": 1, "filtered": 100, "using_index": true }, "table": { "table_name": "pca", "access_type": "ref", "possible_keys": ["Products_Category", "Category_Id"], "key": "Products_Category", "key_length": "4", "used_key_parts": ["Product_Id"], "ref": ["US.p.Product_Id"], "rows": 2, "filtered": 100 }, "table": { "table_name": "ca", "access_type": "eq_ref", "possible_keys": ["PRIMARY"], "key": "PRIMARY", "key_length": "4", "used_key_parts": ["Category_Id"], "ref": ["US.pca.Category_Id"], "rows": 1, "filtered": 100, "index_condition": "(ca.Category_Id = pca.Category_Id)", "attached_condition": "(ca.Category_Code_3 in ('JNF','JUV'))" }, "block-nl-join": { "table": { "table_name": "al", "access_type": "ALL", "possible_keys": ["Asset_Link_Id", "Asset_Id"], "rows": 908975, "filtered": 95.517, "attached_condition": "((al.Asset_Link_Table in ('Contributors','Products')) and (al.Asset_Id <> 0))" }, "buffer_type": "flat", "buffer_size": "1024Kb", "join_type": "BNL", "attached_condition": "((al.Asset_Link_Id = p.Product_Id) or (al.Asset_Link_Id = pc.Contributor_Id))" } } } } }

解释 RDS MariaDB 10.1(限制 908974):

{ "query_block": { "select_id": 1, "filesort": { "temporary_table": { "function": "buffer", "table": { "table_name": "p", "access_type": "ALL", "possible_keys": ["PRIMARY", "ISBN", "Product_Id"], "rows": 120108, "filtered": 99.999, "attached_condition": "(p.Product_ISBN13 is not null)" }, "table": { "table_name": "pc", "access_type": "ref", "possible_keys": ["Products_contributor", "Contributor_Id"], "key": "Products_contributor", "key_length": "4", "used_key_parts": ["Product_Id"], "ref": ["US.p.Product_Id"], "rows": 1, "filtered": 100 }, "table": { "table_name": "c", "access_type": "eq_ref", "possible_keys": ["PRIMARY"], "key": "PRIMARY", "key_length": "4", "used_key_parts": ["Contributor_Id"], "ref": ["US.pc.Contributor_Id"], "rows": 1, "filtered": 100, "using_index": true }, "table": { "table_name": "pca", "access_type": "ref", "possible_keys": ["Products_Category", "Category_Id"], "key": "Products_Category", "key_length": "4", "used_key_parts": ["Product_Id"], "ref": ["US.p.Product_Id"], "rows": 2, "filtered": 100 }, "table": { "table_name": "ca", "access_type": "eq_ref", "possible_keys": ["PRIMARY"], "key": "PRIMARY", "key_length": "4", "used_key_parts": ["Category_Id"], "ref": ["US.pca.Category_Id"], "rows": 1, "filtered": 100, "index_condition": "(ca.Category_Id = pca.Category_Id)", "attached_condition": "(ca.Category_Code_3 in ('JNF','JUV'))" }, "range-checked-for-each-record": { "keys": ["Asset_Link_Id", "Asset_Id"], "table": { "table_name": "al", "access_type": "ALL", "possible_keys": ["Asset_Link_Id", "Asset_Id"], "key": "Asset_Id", "key_length": "4", "used_key_parts": ["Asset_Id"], "rows": 908975, "filtered": 95.517 } } } } } }

我注意到的是,将限制设置为比报告的加入 ros 数量小 1 的任何数字,然后查询使用 "Range checked for each record (index map: 0x6)",而在 MySQL 5.5 上它使用是否有限制。我发现如果我将 force index(Asset_Link_Id) 添加到最后一个连接,它将始终使用 "Range checked..." 然后查询将完成。

虽然修改和优化所有查询是理想的解决方案,但在这种情况下并不是最好的解决方案。我真的不想修改查询的原因是我正在迁移的服务器上有数百个不同的脚本/应用程序,如果我必须在很多不同的应用程序中修改很多查询,那么这将花费我一个很长一段时间,我将无法在迁移截止日期前完成。所以在这一点上,如果这种行为无法通过设置来控制,那么我可能会在新服务器上使用 MySQL 5.5 而不是 MariaDB 10.1。

是否可以解释为什么查询优化器在 5.7 中选择了具有较大/未定义限制的不同路由,而在 5.5 中却没有?同样在阅读了动态范围和 join_buffer 为什么在范围内使用缓冲区会变慢?从我读到的内容来看,我会认为这是更高效的?

(此答案并未直接解决 "why did this slow down" 问题,但作为安慰奖,解决了其他性能问题。)

我看到了两个多对多映射表。这种典型的实现效率低于它应有的水平。

请按照 https://mariadb.com/kb/en/library/building-the-best-index-for-a-given-select/#many-to-many-mapping-table 中的提示进行操作 -- 然后看看使用或不使用 LIMIT 时性能是否有所提高。

EXPLAIN可能会改变;让我们看看。

Profiling -- 是的,那通常没用;它有一些无意义的消息,它花费了 99% 的时间。

不要将缓冲区大小增加到导致交换的程度;那会很痛。

在允许这样的版本上,请提供EXPLAIN FORMAT=JSON SELECT ...

OR 通常是性能杀手;把它变成 UNION:

 ( SELECT ...
       JOIN US.Asset_Links al ON al.Asset_link_Id = p.Product_id
       ...
 ) UNION DISTINCT
 ( SELECT ...
       JOIN US.Asset_Links al al.Asset_link_Id = c.Contributor_ID
       ...
 )

(因为 GROUP BY,我可能没有将 OR 正确映射到 UNION。)