【问题标题】:How to index table for different single-column clauses如何为不同的单列子句索引表
【发布时间】:2018-10-26 04:48:06
【问题描述】:

我有下表:

CREATE TABLE Test (
  device varchar(12),
  pin varchar(4),
  authToken varchar(32),
  Primary Key (device)
);

在应用程序的不同点,我需要通过不同的 single 列子句查询此表。这意味着我有以下疑问:

SELECT * FROM Test WHERE device = ?;
SELECT * FROM Test WHERE authToken = ?;
SELECT * FROM Test WHERE pin = ?;

据我了解,在这种情况下,(device, authToken, pin) 的组合索引没有意义,因为这只会加速第一个查询,而不是第二个或第三个查询。

对于这个表来说,读取速度比写入更重要,那么简单地单独索引每一列是不是这里的最佳解决方案?

【问题讨论】:

  • 是的,您需要三个单独的单列索引。嗯,第一个(device)已经包含在主键中,所以你只需要两个额外的。
  • “阅读”与写作相比有多重要?因为您甚至可以实现覆盖索引,如果您希望查询速度很快。也就是说,通过使用二级索引并避免主索引。我假设你的表是 InnoDB 表,对吗?
  • @TheImpaler 是的,将使用 InnoDB。 “覆盖索引”是所有列的组合索引还是每个列的单独索引?
  • 覆盖索引包括查询引用的所有列。因此它不是单列索引;它从“过滤列”(WHERE 中的列)开始,然后包括其余部分:简而言之,选择非常快,但插入/删除/更新成本很高。此外,您有三个查询,每个查询都需要一个单独的覆盖索引;也就是说,此解决方案将以修改操作为代价使您的选择非常快。仅当您需要高选择速度时才使用它。

标签: mysql sql indexing


【解决方案1】:

直接的答案是为每个查询创建单独的单列索引:

create index ix1 (device); -- no need to create it since it's the PK.
create index ix2 (pin);
create index ix3 (authToken);

第一个索引(来自 PK)使用主索引。第二个和第三个可能会更慢,因为它们受到“二级索引”缓慢的影响:它们总是需要先访问二级索引,然后访问主索引;如果您选择大量行,这可能会变慢。

现在,如果您想在 SELECT 速度方面做得过火,而以修改速度缓慢为代价(INSERTUPDATEDELETE),您可以使用为每个程序量身定制的“覆盖索引”询问。这些应该如下所示:

create index ix4 (device, pin, authToken); -- [non needed] optimal for WHERE device = ?
create index ix5 (authToken, device, pin); -- optimal for WHERE authToken = ?
create index ix6 (pin, device, authToken); -- optimal for WHERE pin = ?

注意:正如 Rick James ix4 所指出的那样,InnoDB 表的主键索引是多余的。没有必要创建它。此处列出只是为了完整性。

这些“覆盖索引”只使用二级索引,完全不需要访问主索引就可以解析查询。对于检索的大量行,它们的速度要快得多。

【讨论】:

  • 不要加ix4; PK完全是多余的。
【解决方案2】:

您不需要索引 pin 列,因为它已经被索引了。对于其他 2 列(即设备和 authToken),是的,根据您的共享查询,最好将它们都单独编入索引。

请注意,当您有大量此类查询访问您在此表上也有大量数据集的服务器时,您将看到性能大幅提升。

【讨论】:

  • 这些单独的索引是否存在性能缺陷?这在非常小的数据集上会明显变慢吗?
  • 一点也不。你放了索引,我相信你会喜欢它的表现:-)
【解决方案3】:

回答:

“如何为不同的单列子句索引表?”

CREATE INDEX Test_device_index ON Test(device);
CREATE INDEX Test_authToken_index ON Test(authToken DESC);
CREATE INDEX Test_pin_index ON Test(pin);

【讨论】:

  • 为什么DESCauthToken-table 上?此外,这基本上是为每一列单独创建一个索引,对吧?还是我错过了什么?
  • @LukasKnuth 我使用DESC 只是为了举例,除了让学习者接触到它之外没有什么特别的原因。据我所知,是的,它为每一列单独创建一个索引。
  • DESCCREATE INDEX 中被忽略(直到 MySQL 8.0)。
  • PRIMARY KEY 是一个索引;所以不要为device添加另一个索引。
【解决方案4】:

这是我建议的架构:

CREATE TABLE Test (
  id SERIAL PRIMARY KEY,
  device VARCHAR(255),
  pin VARCHAR(255),
  authToken VARCHAR(255),
  UNIQUE KEY index_authToken (authToken),
  UNIQUE KEY index_device (device),
  KEY index_pin (pin)
);

您有一个id 类型的列,它不与任何特定数据关联,并且您对authTokendeviceUNIQUE 约束。

请记住将WHERE 中使用的任何列编入索引并使用以下内容测试您的覆盖率:

EXPLAIN SELECT ... FROM Test WHERE pin=?

如果您在计划中看到“表扫描”,那就是缺少索引的问题。

使用VARCHAR(255) 作为默认值也是一个好主意,除非您有非常令人信服的理由来限制它。在您的应用程序层中实施长度限制,以便以后轻松放宽。例如,更改为 6 位 PIN 与 4 是一个简单的代码更改,甚至可以逐步推出,这不是架构更改。

【讨论】:

  • 我不认为pinauthToken 列是独一无二的。是吗?
  • @TheImpaler 这是一个疯狂的猜测,但我希望身份验证令牌是唯一的,否则它正在验证什么设备?
  • 没有。您现在有 3 个唯一键;这会减慢插入速度。使用代理 id 会减慢 device 的查找速度。 VARCHAR(255) -- 是的,有一个保守的限制,但是在某些情况下,一个不合理的大限制是低效的。
  • @RickJames 除非该表具有超过一亿个条目并且具有激进的写入水平,否则额外索引的成本是无关紧要的。此外,由于 MySQL 在所有索引中使用 PRIMARY KEY 值作为行标识符的方式,大主键会对性能产生巨大影响。其他数据库(如 Postgres)总是在索引中使用内部行标识符以避免这种惩罚。 MySQL 没有。
  • @tadman - 通过避免行标识符,MySQL 可以直接获取(通过 PK)行。这使得“惩罚”实际上是一种性能优势(对于 PK)。在这种情况下,我认为 12 字符的 PK 不足以产生“巨大”的影响。
猜你喜欢
  • 2020-09-12
  • 1970-01-01
  • 1970-01-01
  • 2011-11-23
  • 1970-01-01
  • 1970-01-01
  • 2017-07-27
  • 2011-02-19
  • 1970-01-01
相关资源
最近更新 更多