【问题标题】:Big SQL SELECT performance difference when using <= against using < on a DATETIME column在 DATETIME 列上使用 <= 与使用 < 时的 SQL SELECT 性能差异大
【发布时间】:2009-12-30 14:56:44
【问题描述】:

给定下表:

desc exchange_rates;
+------------------+----------------+------+-----+---------+----------------+
| id               | int(11)        | NO   | PRI | NULL    | auto_increment | 
| time             | datetime       | NO   | MUL | NULL    |                | 
| base_currency    | varchar(3)     | NO   | MUL | NULL    |                | 
| counter_currency | varchar(3)     | NO   | MUL | NULL    |                | 
| rate             | decimal(32,16) | NO   |     | NULL    |                | 
+------------------+----------------+------+-----+---------+----------------+

我在 timebase_currencycounter_currency 上添加了索引,并在 (time、base_currency、counter_currency) 上添加了一个复合索引,但是当我执行 @987654326 时,我发现性能差异很大@ 使用&lt;= 反对使用&lt;

第一个SELECT是:

ExchangeRate Load (95.5ms)   
SELECT * FROM `exchange_rates` WHERE (time <= '2009-12-30 14:42:02' and base_currency = 'GBP' and counter_currency = 'USD') LIMIT 1

如您所见,这需要 95 毫秒。

如果我更改查询以便使用&lt; 而不是&lt;= 比较时间,我会看到:

ExchangeRate Load (0.8ms)   
SELECT * FROM `exchange_rates` WHERE (time < '2009-12-30 14:42:02' and base_currency = 'GBP' and counter_currency = 'USD') LIMIT 1

现在只需要不到 1 毫秒,这对我来说是正确的。这种行为有合理的解释吗?

EXPLAIN 的输出提供了更多详细信息,但我不能 100% 确定如何解释:

-- Output from the first, slow, select
SIMPLE      | 5,5     | exchange_rates | 1  | index_exchange_rates_on_time,index_exchange_rates_on_base_currency,index_exchange_rates_on_counter_currency,time_and_currency | index_merge | Using intersect(index_exchange_rates_on_counter_currency,index_exchange_rates_on_base_currency); Using where | 813  |     | index_exchange_rates_on_counter_currency,index_exchange_rates_on_base_currency

-- Output from the second, fast, select
SIMPLE      | 5       | exchange_rates | 1  | index_exchange_rates_on_time,index_exchange_rates_on_base_currency,index_exchange_rates_on_counter_currency,time_and_currency | ref  | Using where | 4988 | const | index_exchange_rates_on_counter_currency

(注意:我通过ActiveRecord(在 Rails 应用程序中)生成这些查询,但这些最终是正在执行的查询)

【问题讨论】:

标签: mysql ruby-on-rails activerecord


【解决方案1】:

在第一种情况下,MySQL 尝试合并所有索引的结果。它从两个索引中获取所有记录,并将它们连接到行指针的值上(MyISAM 中的表偏移量,InnoDB 中的PRIMARY KEY)。

在第二种情况下,它只使用一个索引,考虑到LIMIT 1,这是最好的决定。

您需要在(base_currency, counter_currency, time)(按此顺序)上创建一个复合索引,以使此查询尽可能快地工作。

引擎将使用索引在前导列(base_currency, counter_currency) 上进行过滤,并在尾随列(time) 上进行排序。

您似乎还想在查询中添加ORDER BY time DESC 之类的内容以获取最新汇率。

一般来说,任何没有ORDER BYLIMIT 都应该响铃。

【讨论】:

  • 嗨,谢谢,但实际上我已经在 (time, base_currency, counter_currency) 上添加了一个复合索引。
  • @Olly:列的顺序错误。顺序很重要。
  • 唯一剩下的一点是,为什么当时间列上的
  • @Michael:我自己从未见过(暂时无法重现),但似乎大多数记录都在作为参数传递的时间戳内,@OP 刚刚打了小费MySQL 更喜欢一个计划而不是另一个计划。无论如何,需要访问实际数据才能准确判断。
  • @Quassnoi: 关于顺序,我认为复合索引必须反映字段在 WHERE 子句中出现的顺序?在我的例子中,时间首先出现,然后是 base_currency,然后是 counter_currency,因此我使用 (time, base_currency, counter_currency) 创建了索引。这不正确吗?
猜你喜欢
  • 2011-12-18
  • 2011-04-13
  • 2019-03-09
  • 1970-01-01
  • 1970-01-01
  • 2010-11-26
  • 1970-01-01
  • 2012-12-29
  • 1970-01-01
相关资源
最近更新 更多