【发布时间】:2019-12-02 15:33:13
【问题描述】:
我正在使用 php/mysql(mariaDB) 开发一个项目,该项目使用一些数据作为输入生成统计信息。填充 DB 的系统是一个电力装置,特别是警报系统。
我的数据库有三个字段:DeviceTime、VariableName 和 alarmState
alarmState 字段可以有 2 种可能的状态:Normal(当警报响起时)和 Active(当警报处于活动状态时)。
我想在警报出现的时间 (DeviceTime) (Active State) 和警报关闭的时间 (DeviceTime) (Normal state) 之间进行统计
今天我提出了一个可以正常工作的请求,但仅包含少量数据。 当我用所有数据(大约 48k 行)测试请求时,请求花费的时间太长,一段时间后 mysql 崩溃。
这是我的请求,适用于少量数据
select k.deviceTime as stime, k.variableName as svar, k.alarmState as sstate, i.deviceTime, i.variableName, i.alarmState, timediff(i.deviceTime, k.deviceTime) as diff
from imports k
join imports i on i.variableName = k.variableName
and
i.deviceTime = (select t.deviceTime
from imports t
where t.variableName = k.variableName and
t.deviceTime > k.deviceTime and
t.alarmState ='NORMAL'
order by t.deviceTime limit 1
)
where k.alarmState = 'ACTIVE'
这是我的数据表:
id deviceTime variableName alarmState
1 2019-07-11T10:05:24.482 B1.d_07QFA11AN001XB08 ACTIVE
2 2019-07-11T10:05:24.282 B1.d_07QFA11AN001XB08 NORMAL
3 2019-07-11T10:05:15.409 G1.PTUR-38-T.228.52 ACTIVE
4 2019-07-11T10:03:51.409 G1.PTUR-38-T.228.52 NORMAL
5 2019-07-11T10:03:37.409 G1.PTUR-38-T.228.52 ACTIVE
6 2019-07-11T10:03:09.409 G1.PTUR-38-T.228.52 NORMAL
7 2019-07-11T10:02:55.409 G1.PTUR-38-T.228.52 ACTIVE
8 2019-07-11T09:52:06.415 B1.d_07QFA11AN001XB08 ACTIVE
9 2019-07-11T09:52:06.214 B1.d_07QFA11AN001XB08 NORMAL
10 2019-07-11T09:51:06.403 B1.d_07QFA11AN001XB08 ACTIVE
数据量少的结果:
stime svar sstate deviceTime variableName alarmState diff
2019-07-11T09:52:06.415 B1.d_07QFA11AN001XB08 ACTIVE 2019-07-11T10:05:24.282 B1.d_07QFA11AN001XB08 NORMAL 00:13:17
2019-07-11T10:03:37.409 G1.PTUR-38-T.228.52 ACTIVE 2019-07-11T10:03:51.409 G1.PTUR-38-T.228.52 NORMAL 00:00:14
2019-07-11T10:02:55.409 G1.PTUR-38-T.228.52 ACTIVE 2019-07-11T10:03:09.409 G1.PTUR-38-T.228.52 NORMAL 00:00:14
2019-07-11T09:51:06.403 B1.d_07QFA11AN001XB08 ACTIVE 2019-07-11T09:52:06.214 B1.d_07QFA11AN001XB08 NORMAL 00:00:59
这正是我想要的结果,但是如果有人有优化这个请求的想法,或者另一种方法来构建一个可以返回alarmState 和对应的variableName 之间的时间差的请求。
编辑:
我的 MariaDB 版本是10.4.6-MariaDB
这是表结构
create table imports
(
id bigint unsigned auto_increment
primary key,
deviceTime varchar(255) not null,
variableName varchar(255) not null,
alarmState varchar(255) null,
created_at timestamp null,
updated_at timestamp null
);
还有Explain query
id select_type table type possible_key key key_len ref rows Extra
1 PRIMARY k ALL <null> <null> <null> <null> 44679 Using where; Using temporary; Using filesort
1 PRIMARY i ALL <null> <null> <null> <null> 44679 Using where; Using join buffer (flat, BNL join)
2 DEPENDENT SUBQUERY t ALL <null> <null> <null> <null> 44679 Using where; Using filesort
编辑2
我将deviceTime 列的类型更改为DATETIME。我创建了这样的索引
create index imports_alarmstate_index
on imports (alarmState);
create index imports_devicetime_index
on imports (deviceTime);
create index imports_variablename_index
on imports (variableName);
我将查询修改为使用MIN() 而不是mysql Order BY ... Limit 1。
现在我的查询看起来像
select k.deviceTime as stime,
k.variableName as svar,
k.alarmState as sstate,
i.deviceTime,
i.variableName,
i.alarmState,
timestampdiff (second, i.deviceTime, k.deviceTime) as diff
from imports k
join imports i on i.variableName = k.variableName and
i.deviceTime = (select MIN(t.deviceTime)
from imports t
where t.variableName = k.variableName and
t.deviceTime > k.deviceTime and
t.alarmState ='NORMAL'
)
where k.alarmState <> 'NORMAL'
我使用timestampdiff() 而不是datediff(),因为时间戳格式更易于订购。
我的 where 条件 k.alarmState <> 'NORMAL' 发生了变化,因为有时 alarmState 可以在特定条件下采用另一种状态,但是这种新状态就像 Active 状态
这是我的新EXPLAIN
id select_type table type possible_keys key key_len ref rows filtered Extra
1 PRIMARY k ALL imports_variablename_index,imports_alarmstate_index <null> <null> <null> 45474 50 Using where
1 PRIMARY i ref imports_devicetime_index,imports_variablename_index imports_devicetime_index 5 func 1 100 Using where
2 DEPENDENT SUBQUERY t ref imports_devicetime_index,imports_variablename_index,imports_alarmstate_index imports_variablename_index 1022 Alarms.k.variableName 29 50 Using where
当我执行查询时,我得到了
34567 rows retrieved starting from 1 in 3 m 26 s 135 ms (execution: 158 ms, fetching: 3 m 25 s 977 ms)
我觉得 3 分钟有点长,不是吗?还有其他优化或建议吗?
谢谢!
【问题讨论】:
-
哪个 MySQL/MariaDB 版本?
SELECT VERSION(); -
另外关于性能的问题还应该包括查询中涉及的每个表的表结构(
SHOW CREATE TABLE table)。还有一个EXPLAIN query输出 -
看起来像一个“groupwise-max”问题。请参阅添加的标签进行优化。
-
如前所述,为所有 3 列添加覆盖索引(即,1 个索引包含所有 3 列)。通常 MySQL 不会组合单独的索引,因此您需要 1 个索引来覆盖所有列。并且删除子查询可能会有很大帮助(它必须为它尝试加入的每一行执行该子查询)。
标签: mysql request mariadb query-performance groupwise-maximum