提高时间序列数据的SQL查询性能

问题描述 投票:0回答:1

我正在 MySQL 中为 Grafana 仪表板编写查询,但仅此特定查询的性能非常差。 这是查询:

SELECT 
        br.reading
        ,CONCAT(ino.project,"_",SUBSTRING_INDEX(br.sensor,"_",-1)) AS new_metric
        ,ino.vessel
        ,ADDTIME(DATE('2023-01-01'),-TIMEDIFF(ino.date,br.datetime)) AS norm_date        
        ,br.sensor

FROM db.milestones br
 
INNER JOIN db.projects ino
        ON br.datetime BETWEEN ino.date AND ino.enddate

WHERE project IN ( 'project1','project2','project3','project4' ) 
AND br.sensor LIKE CONCAT(ino.vessel , '%')

EXPLAIN
输出:

| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows     | filtered | Extra                                          |
| -- | ----------- | ----- | ---------- | ---- | ------------- | --- | ------- | --- | -------- | -------- | ---------------------------------------------- |
| 1  | SIMPLE      | ino   |            | ALL  |               |     |         |     | 5        | 100.00   |                                                |
| 1  | SIMPLE      | br    |            | ALL  | idx_datetime  |     |         |     | 31865381 | 1.23     | Range checked for each record (index map: 0x2) |

创建表:

CREATE TABLE `projectdb` (
   `project` varchar(10) DEFAULT NULL,
   `date` datetime DEFAULT NULL,
   `enddate` datetime DEFAULT ((`date` + interval 3 day)),
   `vessel` varchar(45) DEFAULT NULL,
   UNIQUE KEY `project_UNIQUE` (`project`),
   KEY `idx_batch` (`project`) USING BTREE
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;


CREATE TABLE `milestones` (
   `id` int NOT NULL AUTO_INCREMENT,
   `datetime` datetime DEFAULT CURRENT_TIMESTAMP,
   `sensor` varchar(20) DEFAULT NULL,
   `value` decimal(10,4) DEFAULT NULL,
   `monitor` tinyint DEFAULT NULL,
   PRIMARY KEY (`idbioreactors`),
   KEY `idx_datetime` (`datetime` DESC) USING BTREE,
   KEY `idx_sensors` (`sensor`) USING BTREE
 ) ENGINE=InnoDB AUTO_INCREMENT=31649068 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci

索引:

| Table      | Non_unique | Key_name       | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
| ---------- | ---------- | -------------- | ------------ | ----------- | --------- | ----------- | -------- | ------ | ---- | ---------- | ------- | ------------- | ------- | ---------- |
| projectdb  | 0          | project_UNIQUE | 1            | project     | A         | 4           |          |        | YES  | BTREE      |         |               | YES     |            |
| projectdb  | 1          | idx_project    | 1            | project     | A         | 5           |          |        | YES  | BTREE      |         |               | YES     |            |
| milestones | 0          | PRIMARY        | 1            | id          | A         | 31648118    |          |        |      | BTREE      |         |               | YES     |            |
| milestones | 1          | idx_datetime   | 1            | datetime    | D         | 1723411     |          |        | YES  | BTREE      |         |               | YES     |            |
| milestones | 1          | idx_sensors    | 1            | sensor      | A         | 21924       |          |        | YES  | BTREE      |         |               | YES     |            |

projectdb 表示例:

| project   | date             | enddate          | vessel |
| --------- | ---------------- | ---------------- | ------ |
| Project 1 | 24/11/2023 17:30 | 27/11/2023 17:30 | V1     |
| Project 2 | 17/11/2023 19:50 | 20/11/2023 19:50 | V1     |
| Project 3 | 27/10/2023 16:00 | 30/10/2023 16:00 | V2     |

里程碑表样本

| id       | datetime         | sensor   | reading |
| -------- | ---------------- | -------- | ------- |
| 22117821 | 10/10/2023 19:20 | V1_FT001 | 100     |
| 22118005 | 10/10/2023 19:21 | V2_FT001 | 120     |
| 22118189 | 10/10/2023 19:23 | V1_FT001 | 100     |
| 22117835 | 10/10/2023 19:20 | V3_FT001 | 105     |

查看执行计划,很明显

db.milestones.sensor
db.milestones.datetime
上的索引没有被使用。这意味着全表扫描需要遍历全部 3600 万行,这解释了性能不佳的原因。我假设因为这是时间序列数据,所以我有 81 个传感器一遍又一遍地重复,使得索引不太可能被使用。

有没有办法修改此查询以使其始终使用索引,或者如果我想提高查询速度,是否需要采取完全不同的方法?

我尝试在第一个连接中使用子查询在末尾包含

WHERE
子句,以在连接之前过滤掉不必要的数据。这种方法最终表现最差,耗时长达 2 分钟。

我尝试创建 db.milestones 表的视图来包含与

WHERE
子句进行比较的子字符串,因此我可以使用
=
而不是
LIKE
。这也没有达到预期,表现也很糟糕。

我尝试使用

FORCE INDEX
来使用索引,但这不起作用。

有趣的是,如果我减少

WHERE project IN...
中的条件,则使用
db.milestone.sensor
上的索引,并且处理时间会好得多。

mysql indexing time-series query-optimization
1个回答
0
投票

目前,你在

project
列上有两个索引,其中一个是唯一的,没有PK。您应该删除现有的两个索引并在
project
上添加 PK。

合理表现的关键是

(sensor, datetime)
上的复合键以及
vessel
sensor
之间的具体关系。

添加一个简单的连接表(

vessel_sensor
)将使世界变得不同:

船只 传感器
V1 V1_FT001
V2 V2_FT001
V3 V3_FT001

综合指数:

ALTER TABLE milestones ADD INDEX idx_sensor_datetime (sensor, datetime);

然后将连接表添加到现有查询中:

SELECT 
    m.reading,
    CONCAT(p.project, '_', SUBSTRING_INDEX(m.sensor, '_', -1)) AS new_metric,
    p.vessel,
    ADDTIME(DATE('2023-01-01'), -TIMEDIFF(p.date, m.datetime)) AS norm_date,
    m.sensor
FROM projects p
JOIN vessel_sensor vs
    ON vs.vessel = p.vessel
JOIN milestones m
    ON m.sensor = vs.sensor
    AND m.datetime BETWEEN p.date AND p.enddate
WHERE p.project IN ( 'project1', 'project2', 'project3', 'project4' );
© www.soinside.com 2019 - 2024. All rights reserved.