我有一个包含这样的列的表:
| seqid | bigint(20) | NO | PRI | 0 |
| Time | timestamp | NO | PRI | CURRENT_TIMESTAMP |
| DevId | text | YES | MUL | NULL |
该表通过Time列的值启用分区, 并且DevId的索引长度涵盖了每个可能值的全长。
目前该表有250M +行(可能会增长到400M +), 和~18K到~20K不同的DevIds。
当我尝试运行如下的查询时:
select * from TABLE where DevId='00:1F:23:31:44:48'
查询需要30到90秒,另外30到90秒 获取~20000条记录的秒数。输出解释 显示这样的事情:
+----+-------------+------+-------------+---------+-------+------+-------------+
| id | select_type | type | key | key_len | ref | rows | Extra |
+----+-------------+------+-------------+---------+-------+------+-------------+
| 1 | SIMPLE | ref | DevID_IDX | 387 | const |21042 | Using where |
+----+-------------+------+-------------+---------+-------+------+-------------+
有几件事让我想知道:
为什么key_len是387?我知道MySQL使用更多的字节用于UTF-8编码 表格,但387对于价值观来说太长了。 (他们都是17岁 位)
为什么MySQL需要30到90秒才能获取索引记录?我知道 MySQL的页面大小为16KB,存储了二级索引记录 在BTree的叶节点中,每页只能是1/2到15/16满。 这意味着它可能需要从磁盘中寻找30或40页。 90秒肯定看起来太长了。
innodb每个表的一个文件选项会有帮助吗?任何其他方式来改善 搜索速度?对于具有设备ID的单个查询,我们的目标是几秒钟 和时间限制。
提前感谢任何建议。
答案 0 :(得分:1)
感谢您的所有回复。是的DevID是这种情况的MAC地址,但它可以是其他任何东西。将它更改为varchar()确实有所帮助,但不是太多,因为在最新的MySQL中,短文本列实际上使用了与varchar()几乎相同的空间量。
经过深入调查,实际上我找到了自己的根源,而其他地方很少提及。我想分享我在这里学到的东西,并征求大家的意见。众所周知,mysql只能使用单个索引进行此查询。获取时间很长,因为mysql在主索引叶子节点上存储数据,在我的例子中是一个随机数和一个时间戳。需要包含时间戳,因为我需要对表进行分区。这意味着相同设备的行数据不能顺序存储,这会导致巨大的性能限制。根据我的测试,使用整数来存储dev id确实对索引加载有所帮助,但它对行数据提取没有帮助,不幸的是,这是造成缓慢的真正原因。
但是,对于我的应用程序,很少查询相同设备的长时间数据。在大多数情况下,它将少于一天,在1000到2000行之间。如果mysql只需要访问这些行,那实际上非常快。问题是,如果我运行一个简单的查询,如:
select * from TABLE where DevId='00:1F:23:31:44:48' and Time <> (T1, T2);
表中有400M +行,mysql会选择时间索引,这实际上是一个糟糕的猜测,因为它通常意味着范围扫描为100K +行,有时甚至高达10M行。即使选择了dev id索引,另一方面,mysql将按时间列值进行范围扫描和过滤,这也没有多大帮助。我希望mysql可以足够聪明地利用覆盖索引技术 - 因为dev id索引(二级索引)包含dev id和主键(在我的例子中它是随机数和时间),mysql应该能够知道哪些行到只通过查看二级索引来获取。
但丑陋的现实是,它没有。所以事实证明我需要使用一个小连接,如:
select * from TABLE as a1 join
(select seqid from TABLE use index (DevIDIndex)
where DevID='....' and Time <> (T1,T2)) as a2
on a1.seqid=a2.seqid
对于一天的数据,它会在几秒钟内加载,从几分钟开始。
答案 1 :(得分:0)
你的字段类型是文本(中)。这个数据类型是缓慢的I / O操作。请使用collation(ASCII)来调用char / varchar