【问题标题】:Mongodb creating indexes for genomic rangesMongodb 为基因组范围创建索引
【发布时间】:2021-07-22 14:29:05
【问题描述】:

我们是Mongodb 的新手,想用它来插入基因组数据(165M 条目),并通过基因组坐标(范围)检索这些数据。以下是我们存储在单个表中的数据类型。其中列名是chrom,chromStart,chromEnd,datasetid,target,biotype

chr1 9903 10282 ENCSR440COG ZNF239 HEK293
chr1 9904 10252 ENCSR721QZV ZSCAN18 HEK293
chr1 9905 10132 ENCSR241LIH AFF1 K-562
chr1 9905 10238 ENCSR211GNP ZSCAN4 HEK293
chr1 9905 10241 ENCSR776LDJ ZNF645 HEK293
chr1 9905 10243 ENCSR042TWZ SNIP1 MCF-7
chr2 938173 938703 ENCSR000BUL MAX MCF-7
chr2 938174 938376 ENCSR108TYQ GATAD1 Hep-G2
chr3 938174 938412 ENCSR887MXT ZHX1 HeLa-S3
chr3 945236 945377 GSE46055 KDM5B SUM185_SHCTCF
chr4 945236 945488 ENCSR000BPU ETS1 A-549
chr4 945240 945501 GSE76494 CTCF HEK293
chr4 950008 951114 GSE67783 STAG1 HSPC
chr4 950013 950185 ENCSR000BQT TCF3 GM12878
chr4 950015 950797 ENCSR115BLD KDM1A Hep-G2
chr4 950024 950693 GSE88734 ZEB1 MIA-PaCa-2
chr4 950028 950565 ENCSR753GIA TARDBP HEK293T

基因组范围查询的类型是:

db.hsap_all_peaks.find({ chrom: "chr1",  chromStart: {$gte: 9905}, chromEnd:{$lte: 10243}} ).count()
db.hsap_all_peaks.find({ chrom: "chr4",  chromStart: {$gte: 950013}, chromEnd:{$lte: 950693}} ).pretty()

从长远来看,我们计划查询范围和值,例如:

db.hsap_all_peaks.find({ chrom: "chr4",  chromStart: {$gte: 950013}, chromEnd:{$lte: 950693}} , target: "KDM1A").pretty()

这就是我们为坐标创建索引的方式:

db.hsap_all_peaks.createIndex(
    {chrom:1}
)
db.hsap_all_peaks.createIndex(
    {chrom:1,chromStart:1,chromEnd:1}
)

但是,查询的执行时间很长,而且chromStartchromEnd 的索引似乎不起作用。

因此我的问题是:在这里创建索引的最佳方法是什么?

额外信息:

> db.hsap_all_peaks.getIndexes()
[
    {
        "v" : 2,
        "key" : {
            "_id" : 1
        },
        "name" : "_id_"
    },
    {
        "v" : 2,
        "key" : {
            "chrom" : 1,
            "chromStart" : 1,
            "chromEnd" : 1
        },
        "name" : "chrom_1_chromStart_1_chromEnd_1"
    },
    {
        "v" : 2,
        "key" : {
            "chrom" : 1
        },
        "name" : "chrom_1"
    }
]

如果你想重新创建一个类似的表:

wget http://remap.univ-amu.fr/storage/remap2020/hg38/MACS2/remap2020_all_macs2_hg38_v1_0.bed.gz
gunzip remap2020_all_macs2_hg38_v1_0.bed.gz
mongoimport -d databaseName -c hsap_all_peaks --type tsv --file remap2020_all_macs2_hg38_v1_0.bed -f chrom,chromStart,chromEnd,name,score,strand,thickStart,thickEnd,itemRgb  --numInsertionWorkers 2

Explain() 经典查询的输出:

db.hsap_all_peaks.find({ chrom: "chr2",  chromStart: {$gte: 50967094}, chromEnd:{$lte: 50970983} }  ).explain()
{
    "queryPlanner" : {
        "plannerVersion" : 1,
        "namespace" : "remap2020.hsap_all_peaks",
        "indexFilterSet" : false,
        "parsedQuery" : {
            "$and" : [
                {
                    "chrom" : {
                        "$eq" : "chr2"
                    }
                },
                {
                    "chromEnd" : {
                        "$lte" : 50970983
                    }
                },
                {
                    "chromStart" : {
                        "$gte" : 50967094
                    }
                }
            ]
        },
        "queryHash" : "2A452369",
        "planCacheKey" : "C93EF492",
        "winningPlan" : {
            "stage" : "FETCH",
            "inputStage" : {
                "stage" : "IXSCAN",
                "keyPattern" : {
                    "chrom" : 1,
                    "chromStart" : 1,
                    "chromEnd" : 1
                },
                "indexName" : "chrom_1_chromStart_1_chromEnd_1",
                "isMultiKey" : false,
                "multiKeyPaths" : {
                    "chrom" : [ ],
                    "chromStart" : [ ],
                    "chromEnd" : [ ]
                },
                "isUnique" : false,
                "isSparse" : false,
                "isPartial" : false,
                "indexVersion" : 2,
                "direction" : "forward",
                "indexBounds" : {
                    "chrom" : [
                        "[\"chr2\", \"chr2\"]"
                    ],
                    "chromStart" : [
                        "[50967094.0, inf.0]"
                    ],
                    "chromEnd" : [
                        "[-inf.0, 50970983.0]"
                    ]
                }
            }
        },
        "rejectedPlans" : [
            {
                "stage" : "FETCH",
                "filter" : {
                    "$and" : [
                        {
                            "chromEnd" : {
                                "$lte" : 50970983
                            }
                        },
                        {
                            "chromStart" : {
                                "$gte" : 50967094
                            }
                        }
                    ]
                },
                "inputStage" : {
                    "stage" : "IXSCAN",
                    "keyPattern" : {
                        "chrom" : 1
                    },
                    "indexName" : "chrom_1",
                    "isMultiKey" : false,
                    "multiKeyPaths" : {
                        "chrom" : [ ]
                    },
                    "isUnique" : false,
                    "isSparse" : false,
                    "isPartial" : false,
                    "indexVersion" : 2,
                    "direction" : "forward",
                    "indexBounds" : {
                        "chrom" : [
                            "[\"chr2\", \"chr2\"]"
                        ]
                    }
                }
            }
        ]
    },
    "serverInfo" : {
        "host" : "sormiou.local",
        "port" : 27017,
        "version" : "4.4.3",
        "gitVersion" : "913d6b62acfbb344dde1b116f4161360acd8fd13"
    },
    "ok" : 1
}

