我有一张这样的桌子:
create table test (
id int primary key auto_increment,
idcard varchar(30),
name varchar(30),
custom_value varchar(50),
index i1(idcard)
)
我在表格中插入了30,000,000行
然后执行:
select * from test where idcard='?'
该声明需要12秒才能返回
当我使用iostat监视磁盘时
读取速度约为6 mb / s,而实用程序为94%
有什么方法可以对其进行优化?
答案 0 :(得分:1)
12秒 可能是现实的。
关于该问题的假设:
CHARACTER SET
的任何COLLATION
和idcard
。INDEX(idcard)
存在并在查询中使用。以下是处理的详细信息:
?
找到第一个条目,向前扫描直到找到不是?
的条目(大约3K行)。SELECT *
。第1步:快速
第2步:这是昂贵的(基于未缓存的假设)。它可能涉及3K磁盘命中。对于HDD,大约需要30秒。因此,12秒可能意味着某些内容被缓存或碰巧彼此靠近。
第3步:这是网络费用,我没有考虑。
再次运行查询。这次可能只需要1秒钟-因为所有3K块都缓存在RAM中! iostat将显示零活动!
有什么方法可以对其进行优化?
好吧...
innodb_buffer_pool_size
应该是可用RAM的大约70%,但不能太大以至于导致交换。它的设置是什么?您有多少RAM?计算机上还运行着什么?SHOW TABLE STATUS
。)如果您不能使buffer_pool足够大,和,则您会有各种查询竞争此(和其他)表的不同部分,然后更多RAM可能是有益的。答案 1 :(得分:0)
似乎更像是I / O限制,而不是可以通过添加索引来解决的问题。将提高速度的是将idcard列的排序规则更改为latin1_bin。每个字符仅使用1个字节。它还使用二进制比较,比不区分大小写的比较要快。
仅当idcard列中没有特殊字符时才执行此操作,因为latin1的字符集非常有限。
ALTER TABLE `test` CHANGE COLUMN `idcard` `idcard` VARCHAR(30) COLLATE 'latin1_bin' AFTER `id`;
此外,ROW_FORMAT = FIXED也可以提高速度。 ROW_FORMAT = FIXED在InnoDB引擎中不可用,但在MyISAM中可用。我现在拥有的结果表如下所示。 select语句的速度比初始表快5倍(节省了80%的时间)。
请注意,我还将“名称”和“ custom_value”的排序规则更改为latin1_bin。这确实在测试设置速度上产生了很大的差异,但我仍在找出原因。
CREATE TABLE `test` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`idcard` VARCHAR(30) COLLATE 'latin1_bin',
`name` VARCHAR(30) COLLATE 'latin1_bin',
`custom_value` VARCHAR(50) COLLATE 'latin1_bin',
PRIMARY KEY (`id`),
INDEX `i1` (`idcard`)
)
ENGINE=MyISAM
ROW_FORMAT=FIXED ;
答案 2 :(得分:-1)
您可以尝试将select子句中的其他三列添加到索引:
CREATE INDEX idx ON test (idcard, id, name, custom_value);
添加了idcard
以外的三列,以使索引覆盖所有选定的内容。您当前索引的问题在于它仅在idcard
上。这意味着,一旦MySQL遍历了索引中的每个叶节点,它就必须再次寻找聚簇索引以查找select *
中提到的所有列的值。结果,MySQL可能选择完全忽略索引。我在上面提出的建议避免了这种额外的寻求。