mysql:在这种情况下索引多列的最佳方法是啥
Posted
技术标签:
【中文标题】mysql:在这种情况下索引多列的最佳方法是啥【英文标题】:mysql: what is the best way to index multicolum in this scenariomysql:在这种情况下索引多列的最佳方法是什么 【发布时间】:2020-12-02 18:52:44 【问题描述】:我有以下需要大量时间的 sql 命令。我正在考虑索引
SELECT `daily_price_history`.`high`,
`daily_price_history`.`low`,
`daily_price_history`.`open`,
`daily_price_history`.`datetime`,
`daily_price_history`.`close`,
`daily_price_history`.`creation_time`
FROM `daily_price_history`
WHERE (`daily_price_history`.`datetime_utc` BETWEEN '2015-12-04 18:43:28.710229' AND '2020-12-02 18:43:28.710229'
AND `daily_price_history`.`symbol` = 'A')
ORDER BY `daily_price_history`.`creation_time` ASC
所以索引多个列(datetime_utc,symbol)
是最好的方法或索引两个单独的列。
我的SHOW CREATE TABLE
CREATE TABLE `daily_price_history` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`symbol` varchar(50) NOT NULL,
`symbolExpectionCount` int(11) NOT NULL,
`url` longtext NOT NULL,
`volume` double NOT NULL,
`high` double NOT NULL,
`low` double NOT NULL,
`open` double NOT NULL,
`datetime` bigint(20) NOT NULL,
`datetime_utc` datetime(6) NOT NULL,
`close` double NOT NULL,
`creation_time` datetime(6) NOT NULL,
`lot_time` datetime(6) NOT NULL,
`periodType` varchar(50) NOT NULL,
`symbolId_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `daily_price_history_symbolId_id_30077f62_fk_symbol_list_id` (`symbolId_id`),
KEY `datetime` (`datetime`),
CONSTRAINT `daily_price_history_symbolId_id_30077f62_fk_symbol_list_id` FOREIGN KEY (`symbolId_id`) REFERENCES `symbol_list` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11549349 DEFAULT CHARSET=utf8mb4
我有一个名为symbol_list
的表,所以这里symbol
只是符号文本,而symbolId_id
是与symbol_list
的外键关系
【问题讨论】:
我只是索引日期时间并尝试一下。索引的类型应该是 btree 或允许选择范围的任何其他类型的索引。 符号呢 在我看来,包含符号不会得到太多好处:一旦选择了日期时间范围内的行,就可以在内存中快速完成进一步的过滤。您可以尝试这两种选择。 @Tarik - 这个场景是一个很好的例子,说明INDEX(symbol, dt)
的性能将显着优于INDEX(dt)
或INDEX(symbol)
或INDEX(dt, symbol)
。
【参考方案1】:
在大多数情况下,每个表引用只使用一个索引。所以创建两个独立的索引并不是最好的策略。
在(symbol, datetime_utc)
上建立索引会更好。索引中列的顺序很重要!
您的查询具有symbol
的相等条件,因此它应该先行。
datetime_utc
的范围条件使它更好地放在第二个。
您不能同时针对范围条件和 ORDER BY 进行优化。您必须选择其中一个进行优化。
所以索引应该是以下之一:
(symbol, datetime_utc)
- 针对两个搜索词进行优化,但这需要对结果进行文件排序。
(symbol, creation_time)
- 针对 ORDER BY 进行优化,避免文件排序,但不会针对 datetime_utc 条件进行优化。
哪个更好?这取决于您的数据,以及有多少行符合您的条件。没有简单的答案,您只需测试两者即可。
您可能想查看我的演示文稿How to Design Indexes, Really 或the video of me presenting it。
【讨论】:
我们看到WHERE (daily_price_history.datetime_utc BETWEEN '2015-12-04 18:43:28.710229' AND '2020-12-02 18:43:28.710229' AND daily_price_history.symbol = 'A')
。所以顺序是datetime_utc
,然后是symbol
。所以mysql不会忽略你提到的索引(symbol, datetime_utc)
Your query has an equality condition for symbol so it should go first.
我不知道,所以可能是原因
不,MySQL 的优化器被设计成知道AND
是commutative。换句话说,A AND B
给出与B AND A
相同的结果。因此,无论您在查询中编写术语的顺序如何,MySQL 都可以使用适当的索引。【参考方案2】:
您需要PRIMARY KEY
。它必须是独一无二的。如果您可以保证 (symbol, datetime_utc)
始终是唯一的,那么这是最佳选择:
PRIMARY KEY (symbol, datetime_utc)
如果你不能保证唯一性,那就做
id INT UNSIGNED AUTO_INCREMENT NOT NULL,
PRIMARY KEY (symbol, datetime_utc, id),
INDEX(id)
(如果您最终可能有超过 40 亿行,请将 INT
更改为 BIGINT
。)
这令人费解:
WHERE datetime_utc ...
ORDER BY creation_time
这会更快,并且通常会为我们提供相同的结果(甚至可能更好结果)给我们相同的列:
WHERE datetime_utc ...
ORDER BY datetime_utc
如果您希望有数十亿行,则应尽可能缩小每种数据类型。
您也有volume
列吗?某些索引不适合 INT
。
规范化symbol
并将其替换为SMALLINT UNSIGNED
(2 字节,最多 64K 值)或类似的东西。
open
等的值不适合 BRK.A 的某些较小的数据类型。
细价股可能需要多于 2 位小数。
旧读数为分数,分母为 2 的幂。
警告:以上建议基于美国市场;其他市场可能有更多问题。
名称daily_price_history
意味着每天每个符号一行?然而,使用 8 字节 DATETIME(6)
意味着您正在记录每一笔交易。 (DATE
只有 3 个字节。)
做一些修改后,请提供SHOW CREATE TABLE
,主要的疑问,以及我的问题的答案。我可能有更多的建议。
【讨论】:
我正在使用 TD 美国数据。我有重复。因为有时我可能会错误地获得两次数据,所以我使用creation_time
和日期时间。因此,对于一个符号,可以有多个具有相同日期时间的符号。我将使用creation_time
获取最近存储的信息。数据最终也会增长。
@SanthoshYedidi - 将 INSERT
更改为 INSERT ... ON DUPLICATE KEY UPDATE ...
以便在有 dup 时自动“更新”。也许那时你可以摆脱creation_time
。
但是,我每天必须获取 10,000 个符号数据,如果使用 INSERT
进行更新,则需要大量时间。目前我正在使用批量插入,因此需要 2.5 小时。由于 td America 不允许每秒超过 2 个请求,尽管他们说每秒 120 个请求
@SanthoshYedidi - 我不明白这个问题。需要时间的是数据收集,而不是 INSERT
或 IODKU 语句。 1000 行的批处理 INSERT
只需几秒钟。但是从 td amarica 获得这 1000 行“行”需要 500 秒。还是一个“请求”不止一个符号?
@Tarik - 是的复合索引。 symbol
必须是性能第一。但后来我提出了一个案例,其中多个索引会更好。以上是关于mysql:在这种情况下索引多列的最佳方法是啥的主要内容,如果未能解决你的问题,请参考以下文章
在这种情况下,在 AngularJS 中处理事件的最佳方法是啥?
在这种情况下,设计这个具有 3 个唯一列的简单表的最佳方法是啥?
允许用户在我的类中定义数组大小而不是使用固定变量的最佳方法是啥,在这种情况下为 8
在这种情况下,在 SQL Server 中对三个 nchar 列进行索引的最有效方法是啥?
在不使用 mysql 服务器资源的情况下加密数据的最佳方法是啥?使用 CFB 模式的 mcrypt 是使用 php 的最佳方式吗?