我正在创建一个包含非常大(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(或者,就其他数据库引擎而言),如何处理单调增加的字段并执行快速查找,而不使用索引?
有没有更好的方法来实现这一目标?
澄清:
尽管创建一个索引是完全可能的,但如果必须的话,我可能会使用它,找到一个避免它的有效替代方法是问题的主题。希望这种方法不会产生大量的额外维护或存储开销。
因此,这个问题主要是出于好奇心而产生的,希望有人遇到类似的情况并想出一些东西来避免创建索引。
答案 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。