【问题标题】:Is it possible to efficiently search a column with non-decreasing values without an index?是否可以在没有索引的情况下有效地搜索具有非递减值的列?
【发布时间】:2015-11-30 23:18:46
【问题描述】:

我正在创建一个包含非常大 (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(或其他数据库引擎),将单调递增的字段视为此类并执行快速查找,而不使用索引?

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

澄清:

虽然创建索引是完全可能的,而且我可能会在必要时使用它,但找到一种避免它的有效替代方法是问题的主题。希望是一种不涉及大量额外维护或存储开销的方法。

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

【问题讨论】:

  • 不要试图超越标准技术。另外,请停下来重新考虑一下您所问的含义...如果插入的值确实是单调的,那么插入该索引的性能问题几乎不存在,不是吗?
  • 我同意理论上对插入的性能影响应该可以忽略不计,但我担心这过于乐观。我相信树的足够大的部分必须在内存中蜜蜂才能使影响不存在,但是随着索引变大,我怀疑它不适合可用内存并将被交换,从而导致缓存会影响性能的失误。这就是为什么我一直在寻找一种方法来完成这种暗示。我想这是一个很长的镜头,但仍然值得一问。
  • 剧透警报 :) 我想不出这样的机制。但是,除非您需要自动递增的“id”列,否则您也许可以将 (created,controller_address,node_id) 设为主键,因为无论如何这始终是 InnoDB 中的聚集索引……第一个是免费的……但最终我认为你过早地优化了一个主要是理论上的问题,特别是当你以索引顺序插入并且从不修改时。让我们看看是否有人提出了我们没想到的答案。
  • 谢谢。我也希望如此,我会考虑复合主键聚集索引:)
  • 这里的关键问题是表中的记录不是有序结构,而索引是。对有序结构执行的算术运算很便宜,我们可以在几个 I/O 中找到我们需要的记录。表不是有序结构。当然,它是按顺序编写的,但是如果您想找到等于某物或某物之间的记录,则必须遍历整个表才能找到它,因为您不知道必须比较哪些可能的值。指数以空间换取速度并允许这些操作。答案是 - 不,你不能。

标签: mysql database performance indexing


【解决方案1】:

“在特定日期之间”——你的意思是日期吗?还是日期时间?

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

如果 DATETIMEs,则执行类似的操作。这次将每个(例如)第 1000 行的 DATETIME 映射到 INT。现在 WHERE 子句需要足够大(&lt;= 而不是 &lt; 或其他),以避免出现多行具有相同秒数并且您落在集合中间的情况。

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

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

重头来过,这里是最便宜的建表方式:

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

数据中没有额外的成本。还有二级索引的成本(可能是 3GB)。由于已经提到的“热点”,INSERT 只会稍微慢一些。

更好...当然,如果 (created, controller_address, node_id) 是 unique 则将其用作 PK 并完全摆脱 id。这在数据中节省了 4 个字节;指数没有变化; INSERT 没有变化。

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

另外,请记住,您距离溢出 INT UNSIGNED只有 1/40。

【讨论】:

  • 感谢您花时间回答。实际上,每天都有一个条目的 LUT(可以在午夜后的第一批读取期间用餐)可能会起作用。但是,这会导致每个查询和维护 LUT 所需的逻辑开销(我们还将批量删除旧记录)。问题的目的部分是学术性的;以某种方式利用列的非递减属性来提高效率,而无需索引。因此,我暂时将其保持打开状态。
猜你喜欢
  • 2020-12-09
  • 2015-10-08
  • 1970-01-01
  • 1970-01-01
  • 2012-02-05
  • 1970-01-01
  • 2013-12-27
  • 1970-01-01
  • 2017-09-28
相关资源
最近更新 更多