【问题标题】:Why is the same SQL query with different foreign key in WHERE clause getting different performance?为什么 WHERE 子句中具有不同外键的相同 SQL 查询会获得不同的性能?
【发布时间】:2019-06-25 04:40:27
【问题描述】:

为什么这两个查询的唯一区别是campaign_id(另一个表的外键)得到不同的性能和不同的EXPLAIN结果?

查询 1 - 平均时间:0.21 秒

SELECT tx_time, campaign_id, tx_amount, tx_status FROM tx WHERE
 campaign_id=6963 ORDER BY tx_time DESC LIMIT 2500;

查询 2 - 平均时间:0.29 秒

 SELECT tx_time, campaign_id, tx_amount, tx_status FROM tx WHERE
 campaign_id=6946 ORDER BY tx_time DESC LIMIT 2500;

查询 1 与查询 2 解释:

 id  select_type   table   partitions  type    possible_keys     key             key_len   ref   rows    filtered  Extra
 1   SIMPLE        tx      NULL        index   tx_campaign_id    tx_time         4         NULL  85591   2.92      Using where
 1   SIMPLE        tx      NULL        ref     tx_campaign_id    tx_campaign_id  4         const 106312  100       Using index condition; Using filesort

更新:在添加 (tx_id,tx_time,campaign_id) 和 (tx_id,tx_time) 索引并运行 ANALYZE 后,查询 1 已提高到 0.15 秒,但查询 2 已减慢到 13 秒。更新的解释:

 id  select_type   table   partitions  type    possible_keys     key             key_len   ref   rows    filtered  Extra
 1   SIMPLE        tx      NULL        index   tx_campaign_id    tx_time         4         NULL  75450   3.31      Using where
 1   SIMPLE        tx      NULL        ref     tx_campaign_id    tx_campaign_id  4         const 117400  100.00    Using index condition; Using filesort

表交易:

CREATE TABLE tx (
tx_id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
tx_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
campaign_id int(10) unsigned NOT NULL,
tx_amount decimal(12,5) 无符号 NOT NULL,
tx_geo varchar(2) NOT NULL,
tx_langauge varchar(511) NOT NULL,
tx_ua varchar( 511) 非空,
tx_ip varchar(45) 非空,
tx_status tinyint(255) 默认空,
主键 (tx_id),
tx_campaign_id (campaign_id),
KEY tx_time (tx_time) 使用 BTREE,
KEY tx_amount (tx_amount) 使用 BTREE,
KEY tx_time_campaign_id (tx_id,tx_time,campaign_id) 使用 BTREE,
KEY tx_id_time (tx_id,tx_time) 使用 BTREE,
约束 campaign_idcampaign_id 外键 (campaign_id) 参考 campaign (campaign_id) 删除时不操作更新时不操作
) ENGINE=InnoDB AUTO_INCREMENT=10855433 默认字符集=utf8

【问题讨论】:

  • 可能是因为统计信息告诉查询优化器使用不同的索引
  • @RobertKock 是因为可能的总行数 85,591 对 106,312?我应该进行任何更改以进一步优化它吗?
  • 我不是数据库专家。我留下我的评论只是因为我认为原因在那个方向的某个地方。我将最终答案留给更专业的人。
  • 如果您对 0.08 秒感到担忧,您可以更改查询以强制使用索引命中的索引,例如SELECT * FROM table1 USE INDEX (col1_index,col2_index) (see documentation)。
  • 查询显示一张表; EXPLAINs 显示两个表(实际上是“自连接”)。为什么会出现差异??

标签: mysql sql performance


【解决方案1】:

您需要 INDEX(campaign_id, tx_time) 并按该顺序排列列。

一般情况下,将=列放在首位,即campaign_id。在这种情况下,它会处理整个WHERE 子句,因此您可以继续使用ORDER BY。然后添加ORDER BY中的所有列,即tx_time

在成功构建处理这些的索引后,然后处理可以在 LIMIT 行处停止并避免“文件排序”。

Index Cookbook

【讨论】:

  • 谢谢!两个查询现在都在 0.1 秒或以下运行。 Index Cookbook 将进入我的最爱 :)
【解决方案2】:

没有看到您的架构,很难确定,但我猜这是因为优化器试图找出哪个索引更有用。

我假设您的表上没有复合索引 (transaction_id, tx_time);如果你有,优化器可能会使用它(并且更快)。

如果您考虑查询是如何工作的,您可以先根据事务 ID 查找所有记录,然后根据时间对它们进行排序,或者您可以根据时间对记录进行排序,然后丢弃不匹配的记录'不属于您关心的交易ID。

如果您有很多交易 ID 而不是那么多时间戳,则第一个选项(查找所有匹配的交易,然后对它们进行排序)最快。 如果你有很多时间戳,第二个是最快的,而不是很多事务 ID。这就是查询计划之间考虑的行数不同的原因。

对此进行优化的最佳方法是创建复合索引,并确保您update the statistics 查询优化器使用。

【讨论】:

  • InnoDB 不需要 OPTIMIZE TABLE。一张大桌子需要很长时间。 ANALYZE TABLE 非常快的刷新统计;它也很少需要。
  • 是的 - 这个问题没有提到 InnoDB,所以我链接到文档,希望它能解释事情,并导致进一步的调查......
  • @NevilleKuyt 添加复合索引后,查询 1 已加速到 0.13 秒(从 0.21 秒),但查询 2 已减慢到 13 秒(从 0.29 秒)。我已经包含了更新的解释和表架构。
  • @NevilleKuyt - transaction_id??
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-28
  • 2017-06-12
  • 1970-01-01
  • 1970-01-01
  • 2019-10-18
  • 1970-01-01
相关资源
最近更新 更多