【问题讨论】:

  • 我不知道答案,但是快速的谷歌表明 MongoDb 支持查询计划和使用docs.mongodb.com/manual/reference/method/db.collection.explain/…挖掘它们的方法
  • 关于MongoDB Indexing Strategies的一般准则。
  • 谢谢@prasad_ 这是我做的第一件事,用谷歌搜索,并查看了指南,但并没有真正找到答案(因此我在这里发帖)
  • 您的查询(如帖子中所列)无法使用所有索引。如我所见,您只需要一个复合索引。 (除非您查询单个字段)。索引使用磁盘空间和 RAM。
  • winningPlan" : { ..... "inputStage" : { "stage" : "IXSCAN", .... "indexName" : "chrom_1_chromStart_1_chromEnd_1" 部分说它确实使用了索引。它是复合的,它最支持您的查询。那里没有什么可以改进的。

标签: database mongodb indexing mongodb-query mongodb-indexes


【解决方案1】:

总结 cmets 没有什么可以改进的索引。您已经拥有最支持您的查询的那个,而 mongo 实际使用它。

提高性能是一个更笼统的话题,对于 SO 格式来说可能太宽泛了。

在这种特殊情况下,如果我们可以假设chromStart 始终小于chromEnd,我们可以通过添加chromStart 的上限与chromEnd 的下限相同来修改查询:

db.hsap_all_peaks.find({ 
    chrom: "chr1",  
    chromStart: {$gte: 9905, $lte: 10243}, 
    chromEnd:{$lte: 10243}
} )

它将改变explain()中的范围

            "indexBounds" : {
                "chrom" : [
                    "[\"chr2\", \"chr2\"]"
                ],
                "chromStart" : [
                    "[9905.0, inf.0]"
                ],
                "chromEnd" : [
                    "[-inf.0, 10243.0]"
                ]
            }

            "indexBounds" : {
                "chrom" : [
                    "[\"chr2\", \"chr2\"]"
                ],
                "chromStart" : [
                    "[9905.0, 10243.0]"
                ],
                "chromEnd" : [
                    "[-inf.0, 10243.0]"
                ]
            }

chromStart 范围越小,需要检查的索引中的节点就越少。

【讨论】:

    【解决方案2】:

    索引的目的是减少处理查询时必须检查的数据量。

    使用现有索引和查询,您可以避免查看任何与查询不匹配的文档。

    唯一可能的改进是减少检查的索引键的数量。

    mongod 扫描索引的方式从 $lte 运算符的负无穷开始,并扩展到 $gte 运算符的正无穷。

    对于任何单个文档,示例数据似乎具有chromStart 严格小于chromEnd 的属性。

    如果该假设是正确的,您可以使用它通过限制限制来进一步优化查询。

    考虑您解释的查询:

    db.hsap_all_peaks.find({ 
                  chrom: "chr2",  
                  chromStart: {$gte: 50967094}, 
                  chromEnd:{$lte: 50970983} 
    })
    

    正如解释命令所报告的,获胜计划使用索引边界:

     "indexBounds" : {
                        "chrom" : [ "[\"chr2\", \"chr2\"]" ],
                        "chromStart" : ["[50967094.0, inf.0]"],
                        "chromEnd" : ["[-inf.0, 50970983.0]"]
                    }
    

    那些inf.0 意味着无穷大,对于不匹配的文档来说,这可能是相当多的键。

    如果您要在查询中对两个条件都使用这两个值,例如:

    db.hsap_all_peaks.find({ 
                  chrom: "chr2",  
                  chromStart: {$gte: 50967094, $lte: 50970983}, 
                  chromEnd:{$gte: 50967094, $lte: 50970983} 
    })
    

    这些索引范围可以(理论上)减少到:

     "indexBounds" : {
                        "chrom" : [ "[\"chr2\", \"chr2\"]" ],
                        "chromStart" : ["[50967094.0, 50970983.0]"],
                        "chromEnd" : ["[50967094.0, 50970983.0]"]
                    }
    

    在一个非常大的数据集中,可能有数百万个不再需要评估的键。

    或者这完全是浪费时间。

    我很想知道它是否真的有帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-05-15
      • 1970-01-01
      • 1970-01-01
      • 2021-08-24
      • 2016-05-26
      • 2018-12-15
      • 2011-03-20
      相关资源
      最近更新 更多