这是我第一次使用大型MySQL表,我对搜索速度有几个问题。
我在MySQL表中有一个包含1亿条目的表。该表现在看起来像这样:
+-----------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+-------+
| Accession | char(10) | NO | PRI | NULL | |
| DB | char(6) | NO | | NULL | |
| Organism | varchar(255) | NO | | NULL | |
| Gene | varchar(255) | NO | | NULL | |
| Name | varchar(255) | NO | | NULL | |
| Header | text | NO | | NULL | |
| Sequence | text | NO | | NULL | |
+-----------+--------------+------+-----+---------+-------+
使用这样的索引:
+---------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+---------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| uniprot | 0 | PRIMARY | 1 | Accession | A | 94275840 | NULL | NULL | | BTREE | | |
| uniprot | 1 | main_index | 1 | Accession | A | 94275840 | NULL | NULL | | BTREE | | |
| uniprot | 1 | main_index | 2 | DB | A | 94275840 | NULL | NULL | | BTREE | | |
| uniprot | 1 | main_index | 3 | Organism | A | 94275840 | 191 | NULL | | BTREE | | |
| uniprot | 1 | main_index | 4 | Gene | A | 94275840 | 191 | NULL | | BTREE | | |
| uniprot | 1 | main_index | 5 | Name | A | 94275840 | 191 | NULL | | BTREE | | |
+---------+------------+------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
我的问题是关于效率。我使用的searces非常简单,但我需要的答案非常快。 在80%的情况下,我使用Accession作为查询,我希望序列回来。
select sequence from uniprot where accession="q32p44";
...
1 row in set (0.06 sec)
有10%的时间我会搜索一个" Gene"我有10%的时间都在寻找一种生物。
该表对于"加入"。
是唯一的我的问题是:
无论如何,我能否使这个表更有效率(搜索时间明智)?
索引编制好吗?
通过制作像(加入,基因,生物)这样的多键主键来加快搜索时间吗?
非常感谢!
EDIT1:
根据评论中的要求:
mysql> show create table uniprot;
+---------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+---------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| uniprot | CREATE TABLE `uniprot` (
`Accession` char(10) NOT NULL,
`DB` char(6) NOT NULL,
`Organism` varchar(255) NOT NULL,
`Gene` varchar(255) NOT NULL,
`Name` varchar(255) NOT NULL,
`Header` text NOT NULL,
`Sequence` text NOT NULL,
PRIMARY KEY (`Accession`),
KEY `main_index` (`Accession`,`DB`,`Organism`(191),`Gene`(191),`Name`(191))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 |
+---------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
答案 0 :(得分:1)
不要使用“前缀”索引,它几乎不会像你期望的那样好。
带有CHAR(10)
的 utf8mb4
表示您总是占用40个字节。 accession="q32p44"
意味着VARCHAR
,ascii
会更好。有了这些改变,我就不会费心切换到'代理'键。请考虑DB
的相同问题。
使用PRIMARY KEY(Accession)
和InnoDB,拥有KEY main_index (Accession, ...)
没有任何优势。删除KEY
。
什么是Sequence
?如果它是只有4个不同字母的文本字符串,那么它应该是高度可压缩的。而且,对于100M行,缩小磁盘空间可能会导致明显的加速。我会在客户端COMPRESS
将其存储到BLOB
。
你真的需要varchar(255)
中的255吗?请缩小到合理的数据。这样,我们可以重新考虑要添加的索引,而不使用前缀。
select sequence from uniprot where accession="q32p44";
使用PRIMARY KEY(accession)
select sequence from uniprot where accession="q32p44" AND gene = '...';
也可以有效地使用PK。它将找到q32p44的 one 行,然后只检查gene
是否匹配;然后提供0或1行。
select sequence from uniprot where gene = '...';
将受益于INDEX(gene)
。同样适用于Organism
。
表格有多大(以GB为单位)? innodb_buffer_pool_size
的价值是多少?你有多少RAM?如果表比缓冲池大很多,则随机“点查询”(WHERE accession = constant
)通常会使一个磁盘命中。要讨论其他问题,请向我们展示SELECT
。
修改强>
对于100M行,缩小磁盘占用空间对性能非常重要。有多种方法可以做到这一点。我想专注于(1)缩小每列的大小; (2)避免索引中的隐式开销。
每个辅助密钥隐含地包含PRIMARY KEY
。因此,如果有3个索引,则有3个PK副本。这意味着PK的大小尤其重要。
我推荐像
这样的东西CREATE TABLE `uniprot` (
`Accession` VARCHAR(10) CHARACTER SET ascii NOT NULL,
`DB` VARCHAR(6) NOT NULL,
`Organism` varchar(100) NOT NULL,
`Gene` varchar(100) NOT NULL,
`Name` varchar(100) NOT NULL,
`Header` text NOT NULL,
`Sequence` text NOT NULL,
PRIMARY KEY (`Accession`),
INDEX(Gene), -- implicitly (Gene, Accession)
INDEX(Name) -- implicitly (Organism, Accession)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
您的主要疑问是
SELECT Sequence FROM uniprot WHERE Accession = '...';
SELECT Sequence FROM uniprot WHERE Gene = '...';
SELECT Sequence FROM uniprot WHERE Organism = '...';
如果Accession
实际上是可变长度且比ascii更短,那么我建议将长度从40字节* 3次出现* 100M行= 12GB,仅用于Accession的副本,可能是2GB。我认为节省10GB是值得的。去BIGINT也将是大约2GB(没有进一步的节省);去INT会大约1GB(节省更多,但不多)。
将基因和生物缩小到“合理”大小(如果可行),避免了使用前缀的需要,从而使索引更好地工作。但是,您可以争辩说,前缀可能会在INDEX(Gene(11))
中“足够好”。让我们得到一些数字来使论证成为某种方式。 Gene
(和Organism
)的平均长度是多少? Gene
中有多少个 通常足以识别基因?
另一个空间问题是基因和/或生物体中是否存在大量重复。如果是这样,那么“正常化”这些字段将是合理的。同名,标题和序列。
如果您为JOIN
和/或Accession
制作代理,则需要Gene
(或两个)只是一点点开销,不足以担心。
答案 1 :(得分:0)
首先,正如评论中所提到的,我不会使用自然键(Accession),我会选择代理键(Id),但是有100M行,这将是一个痛苦的改变,在此期间桌子将被锁定。
话虽如此,Accession已被索引为b / c它是主键,因此对于简单查询,您无法进一步优化:
select sequence from uniprot where accession="q32p44";
如果对其他列进行查找,那么最好的办法是为每列添加单独的索引:
ALTER TABLE uniprot ADD INDEX (Gene(10)), ADD KEY (Organism(10));
目标是索引值的唯一性(基数),所以如果你有很多值的somethingsomething1,somethingsomething2,somethingsomething3那么最好使用18+但不大于30的前缀
如果列中的名称通常在前10个字符中不同,则此索引不应比从整个名称列创建的索引慢得多。此外,使用索引的列前缀可以使索引文件更小,这可以节省大量磁盘空间,也可能加快INSERT操作。
所以我们的目标是索引唯一性(基数),但不要在磁盘上增加大小。
我还会删除main_index
索引,因为我没有看到好处,因为您没有同时搜索所有这些列,并且由于长度,会减慢您的写入速度读取的收益很少。
在生产中运行任何内容之前,请务必测试。也许得到一个小样本(1-5%的数据集)并为您计划运行explain
的查询添加前缀,以查看MySQL将如何执行它们。