admin

具有枚举列的数据库设计的替代方法,导致性能不佳

sql

在对上一个问题进行评论之后,我在这里描述了导致我拥有带有枚举列的数据库架构的问题,从而导致性能下降。

(有关我的总体结论,请参见此问题底部的编辑)

我处理基因表达数据。我们捕获了condition任何s在其中gene表达(例如,说基因X在条件[器官Y-生命阶段Z]中表达)。我有4
dataTypes可以产生这样的表达数据。因此,我的原始数据存储在不同的表中,例如(这只是一个示例,原始数据要复杂得多):

+--------------------+------------------------------------+------+-----+--------------+-------+
| Field              | Type                               | Null | Key | Default      | Extra |
+--------------------+------------------------------------+------+-----+--------------+-------+
| geneId             | int(10) unsigned                   | NO   | PRI | NULL         |       |
| evidenceId         | varchar(70)                        | NO   | PRI | NULL         |       |
| experimentId       | varchar(70)                        | NO   | MUL | NULL         |       |
| conditionId        | mediumint(8) unsigned              | NO   | MUL | NULL         |       |
| expressionId       | int(10) unsigned                   | NO   | MUL | NULL         |       |
| detectionFlag      | enum('expressed', 'not expressed') | NO   |     | NULL         |       |
| quality            | enum('low quality','high quality') | NO   |     | NULL         |       |
+--------------------+------------------------------------+------+-----+--------------+-------+

我每个人都有一张这样的桌子dataType。现在,典型的查询将同时请求数千个基因。因为数据非常大(每个表中有几亿行),并且包含冗余值(相同证据的吨证据,相同证据的genegene证据),所以单独查询每个表非常慢。因此,我们有一个预先计算的“摘要”表,该表是根据以下4个表中的信息计算得出的:

+----------------+-----------------------+------+-----+---------+----------------+
| Field          | Type                  | Null | Key | Default | Extra          |
+----------------+-----------------------+------+-----+---------+----------------+
| expressionId   | int(10) unsigned      | NO   | PRI | NULL    | auto_increment |
| geneId         | int(10) unsigned      | NO   | MUL | NULL    |                |
| conditionId    | mediumint(8) unsigned | NO   | MUL | NULL    |                |
+----------------+-----------------------+------+-----+---------+----------------+

(请注意,此表中还有其他有用的列)。该expressionId字段允许返回原始数据。

现在我的问题是:

  • 对于每种数据类型,我们summaryQuality根据condition本身支持表达式行的不同实验的数量来计算a ,同时还要考虑任何相关的关系condition(我给出了相关的内容condition,但是,是的,conditions之间的关系可以是存储在另一个表中)。
  • summaryQuality通过汇总支持dataTypes的任何表达式行的实验,用户应该能够计算出“全局” 。例如,他们应该能够说“从dataType1和dataType2的实验总和中得到x个实验支持的结果”,或“从dataType1和dataType2和dataType3和dataType4的实验总和中得到y个实验的支持” 。

因此,我完成了以下设计:

+--------------------------+-----------------------+------+-----+---------+----------------+
| Field                    | Type                  | Null | Key | Default | Extra          |
+--------------------------+-----------------------+------+-----+---------+----------------+
| expressionId             | int(10) unsigned      | NO   | PRI | NULL    | auto_increment |
| geneId                   | int(10) unsigned      | NO   | MUL | NULL    |                |
| conditionId              | mediumint(8) unsigned | NO   | MUL | NULL    |                |
| dataType1ExperimentCount | smallint(5) unsigned  | NO   |     | 0       |                |
| dataType2ExperimentCount | smallint(5) unsigned  | NO   |     | 0       |                |
| dataType3ExperimentCount | smallint(5) unsigned  | NO   |     | 0       |                |
| dataType4ExperimentCount | smallint(5) unsigned  | NO   |     | 0       |                |
+--------------------------+-----------------------+------+-----+---------+----------------+

该表中的行是通过考虑给定的所有dataType和所有相关condition的来预先计算的conditionId。这是非常缓慢的计算。该表因此具有数亿行。

现在我的查询看起来像:

SELECT * FROM myTable WHERE geneId IN (?, ?, ?, ...) AND (dataType1ExperimentCount + dataType2ExperimentCount + dataType3ExperimentCount + dataType4ExperimentCount) >= ?;
SELECT * FROM myTable WHERE geneId IN (?, ?, ?, ...) AND (dataType1ExperimentCount + dataType2ExperimentCount) >= ?;

根据我上一个问题的答案,性能非常差,因为此类查询无法使用索引。我需要允许dataTypes的任意组合。我需要dataType在将来允许添加new
(从而使组合数达到32或64的速度非常快)。

我能提出什么更好的设计?

编辑用户Rick James的以下请求,显示创建表:

CREATE TABLE `expression` (
  `expressionId` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `geneId` mediumint(8) unsigned NOT NULL,
  `conditionId` mediumint(8) unsigned NOT NULL,
  `dataType1ExperimentCount` smallint(5) unsigned NOT NULL DEFAULT '0',
  `dataType2ExperimentCount` smallint(5) unsigned NOT NULL DEFAULT '0',
  `dataType3ExperimentCount` smallint(5) unsigned NOT NULL DEFAULT '0',
  `dataType4ExperimentCount` smallint(5) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`expressionId`),
  UNIQUE KEY `geneId` (`geneId`,`conditionId`),
  KEY `conditionId` (`conditionId`),
  CONSTRAINT `expression_ibfk_1` FOREIGN KEY (`geneId`) REFERENCES `gene` (`geneId`) ON DELETE CASCADE,
  CONSTRAINT `expression_ibfk_2` FOREIGN KEY (`conditionId`) REFERENCES `cond` (`conditionId`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

(是的,一个给定的geneId表中的行少于一个给定的行conditionId,因此正确地对多个唯一键进行排序)。

编辑,总体结论

  • @RickJame的回答使我的查询运行速度提高了4到5倍,现在它们在合理的时间内运行。现在问题解决了。
  • 但是@Strawberry是正确的,我的设计可以改进(请参阅此问题的评论)
  • 但是在MySQL上,“正确”的设计使我的查询运行速度降低了10倍。我认为这是因为MySQL是基于行的数据库,非常适合检索单行中多列中的所有信息,就像我当前的“枚举”设计一样。
  • 我认为长期解决方案将是使用@Strawberry提出的正确设计,切换到@艁ukaszKami艅ski的答案中提出的基于列的数据库。因为那样,要检索的信息将在几行中,但在同一列中。

阅读 134

收藏
2021-06-07

共1个答案

admin

代替

PRIMARY KEY (`expressionId`),
UNIQUE KEY `geneId` (`geneId`,`conditionId`),

使用

PRIMARY KEY(`geneId`,`conditionId`),
INDEX (`expressionId`),

如果没有其他表在引用expressionId,请摆脱该列及其上的索引。

为什么有帮助?数据通过主键聚类;您正在按来查找数据geneId,这是PK的开始;因此,可以更有效地获取数据,尤其是如果该表要大得多innodb_buffer_pool_size(应该是RAM的70%左右)时。

2021-06-07