在大型MySQL表中优化索引

时间:2018-09-05 18:17:02

标签: mysql database optimization indexing

我有一个大表(大约3百万条记录),主要包括以下字段:rowID(int),deviceID(varchar(20)),格式为1536169459(int(10))的UnixTimestamp,powerLevel,的整数范围是30到90(smallint(6))。

我正在寻找在特定时间范围内(使用UnixTimestamp)获取特定deviceID且powerLevel高于特定数字的记录。拥有超过300万条记录,需要一段时间。有没有一种方法可以创建为此优化的索引?

3 个答案:

答案 0 :(得分:0)

在以下位置创建索引:

DeviceId,
PowerLevel,
UnixTimestamp

选择时,将首先缩小给定设备的记录集,然后将其缩小到仅在PowerLevel范围内的那些记录。最后,对于每个PowerLevel,它将通过UnixTimestamp缩小到正确的记录。

答案 1 :(得分:0)

如果我对您的理解正确,则希望加快此类查询的速度。

SELECT something
  FROM tbl
 WHERE deviceID = constant
   AND start <= UnixTimestamp
   AND UnixTimestamp < end
   AND Power >= constant

您有一个常量条件(deviceID)和两个范围标准(UnixTimestamp和Power)。 MySQL的索引是BTREE(按顺序排序),MySQL只能对SELECT进行一次索引范围扫描。

因此,您可能应该选择(deviceID, UnixTimestamp, Power)上的索引。为了满足该查询,MySQL将随机访问设备ID条目的索引,然后进一步随机访问满足UnixTimestamp起始条件的第一行。

然后它将顺序扫描索引,并使用每个索引条目中的Power信息来决定是否应选择每一行。

您还可以使用(deviceID, Power, UnixTimestamp)。但是在这种情况下,MySQL将找到匹配设备和电源标准的第一个条目,然后扫描索引以查看所有时间戳记的条目,以查看应选择的行。

您的性能目标是使MySQL扫描尽可能少的索引条目,因此,(deviceID, UnixTimestamp, Power)选择似乎很有可能是更好的选择。 UnixTimestamp上的索引列可能比Power上的索引列更具选择性。 (这是我的猜测。)

ALTER TABLE tbl CREATE INDEX tbl_dev_ts_pwr (deviceID, UnixTimestamp, Power);

请看Bill Karwin的教程。还要看看马库斯·温南(Markus Winand)的https://use-the-index-luke.com

答案 2 :(得分:0)

建议的3列索引仅部分有用。优化程序将使用前两列,但忽略第三列。

更好:

INDEX(DeviceId, PowerLevel),
INDEX(DeviceId, UnixTimestamp)

为什么?

优化器将在这两者之间进行选择,这似乎更具选择性。如果时间范围为“ narrow”,则将使用第二个索引;如果没有许多具有所需PowerLevel的行,则将使用第一个索引。

更好...

PRIMARY KEY ...您可能以Id作为PK?也许(DeviceId, UnixTimestamp)是唯一的? (或者您可以在一秒钟内获得单个设备的两个读数吗??)如果该对是唯一的,则完全摆脱Id并拥有

PRIMARY KEY(DeviceId, UnixTimestamp),
INDEX(DeviceId, PowerLevel)

注意:

  • 摆脱Id可以节省空间,从而提高速度。
  • 使用二级索引时,执行程序会花费一些时间在索引的BTree和数据BTree(由PK排序)之间反弹。通过使用PRIMARY KEY(Id),可以确保您进行弹跳。通过将PK更改为此,可以避免跳动。这可能使查询速度翻倍。
  • (我不确定二级索引是否会全部使用。)

另一个(次要)建议:规范DeviceId,使其(也许)是2字节的SMALLINT UNSIGNED(范围为0..64K)而不是VARCHAR(20)。即使需要JOIN,查询也会运行得更快。并节省了大量空间。