是否可以在没有索引的情况下有效地搜索具有非递减值的列?

时间:2015-11-30 23:18:46

标签: mysql database performance indexing

我正在创建一个包含非常大(100M +)的历史记录集的表。每条记录都带有时间戳,保证永远不会改变。

主要的读取操作是查询特定日期/日期时间之间的所有记录,这将主要导致总记录的一小部分 - 几天的时间跨度,通常来自特定的控制器。

由于数据是按顺序写入的,因此时间戳保证不会减少。

CREATE TABLE history
(
  id            INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
  controller_id INT(11) UNSIGNED,
  node_address  SMALLINT UNSIGNED,
  p1            SMALLINT,
  p2            SMALLINT,
  state         BOOL,
  created       DATETIME
)
  ENGINE = InnoDB
  DEFAULT CHARSET = utf8;

为简单起见,我们可以忽略其他约束,并假设典型的查询类似于:

SELECT * FROM `history` WHERE `created` BETWEEN <date_time_1> AND <date_time_2>

创建索引的空间成本很高,可能会降低插入性能,但如果没有它,可能会执行全表扫描,使其成为O(n)操作,而实际上是O(log(n)在给定数据约束的情况下,通过二分搜索就足够了。

有没有办法提示MySQL(或者,就其他数据库引擎而言),如何处理单调增加的字段并执行快速查找,而不使用索引?

有没有更好的方法来实现这一目标?

澄清:

尽管创建一个索引是完全可能的,但如果必须的话,我可能会使用它,找到一个避免它的有效替代方法是问题的主题。希望这种方法不会产生大量的额外维护或存储开销。

因此,这个问题主要是出于好奇心而产生的,希望有人遇到类似的情况并想出一些东西来避免创建索引。

1 个答案:

答案 0 :(得分:1)

&#34;特定日期之间&#34; - 你的意思是DATE?还是DATETIMEs?

如果 DATE ,则构建一个将DATE映射到INT的查找表,并对巨大的表进行一次传递以填充此查找表。然后在任何SELECT中使用该表将给定的DATE更改为所需的INT(s)。请务必选择DATE每次出现的 first 的ID。

如果 DATETIME ,则执行类似的操作。这个时间映射每个,例如,第1000行的DATETIME到INT。现在WHERE子句需要是慷慨的(<=而不是<,或者其他什么),以避免存在多行具有相同秒的情况并且你落在集合的中间。< / p>

(最终案例是令人讨厌的;我提到了其中一些。有些变化可能更简单。)

如果您在日期(或小时)范围内进行SUM和COUNT,则应定期构建另一天(或小时)的摘要。将它们放入汇总表。然后查询会更快。

做一些事情,这是建立表格的最便宜方式:

PRIMARY KEY(created, id)  -- clustered, giving you the index you really want
INDEX(id)  -- sufficient to make AUTO_INCREMENT happy

数据没有额外费用。二级索引的成本(可能是3GB)。由于&#34;热点&#34; INSERT只会稍微慢一些。已经提到了。

更好......当然,如果(创建,controller_address,node_id)唯一,那么将其用作PK并完全摆脱id。这样可以节省4个字节的数据;指数没有变化; INSERT没有变化。

请参阅 pt-online-schema-change ,了解如何进行更改几乎没有任何影响。 (它需要一个TRIGGER和足够的磁盘空间来容纳表的额外副本。)

此外,请记住,您是溢出 INT UNSIGNED的1/40。