mysql select * by index很慢

时间:2018-12-10 02:31:53

标签: mysql query-optimization

我有一张这样的桌子:

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%

有什么方法可以对其进行优化?

3 个答案:

答案 0 :(得分:1)

12秒 可能是现实的。

关于该问题的假设:

  • 总共3000万行,但结果集中只有3000行。
  • 没有足够的空间在RAM 中缓存内容。
  • InnoDB或MyISAM(分析相同;细节完全不同)。
  • CHARACTER SET的任何COLLATIONidcard
  • INDEX(idcard)存在并在查询中使用。
  • HDD磁盘驱动器,而不是SSD。

以下是处理的详细信息:

  1. 转到索引,用?找到第一个条目,向前扫描直到找到不是?的条目(大约3K行)。
  2. 对于这3K项中的每一项,请进入表格以查找所有列(参见{SELECT *
  3. 交付它们。

第1步:快速

第2步:这是昂贵的(基于未缓存的假设)。它可能涉及3K磁盘命中。对于HDD,大约需要30秒。因此,12秒可能意味着某些内容被缓存或碰巧彼此靠近。

第3步:这是网络费用,我没有考虑。

再次运行查询。这次可能只需要1秒钟-因为所有3K块都缓存在RAM中! iostat将显示零活动!

  

有什么方法可以对其进行优化?

好吧...

  • 您已经拥有最好的索引。
  • 您打算一次处理3000行吗?这是一次性任务吗?
  • 使用InnoDB时,innodb_buffer_pool_size应该是可用RAM的大约70%,但不能太大以至于导致交换。它的设置是什么?您有多少RAM?计算机上还运行着什么?
  • 在获取3K行时,您可以做更多的工作吗?
  • 切换到SSD会有所帮助,但我不喜欢硬件创可贴;它们不可重用。
  • 表有多大(以GB为单位)-也许3GB数据加上索引? ({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可能选择完全忽略索引。我在上面提出的建议避免了这种额外的寻求。

相关问题