mysql - 在 DATETIME 列上使用 <= 与使用 < 时的 Big SQL SELECT 性能差异

标签 mysql ruby-on-rails activerecord

给出下表:

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    |                | 
+------------------+----------------+------+-----+---------+----------------+

我在 time 上添加了索引, base_currencycounter_currency ,以及关于 (time, base_currency, counter_currency) 的复合索引,但是当我执行 SELECT 时,我看到了很大的性能差异。使用 <=反对使用< .

第一个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 毫秒。

如果我更改查询以便使用 < 比较时间而不是 <=我看到了这个:

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 尝试合并来自所有索引的结果。它从两个索引中获取所有记录并将它们连接到行指针的值(MyISAM 中的表偏移量,InnoDB 中的PRIMARY KEY)。

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

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

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

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

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

关于mysql - 在 DATETIME 列上使用 <= 与使用 < 时的 Big SQL SELECT 性能差异,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1980579/

相关文章:

ruby-on-rails - Sidekiq + Sidetiq 每 2 小时重复一次?

ruby-on-rails - Ruby API - JSON 解析 - 写入文件

mysql - 同一个表中的 char 和 varchar

mysql - MySQL、PostgreSQL、SQLite中数据库列类型的比较? (交叉映射)

phpmailer 抛出 mysql 错误

ruby-on-rails - Rails 在 Controller 中使用哈希

ruby-on-rails - 在维护关联时将多个 CSV 导入为 Rails 时遇到问题

mysql - 是否可以使用 codeigniter 事件记录来模拟所有查询

ruby-on-rails - 将元素插入 PostgreSQL 数组而不覆盖它

MySQL View 无法从 MS Access 编辑