【问题标题】:How to best index this table?如何最好地索引这个表?
【发布时间】:2020-08-24 20:48:14
【问题描述】:

我在 PostgreSQL 中有一个大表(>2000 M 行),必须尽快查询。它代表生物样品中基因表达的测量值。问题是有时测量直接在基因上(“探针”然后为 NULL),有时测量是通过基因的“探针”完成的(“基因”仍然设置)。一个基因可以有多个探针。没有其他表包含基因-探针关系。

CREATE TABLE "gene_measurements" (
  "gene" INTEGER NOT NULL REFERENCES "genes" ON DELETE CASCADE,
  "sample" INTEGER NOT NULL REFERENCES "samples" ON DELETE CASCADE,
  "probe" INTEGER REFERENCES "probes" ON DELETE CASCADE,
  "value" REAL NOT NULL
);

常见的查询包括获取给定样本中所有基因的表达,获取所有样本中给定基因/探针的表达,或获取给定样本中给定基因/探针的表达。

现在我有以下覆盖索引。它工作正常,但它非常占用空间。

CREATE INDEX "gene_measurements_gene_sample_value_index" ON "gene_measurements" ("gene", "sample", "value");
CREATE INDEX "gene_measurements_sample_gene_value_index" ON "gene_measurements" ("sample", "gene", "value");
CREATE INDEX "gene_measurements_sample_probe_value_index" ON "gene_measurements" ("sample", "probe", "value");
CREATE INDEX "gene_measurements_probe_sample_value_index" ON "gene_measurements" ("probe", "sample", "value");

在保持速度的同时,我可以做一些聪明的事情来获得更整洁和/或更小的实现吗?谢谢!

【问题讨论】:

    标签: postgresql data-modeling database-indexes


    【解决方案1】:

    一个 SQL 表确实需要一个主键。从理论上讲,没有键的表是没有意义的。 (实际上,缺少 PK 的 3G 行的表是一场灾难)

    在您的情况下,自然键似乎是(gene_id,sample_id,probe_id) 列的组合。这三列的值需要唯一地寻址value

    问题是你的if probe is absent; measurement was directly on the gene 反约束。 这将禁止三列键。 删除此异常将允许多列主键。 现在,数据技巧是将一个虚拟行插入到探针中,例如,id=0。

    INSERT INTO probe(probe_id, probe_when, probe_name)
     VALUES( 0, '1901-01-01 00:00:00', 'Dummy probe');
    

    现在更新gene_measurements,将probe IS NULL 更改为probe=0


    CREATE TABLE gene_measurements (
      gene INTEGER NOT NULL REFERENCES genes(gene_id) ON DELETE CASCADE
      , sample INTEGER NOT NULL REFERENCES samples(sample_id) ON DELETE CASCADE
      , probe INTEGER NOT NULL REFERENCES probes (probe_id)
      , value REAL NOT NULL
            , PRIMARY KEY ( gene_id, sample_id,probe_id)
    );
    

    也许也可以添加一些其他索引,以不同的顺序来帮助特定的查询,例如:

    CREATE UNIQUE INDEX ON gene_measurements (sample_id,gene_id,probe_id);
    

    您需要一个支持探针 FK 的索引,任何以探针作为第一列的索引都可以:

    CREATE INDEX ON gene_measurements (probe_id, ...);
    

    【讨论】:

    • 谢谢!我已经重新考虑并将遵循您的建议。
    • 它确实涉及对现有数据的一些工作。作为副作用,您可能会捕获一些重复项,从而产生更多工作。关键点是您的三个维度(某些)的(缺乏)基数,但我不知道您的数据。
    • 我不认为我理解关于基数的部分。大约有 5 万个样本、50 万个基因和 50 万个探针。约 20% 的行会有虚拟探针。这种不对称是个问题吗?
    【解决方案2】:

    您可以在空间和时间之间选择任意阈值。现在,您已经为整个表建立了四次索引。这显然会占用大量空间。

    您可以从索引中删除一些数据以换取更快的运行时间:

    • 例如,您可以从所有索引中删除value。但是,除了在索引中查找之外,还需要查找数据。
    • 您也可以完全删除一些索引。例如,根据您的数据,您可以删除(sample, gene)(sample, probe)。这将删除一个完整的数据覆盖范围,同时仍然允许您使用 sample 部分查询具有 sample 和已删除列的条件。同样,您删除的案例也没有以前那么快了。

    如果您的目标是不惜一切代价实现最小运行时间,那么所有这些建议都不适合您。我认为 PostgreSQL 世界中目前没有任何东西可以解决您的问题。

    由于您的数据很简单并且您的用例受到限制,您可以考虑使用 PostgreSQL 以外的解决方案。特别是,您基本上只需要一个 B-Tree 数据结构。 (或多个。)有other solutions来构建这样的数据结构,例如QDBM。尽管如此,您仍需要构建多个这样的结构来优化您选择的每种类型。我认为可实现的空间节省不是很高——基本上,您可以摆脱数据但没有索引。因此,您可以节省大约 1/5 的当前存储大小,但代价是功能受限并增加了软件生态系统的复杂性。

    你必须决定你需要什么、你想要什么以及你想为这些目标牺牲什么。考虑到我在这里写下的内容,我会坚持使用 PostgreSQL。

    【讨论】:

    • 谢谢!然后我想我坚持当前的实现。 WHERE PROBE IS NOT NULL 是否为涉及探测的索引节省了空间/时间?
    • 哦,是的,既然"probe"可以为NULL,使用这两个索引可能就足够了:("probe", "sample", "gene", "value")("probe", "gene", "sample", "value")
    • 不,我通常只通过基因查询而忽略它是否使用探针测量。我想我不能有像("probe", "gene", "sample", "value") 这样的索引,并且对于某些查询指定探针可以是任何东西?我真的不知道索引是如何实现的:)
    • 但是,它基本上归结为只是根据您的查询优化索引。您可以使用documentation as a starting point
    • 不,我认为 B-Tree 索引不可能做到这一点
    猜你喜欢
    • 2023-03-23
    • 2021-08-11
    • 2021-10-10
    • 2021-08-31
    • 1970-01-01
    • 1970-01-01
    • 2020-11-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多