【问题标题】:Reducing MySQL Query Time (Currently running for 24 hours and still going)减少 MySQL 查询时间(目前运行 24 小时且仍在运行)
【发布时间】:2012-03-01 09:34:38
【问题描述】:

我有一个包含三个表的数据库,我需要将第一个表与其他两个表交叉引用以创建第四个合并信息表。所有表格都有一个通用字段,这是 MSISDN(移动/手机号码),长度至少为 10 位。

表 1 - 819,248 行

表 2 - 75,308,813 行

表 3 - 17,701,196 行

当有匹配的 MSISDN 时,我想返回表 1 中的所有行并附加表 2 和表 3 中的一些字段。 我的查询现在已经运行了超过 24 小时,我不知道这样的事情需要多长时间。

这类查询可能是常规项目——有没有办法显着减少查询时间?

我已经用 MSISDN 索引了表 2 和表 3 以及我需要返回的字段。

我的查询是这样的:

create TABLE FinishedData 
select 
   Table1.ADDRESS, table1.POSTAL, table1.MOBILE, 
   table1.FIRST, table1.LAST, table1.MID, table1.CARRIER, 
   table1.TOWN, table1.ID, table2.status as 'status1', 
   table2.CurrentNetworkName as 'currentnetwork1', 
   table2.DateChecked as 'datechecked1', table3.Status as 'status2', 
   table3.CurrentNetworkName 'currentnetwork2', 
   table3.DateChecked as 'datechecked2' 
from 
   table1 left join (table2, table3) 
       on (right(table1.MOBILE, 10) = right(table2.MSISDN, 10) 
       AND right(table1.MOBILE,10) = right(table3.MSISDN,10))

MySQL 在具有 12GB 内存和 8 个逻辑内核 @ 3GHz 的 64 位 Windows 机器上运行。 MySQLd 在运行查询时仅使用 10% 的 cpu 和 600MB 的资源。

感谢任何帮助。

【问题讨论】:

    标签: mysql sql query-optimization


    【解决方案1】:

    kill性能问题在于正确的函数当你使用这个函数时,MySQL不能使用索引。

    我的建议是:

    1. 在表 2 和表 3 中使用 MSISDN 的 reverse 内容创建新字段
    2. 用左函数替换右函数。

    有了这个小改动,MySQL 可以使用索引来进行连接。

    说明步骤:

    1) 创建新列:

    Alter table table2 add column r_MSISDN varchar(200);
    update table2 set r_MSISDN = reverse( MSISDN );
    
    Alter table table3 add column r_MSISDN varchar(200);
    update table3 set r_MSISDN = reverse( MSISDN );
    

    2) 新加入:

    ...
    from 
       table1 left join (table2, table3) 
           on (right(table1.MOBILE, 10) = left(table2.r_MSISDN, 10) 
           AND right(table1.MOBILE,10) = left(table3.r_MSISDN,10))
    

    【讨论】:

    • 感谢 danihp - 这似乎是导致问题的“正确”功能。我从你的帖子中猜想“左”没有同样的问题。我将尝试使用规范化数据创建新列,然后避免使用“右”或“左”——这样可以避免我以后添加更多数据时必须再次反转数据。我正在使用您的代码来创建新列,但我没有反转数据,而是对其进行规范化。
    【解决方案2】:

    RIGHT 是一个函数。在 where 子句中使用函数意味着 MySQL(可能还有任何数据库)不能使用索引,因为它必须在比较之前为每一行计算函数返回的值。

    如果您想更快地进行此查询,请考虑以标准化形式存储 MSISDN 并使用 = 运算符进行比较。

    现在我不确定 MSISDN 号码是什么样的。如果它是一个固定宽度的数字,那么你的工作很容易。如果它包含分隔符(空格/连字符)并且分隔符只是为了便于阅读,您应该在存储到数据库之前将其删除。如果前 10 个字符很重要,而其余字符是可选的,则可以考虑将前 10 个字符和剩余字符分别存储在不同的列中。

    【讨论】:

    • 谢谢萨尔曼。我正在使用 danihp 建议的代码创建新列,但对数据进行规范化而不是对其进行反转。我想我本来应该这样做的,所以这就是我将使用的解决方案。这也意味着将来我可以规范化更多的 MSISDN 并针对这些进行查询。
    【解决方案3】:

    正如其他人已经提到的,问题在于不允许使用任何索引的 right 函数。

    简单来说,您当前对 table1 中每一行的查询都会对 table2 进行完整扫描,并且对于每个匹配项都会对 table3 进行完整扫描。考虑到您在 table2 和 table3 中有多少行,您很有可能在查询完成之前看到这个世界。

    另一个问题是查询启动了一个巨大的事务,正如 MySQL 认为的那样,它应该能够回滚,您可能会考虑隔离级别。

    不过,我不会更改当前表格。我将使用所需列创建 table2 和 table3 的子副本,并将 right(table2.MSISDN, 10) 添加为 table2 副本中的单独索引列(table3 副本中的 right(table3.MSISDN,10) )。

    然后,您可以对副本执行 LEFT JOIN,甚至可以将副本减少到与 table1 中的任何内容匹配的行,然后执行 LEFT JOIN。

    【讨论】:

    • 感谢您的帮助。我在您发布之前开始创建新列,所以我将完成此操作并检查它是否有效。因为我将来可能会再次引用 table2 和 table3,所以我可能想要不同的列,所以我会尽量避免每次都创建新表。我没有足够的代表来投票支持您的回复 - 抱歉!我需要 15 次或更多。
    猜你喜欢
    • 1970-01-01
    • 2014-07-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-13
    • 2023-03-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多