我有一张这样的table:
create table test (
id int primary key auto_increment,
idcard varchar(30),
name varchar(30),
custom_value varchar(50),
index i1(idcard)
)
我在表中插入30000000行
然后我执行:
select * from test where idcard='?'
返回语句需要12秒
当我使用iostat监视磁盘时
读取速度约为6 mb/s,而利用率为94%
有什么方法可以优化它吗?
3条答案
按热度按时间s8vozzvw1#
您可以尝试将select子句中的其他三列添加到索引中:
除了
idcard
正在添加以允许索引覆盖所选的所有内容。当前索引的问题是它只在idcard
. 这意味着一旦mysql向下遍历到索引中的每个叶节点,它就必须对聚集索引执行另一次查找,以查找索引中提到的所有列的值select *
. 因此,mysql可能会选择完全忽略索引。我上面提出的建议避免了这种额外的寻求。kknvjkwl2#
12秒可能是现实的。
关于问题的假设:
总共有3000万行,但结果集中只有3000行。
内存中没有足够的空间来缓存东西,或者您是从一个冷启动运行。
innodb或myisam(分析相同;细节完全不同)。
任何
CHARACTER SET
以及COLLATION
为了idcard
.INDEX(idcard)
存在并在查询中使用。hdd磁盘驱动器,而不是ssd。
以下是处理过程的细目:
转到索引,用
?
,向前扫描,直到找到一个不是?
(大约3k行之后)。对于这3k项中的每一项,深入表中查找所有列(cf
SELECT *
.把它们送过去。
第一步:快速。
步骤2:这是(基于不被缓存的假设)代价高昂的。它可能涉及大约3k个磁盘点击。对于hdd来说,这大约需要30秒。所以,12秒可能意味着一些东西被缓存了,或者碰巧在彼此附近。
第三步:这是一个网络成本,我没有考虑。
再次运行查询。这次可能只需要1秒——因为所有3k块都缓存在ram中!iostat将显示零活动!
有什么方法可以优化它吗?
好。。。
你已经有了最好的索引。
你打算一次用3000排干什么?这是一次性任务吗?
使用innodb时,
innodb_buffer_pool_size
应该是可用ram的70%左右,但不会太大导致交换。它的设置是什么?你有多少内存?机器上还运行什么?在获取3k行时,您能完成更多的任务吗?
切换到固态硬盘会有所帮助,但我不喜欢硬件绷带;它们不可重复使用。
表有多大(gb)--可能是3gb数据加索引(
SHOW TABLE STATUS
)如果不能使缓冲池足够大,并且有各种各样的查询竞争这个(和其他)表的不同部分,那么更多的ram可能是有益的。okxuctiv3#
似乎更像是i/o限制,而不是可以通过添加索引来解决的问题。提高速度的方法是将idcard列的排序规则改为latin1\u bin。每个字符仅使用1字节。它还使用二进制比较,这比不区分大小写的比较快。
只有在idcard列中没有特殊字符时才这样做,因为拉丁1的字符集非常有限。
此外,row_format=fixed也提高了速度。row_format=fixed在使用innodb引擎时不可用,但在myisam中可用。我现在得到的结果表如下所示。使用select语句比使用初始表快5倍(时间减少80%)。
注意,我还将'name'和'custom\u value'的排序规则更改为latin1\u bin。这在我的测试设置中确实对速度有很大的影响,我还在想原因。