【问题标题】:MySQL performance issue on a simple two tables joined Query一个简单的两个表连接查询的 MySQL 性能问题
【发布时间】:2016-10-02 21:01:54
【问题描述】:

我在 MySQL 上遇到了性能问题,我无法理解我错在哪里。该机器运行 MySQLServer 5.7.15,具有两个 Xeon 64 位处理器8GBytes 的 RAM。 我有两张桌子:

data_raw 包含多个字段(参见 VRMS0VRMS1VRMS2PWRA0 em>,PWRA1,PWRA2)

描述从复杂仪器每 30 秒从现场的多个探头获取的电压和有功功率,每个探头由其 DEVICE_ID 唯一标识。

data_timeslot 包含少量字段,用于跟踪单个 data_raw 记录的发送时间(参见 SRV_TIMESTAMP 字段)

以及来自哪个设备(参见 DEVICE_ID 字段)。

每个表包含大约 7.800.000 条记录。 这两个表在 data_timeslot 上使用 ID 上的 PK(自动增量)和 PK 上的 data_timeslot 上的 em>TIMESLOT_ID(自动递增)。 这是查询:

 SELECT D.VRMS0,D.VRMS1,D.VRMS2,D.PWRA0,D.PWRA1,D.PWRA2,T.DEVICE_ID, T.SRV_TIMESTAMP 
 FROM data_raw AS D FORCE INDEX(PRIMARY) 
 INNER JOIN data_timeslots AS T ON T.ID=D.TIMESLOT_ID 
 WHERE T.DEVICE_ID='CEC02' 
 ORDER BY T.ID DESC LIMIT 1 

查询总是需要 10 秒,而对单个表的相同查询需要几毫秒。 换句话说,查询

SELECT * FROM 'data_raw' order by TIMESLOT_ID desc limit 1

只需 0.0071 秒即可完成查询

SELECT * FROM 'data_timeslots' order by ID desc limit 1

只需要 0.0042 秒,所以我想知道为什么加入需要这么长时间。

瓶颈在哪里?

附: “扩展”表明数据库正在正确使用 PK 进行操作。 在扩展打印输出下方:

`EXPLAIN SELECT D.VRMS0,D.VRMS1,D.VRMS2,D.PWRA0,D.PWRA1,D.PWRA2,T.DEVICE_ID, T.SRV_TIMESTAMP FROM data_raw AS D INNER JOIN data_timeslots AS T ON T.ID =D.TIMESLOT_ID WHERE T.DEVICE_ID='XXXXX' ORDER BY T.ID ASC LIMIT 1


1 SIMPLE T index PRIMARY,PK_CLUSTER_T,DEVICE_ID PRIMARY 8 30 3.23 使用where


1 SIMPLE D eq_ref PRIMARY PRIMARY 8 splc_smartpwr.T.ID 1 100.00 NULL`

更新(@Alberto_Delgado_Roda 建议):如果我使用 ASC LIMIT 1,查询只需 0,0261 秒

【问题讨论】:

  • 您需要在DEVICE_ID(和timeslot_id,但您已经在使用)的索引。
  • 请附上 2 表查询的解释计划。每个表中索引了哪些列?为什么要强制索引主要?如果你不这样做会怎样?
  • @Used_By_Already : FORCE INDEX 只是为了解决这个问题。如果我不使用它,结果是相同的(因为已经使用了主键)。在解释文本结果上方
  • @Solarflare:在 data_timestamps 上已经有 ID (PKey)、DEVICE_ID、SRV_TIMESTAMP 和 DEVICE_ID+SRV_TIMESTAMP(集群索引)上的覆盖索引。在 data_raw 上,唯一可能的索引位于 TIMESLOT_ID
  • 尝试在DEVICE_ID上强制索引。

标签: mysql sql database join


【解决方案1】:

回复“为什么”

Data_timeslots 有一个符合升序的聚集索引

聚集索引如何加速查询

通过聚集索引访问一行速度很快,因为索引搜索直接指向包含所有行数据的页面。如果表很大,与使用与索引记录不同的页面存储行数据的存储组织相比,聚集索引架构通常会节省磁盘 I/O 操作。 (例如,MyISAM 将一个文件用于数据行,另一个用于索引记录。)

https://dev.mysql.com/doc/refman/5.7/en/innodb-index-types.html

【讨论】:

    【解决方案2】:

    试试这个:

    1:如果将 INNER JOIN 替换为 STRAIGHT_JOIN,会发生什么情况?

     SELECT D.VRMS0,D.VRMS1,D.VRMS2,D.PWRA0,D.PWRA1,D.PWRA2,T.DEVICE_ID, T.SRV_TIMESTAMP 
     FROM data_raw AS D FORCE INDEX(PRIMARY) 
     STRAIGHT_JOIN data_timeslots AS T ON T.ID=D.TIMESLOT_ID 
     WHERE T.DEVICE_ID='CEC02' 
     ORDER BY T.ID DESC LIMIT 1 
    
    1. 如果将 DESC LIMIT 1 替换为 ASC LIMIT 1 会怎样?

    【讨论】:

    • STRAIGHT_JOIN 需要更长的时间(102 秒)ASC LIMIT 1 快得令人难以置信 0.0139 秒...为什么?
    【解决方案3】:

    我刚刚发现查询: SELECT T.ID,T.DEVICE_ID, T.SRV_TIMESTAMP, D.VRMS0,D.VRMS1,D.VRMS2,D.PWRA0,D.PWRA1,D.PWRA2 FROM data_timeslots as T INNER JOIN data_raw AS D ON D.TIMESLOT_ID=T.ID ORDER BY T.ID DESC LIMIT 1 正如预期的那样,只需 0.0174 秒我只是颠倒了 SELECT 语句中的顺序,结果发生了巨大变化。现在的问题是为什么???

    【讨论】:

    • 查看解释。您还更改了联接的顺序。也许它现在使用device_id 上的索引?而且,只是为了确保:您使用的是 InnoDB?
    • @Solarflare:是的,我正在使用 InnoDB。我猜想改变连接的顺序也会以某种方式改变使用的索引。但是我很困惑:为什么改变 select 的顺序和 join 的顺序会有很大的不同?我没想到会这样。
    • 你检查它是否改变了索引?实际上可能是缺少的where 改变了执行时间(它可以使用主键有效地加入,而不需要device_id 上的索引)。
    • @Solarflare:我想你是对的。 1 SIMPLE T ref DEVICE_ID DEVICE_ID 15 const 248840 100.00 使用 where 1 SIMPLE D eq_ref PRIMARY PRIMARY 8 splc_smartpwr.T.ID 1 100.00 Null
    • 您使用的是 MySQL 还是 MS SQL Server? (不要标记未涉及的产品。)
    猜你喜欢
    • 1970-01-01
    • 2011-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-02
    • 2014-11-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多