在时间戳上创建索引以优化查询

Posted

技术标签:

【中文标题】在时间戳上创建索引以优化查询【英文标题】:Creating an index on a timestamp to optimize query 【发布时间】:2012-02-23 15:47:00 【问题描述】:

我有以下形式的查询:

SELECT * FROM MyTable WHERE Timestamp > [SomeTime] AND Timestamp < [SomeOtherTime]

我想优化这个查询,我正在考虑在时间戳上放置一个索引,但不确定这是否有帮助。理想情况下,我想让时间戳成为聚集索引,但 mysql 不支持聚集索引,除了主键。

MyTable 有超过 400 万行。 Timestamp 实际上是 INT 类型。 一旦插入一行,就永远不会改变。 任何给定Timestamp 的行数平均约为 20,但可能高达 200。 新插入的行的Timestamp 大于大多数现有行,但可能小于某些最近的行。

Timestamp 上的索引能否帮助我优化此查询?

【问题讨论】:

即使在 MSSQL 中,如果您在非唯一列上创建聚集索引,它也会在幕后使其唯一。当然,索引将有助于选择但会减慢插入速度,并且索引会占用磁盘空间。但这听起来像是你愿意接受的交易。将索引放在桌子上并测试。索引用于 > 和 <.> 这张表有聚集索引吗? @BalamBalam 我实际上是在为上述类型的查询设计一个数据库,所以我无法测试性能。 @ypercube MySQL 不支持聚集索引,除了主键,默认是聚集的。 其实没有。 InnoDB 支持每个表使用一个聚集键,PRIMARY KEYUNIQUE KEY。如果您还没有定义任何这些,它将成为一个“卧底”(使用 6 字节 INT,如果我没记错的话)。 【参考方案1】:

对此毫无疑问。如果没有索引,您的查询必须查看表中的每一行。使用索引,就定位正确的行而言,查询几乎是即时的。您将付出的代价是插入的性能轻微下降;但这真的很轻微。

【讨论】:

那么,唯一时间戳的数量非常多并因此会导致相当大的索引这一事实没有不利之处吗? 如果[SomeOtherTime][SomeTime] 之间的差异很小,则会立即生效。 谢谢@ypercube - 只是在答案中限定了:) - 我会说几兆字节的索引的缺点是值得的。数据库擅长这类事情! 它将读取 20-200 行的索引;这些将在 BTree 中是连续的。然后它将在表中查找所需的任何其他列 (SELECT *) 进行 20-200 次查找。与没有INDEX(Timetamp) 相比,非常高效。【参考方案2】:

您绝对应该使用索引。 MySQL 不知道这些时间戳的顺序,为了找到给定时间戳(或时间戳范围)的记录,它需要查看每条记录。有 400 万个,那是相当多的时间!索引是你告诉 MySQL 你的数据的方式——“我会经常查看这个字段,所以请列出我可以在哪里找到每个值的记录。”

一般来说,索引对于定期查询的字段来说是个好主意。定义索引的唯一缺点是它们使用额外的存储空间,因此除非您的空间非常紧张,否则您应该尝试使用它们。如果它们不适用,MySQL 无论如何都会忽略它们。

【讨论】:

【参考方案3】:

我不同意索引对缩短选择查询时间的重要性,但如果您可以对其他键进行索引(并使用这些索引形成您的查询),则可能不需要对时间戳进行索引。

例如,如果您有一个包含timestampcategoryuserId 的表,则最好在userId 上创建索引。在具有许多不同用户的表中,这将大大减少用于搜索时间戳的剩余集合。

...如果我没记错的话,这样做的好处是避免在每次插入时创建时间戳索引的开销——在具有高插入率和高度唯一时间戳的表中,这可能很重要考虑。

我正在努力解决基于时间戳和其他键的索引的相同问题。我还有测试要做,所以我可以证明我在这里所说的。我会根据我的结果尝试回发。

一个更好解释的场景:

    时间戳 99% 唯一 userId 80% 唯一

    类别 25% 独特

    时间戳索引将快速将查询结果减少到表大小的 1% 对 userId 进行索引会迅速将查询结果减少到表大小的 20% 对类别进行索引会迅速将查询结果减少到表大小的 75% 在时间戳上插入索引会产生高开销** 尽管我们知道我们的插入将尊重时间戳递增的事实,但我没有看到任何关于基于递增键的 MySQL 优化的讨论。 在 userId 上插入索引会产生相当高的开销。 在类别上插入索引将具有相当低的开销。

** 抱歉,我不知道计算出的开销或索引插入。

【讨论】:

【参考方案4】:

如果您的查询主要使用此时间戳,您可以测试此设计(以时间戳作为第一部分扩大主键):

CREATE TABLE perf (
  , ts INT NOT NULL
  , oldPK 
  , ... other columns 
, PRIMARY KEY(ts, oldPK)
, UNIQUE (oldPK)
) ENGINE=InnoDB ;

这将确保您发布的查询将使用集群(主)键。

缺点是您的插入会有点慢。此外,如果表上有其他索引,它们将使用更多空间(因为它们将包括 4 字节宽的主键)。

这种聚集索引的最大优点是具有大范围扫描的查询,例如必须读取表的大部分或整个表的查询将按所需顺序(BY timestamp)按顺序查找相关行,如果您想按天、周、月或年分组,这也很有用。

通过保留UNIQUE 约束,旧的PK 仍可用于识别行。


您可能还想看看TokuDB,这是一个允许multiple clustered indices 的MySQL(和开源)变体。

【讨论】:

这种方法的一大缺点是您现在需要知道时间戳以及旧 PK 才能按 PK 查找行。 @DavidHarkness 不,如果旧的 PK 仍然是唯一的。我将编辑答案以明确这一点。 是的,有了新的唯一约束,你就很好了。如果按时间戳进行聚类很重要,那么成本可能是值得的。对于我目前正在构建的系统中的两个表,我必须考虑这一点,它们本质上是用于报告的事务日志。 其实不推荐,PK的用法和索引不一样。 使用时间戳作为PK,不安全,你可以用索引做到这一点,并为id保留PK。

以上是关于在时间戳上创建索引以优化查询的主要内容,如果未能解决你的问题,请参考以下文章

索引优化之:创建填充和查找

Mysq索引优化(什么情况创建索引,什么情况不创建索引)

SQL Server查询优化和事务处理

数据库05 /索引原理/创建用户和授权/数据库备份/慢查询优化/正确使用索引

SQL Server 查询性能优化——创建索引原则

数据库查询优化-添加索引