【问题标题】:MySQL query running very slow on Composite key tableMySQL 查询在复合键表上运行非常慢
【发布时间】:2013-07-07 10:31:38
【问题描述】:

我有一个复合键表 CUSTOMER_PRODUCT_XREF

__________________________________________________________________
|CUSTOMER_ID (PK NN VARCHAR(191)) | PRODUCT_ID(PK NN VARCHAR(191))|
-------------------------------------------------------------------

在我的批处理程序中,我需要选择 500 个更新的客户,并获取由逗号分隔的客户购买的 PRODUCT_ID,并更新我们的 SOLR 索引。在我的查询中,我选择了 500 个客户并对 CUSTOMER_PRODUCT_XREF 进行左连接

SELECT 
    customer.*, group_concat(xref.PRODUCT_ID separator ', ')
FROM
    CUSTOMER customer
LEFT JOIN CUSTOMER_PRODUCT_XREF xref ON customer.CUSTOMER_ID=xref.CUSTOMER_ID  
group by customer.CUSTOMER_ID 
LIMIT 500;

编辑:解释查询

id  select_type table   type    possible_keys   key      key_len    ref     rows    Extra
1   SIMPLE     customer ALL      PRIMARY        NULL     NULL       NULL    74236   Using where; Using temporary; Using filesort
1   SIMPLE      xref    index     NULL          PRIMARY  1532       NULL    121627  Using where; Using index; Using join buffer (Block Nested Loop)

运行上述查询 20 分钟后,我遇到了丢失连接异常。

我尝试了以下(子查询),需要 1.7 秒才能得到结果,但仍然很慢。

SELECT 
customer.*, (SELECT group_concat(PRODUCT_ID separator ', ') 
     FROM CUSTOMER_PRODUCT_XREF xref 
        WHERE customer.CUSTOMER_ID=xref.CUSTOMER_ID
        GROUP BY customer.CUSTOMER_ID) 
FROM
CUSTOMER customer
LIMIT 500;

编辑:解释查询产生

id  select_type          table      type    possible_keys   key    key_len  ref     rows   Extra
1   PRIMARY              customer     ALL       NULL        NULL    NULL    NULL    74236   NULL
2   DEPENDENT SUBQUERY    xref      index        NULL     PRIMARY   1532    NULL    121627 Using where; Using index; Using temporary; Using filesort

问题

CUSTOMER_PRODUCT_XREF 已经将两列都设置为 PRIMARY_KEY 和 NOT_NULL 但为什么我的查询仍然很慢?我认为在列上有主键就足以为其建立索引。我需要进一步索引吗?

数据库信息:

  • 我数据库中的所有 ID 都是 VARCHAR(191),因为 ID 可以包含字母。
  • 我正在使用 utf8mb4_unicode_ci 字符编码
  • 我正在使用 SET group_concat_max_len := @@max_allowed_pa​​cket 来获取每个客户的最大 product_id 数量。更喜欢在一个主查询中使用 group_concat,这样我就不必执行多个单独的查询来为每个客户获取产品。

【问题讨论】:

  • 使用EXPLAIN 运行查询并将该命令的结果添加到问题中,它将帮助人们(和您)了解正在发生的事情。但说真的,191 个字符的主键?你确定这不是矫枉过正吗? :-)
  • 尝试在 CUSTOMER_PRODUCT_XREF 上创建一个非唯一索引,仅包括 customer_id 列。
  • 使用 INT 主键可能更有效(通过 LONG 方式),您的 191 个字符键是不同的列,并且您的相关表使用 INT 键而不是长键。
  • 在数据库设计方面,您可以使用自增代理键作为客户表和产品表的主键。您仍然可以保留原始的长客户 ID 和产品 ID,每个 ID 上都有一个唯一索引。然后,您可以在 customer_product_xref 中只包含一对整数的行,这样可以更小、更快地进行比较。
  • @fvu 我添加了解释查询输出 :) 我认为字段的默认大小无关紧要。我应该缩小尺寸吗?

标签: mysql performance


【解决方案1】:

您的原始查询版本首先执行join,然后对所有结果数据进行排序——考虑到字段的大小,这可能相当大。

您可以通过先选择 50000 名客户然后加入来“修复”该版本:

SELECT c.*, group_concat(xref.PRODUCT_ID separator ', ')
FROM (select c.*
      from CUSTOMER customer c
      order by c.customer_id
      limit 500
     ) c LEFT JOIN
     CUSTOMER_PRODUCT_XREF xref
     ON c.CUSTOMER_ID=xref.CUSTOMER_ID  
group by c.CUSTOMER_ID ;

另一种可能会或可能不会产生重大影响的替代方法是在子查询中按客户进行聚合并加入,如下所示:

SELECT c.*, xref.products
FROM (select c.*
      from CUSTOMER customer c
      order by c.customer_id
      limit 500
     ) c LEFT JOIN
     (select customer_id, group_concat(xref.PRODUCT_ID separator ', ') as products
      from CUSTOMER_PRODUCT_XREF xref
     ) xref
     ON c.CUSTOMER_ID=xref.CUSTOMER_ID;

您发现 MySQL 优化器无法识别这种情况(限制对性能有很大影响)。在这种情况下,其他一些数据库引擎在优化方面做得更好。

【讨论】:

  • 第一次查询耗时 1.45 秒,第二次查询未返回任何结果。在我的问题中,第一个查询需要 1.30 秒,第二个查询需要 54 秒。我已将 CUSTOMER_PRODUCT_XREF 主键的 VARCHAR id 长度更改为 20 个字符,但仍然很慢 :(
  • @CMR 。 . .我不知道你为什么皱眉。您正在了解 MySQL 优化器的工作原理。我对相关子查询在 MySQL 中运行良好并不感到惊讶——这并不意味着它在其他数据库中也能运行良好。
【解决方案2】:

好吧,当我在 CUSTOMER_PRODUCT_XREF 表中的 CUSTOMER_ID 上创建索引时,我的问题中的查询速度很快。

所以我现在有两个索引 PRODUCT_ID 和 CUSTOMER_ID 上的 PRIMARY_KEY_INDEX CUSTOMER_ID 上的 CUSTOMER_ID_INDEX

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-09
    • 1970-01-01
    • 2010-12-19
    • 1970-01-01
    • 2021-07-11
    • 1970-01-01
    • 2019-09-29
    相关资源
    最近更新 更多