Mysql 改进SQL查询以获取特定日期的传感器事件&;设备类型
我们有一个事件mysql表,其中存储了从不同类型的传感器生成的事件。下面是同一个表的CREATETABLE查询Mysql 改进SQL查询以获取特定日期的传感器事件&;设备类型,mysql,Mysql,我们有一个事件mysql表,其中存储了从不同类型的传感器生成的事件。下面是同一个表的CREATETABLE查询 CREATE TABLE `event` ( `id` varchar(36) NOT NULL, `device_id` varchar(36) NOT NULL, `device_type` varchar(45) NOT NULL, `data` text NOT NULL, `created_at` datetime NOT NULL, PRIMAR
CREATE TABLE `event` (
`id` varchar(36) NOT NULL,
`device_id` varchar(36) NOT NULL,
`device_type` varchar(45) NOT NULL,
`data` text NOT NULL,
`created_at` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id_UNIQUE` (`id`),
KEY `fk_event_device_idx` (`device_id`),
KEY `event_device_type` (`device_type`),
KEY `event_created_at_idx` (`created_at`),
CONSTRAINT `fk_event_device` FOREIGN KEY (`device_id`) REFERENCES `device` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
我们有一个来自设备表的设备id外键,设备表有一个来自区域表的区域id外键
我们希望获取特定区域和设备类型(例如THL传感器)的事件,以确定日期(例如2017-02-26)。下面是我正在运行的查询,以执行相同的操作
select e.data from event e
left join device d on d.id = e.device_id
where d.type = 'mdc' and d.zone_id = 'e451b2a1-5f6c-4a75-8038-30854926a9c0' and DATE(e.created_at) = '2018-03-01';
+----+-------------+-------+------------+------+--------------------------------------+---------------------+---------+--------------+------+----------+------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+--------------------------------------+---------------------+---------+--------------+------+----------+------------------------------------+
| 1 | SIMPLE | d | NULL | ref | PRIMARY,id_UNIQUE,fk_device_zone_idx | fk_device_zone_idx | 110 | const | 23 | 10.00 | Using index condition; Using where |
| 1 | SIMPLE | e | NULL | ref | fk_event_device_idx | fk_event_device_idx | 110 | senzopt.d.id | 197 | 100.00 | Using where |
+----+-------------+-------+------------+------+--------------------------------------+---------------------+---------+--------------+------+----------+------------------------------------+
解释计划给出了以下相同的结果
select e.data from event e
left join device d on d.id = e.device_id
where d.type = 'mdc' and d.zone_id = 'e451b2a1-5f6c-4a75-8038-30854926a9c0' and DATE(e.created_at) = '2018-03-01';
+----+-------------+-------+------------+------+--------------------------------------+---------------------+---------+--------------+------+----------+------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+--------------------------------------+---------------------+---------+--------------+------+----------+------------------------------------+
| 1 | SIMPLE | d | NULL | ref | PRIMARY,id_UNIQUE,fk_device_zone_idx | fk_device_zone_idx | 110 | const | 23 | 10.00 | Using index condition; Using where |
| 1 | SIMPLE | e | NULL | ref | fk_event_device_idx | fk_event_device_idx | 110 | senzopt.d.id | 197 | 100.00 | Using where |
+----+-------------+-------+------------+------+--------------------------------------+---------------------+---------+--------------+------+----------+------------------------------------+
事件表中的记录总数约为500万条,执行上述查询并提供结果大约需要1秒。我希望提高sql执行时间。我们需要同样的建议。请让我知道我能做的一切
注意:我知道我也应该搬到NOSQL(Kafka/Cassandra/Spark)。为此,我们也在并行工作。但是,我希望改进查询,以便在当前环境下更好地为我的客户服务。以下是您的查询,以更易读的格式重复:
SELECT
e.data
FROM event e
LEFT JOIN device d
ON d.id = e.device_id
WHERE
d.type = 'mdc' AND
d.zone_id = 'e451b2a1-5f6c-4a75-8038-30854926a9c0' AND
DATE(e.created_at) = '2018-03-01';
我们可以通过添加适当的索引来提高此查询的性能,也可以对其进行重新表述
首先,您可以在(类型,区域id)
上的设备
表中创建一个复合索引。这对WHERE
子句应该有帮助。请注意,假设device.id
是该表的主键,它应该已经被索引,这意味着您的LEFT JOIN
条件应该是最佳的
您还可以在事件
表中的事件.在
列中创建索引。但为了利用这一点,我们必须重写不可出售的条件其中DATE(e.created_at)='2018-03-01'
:
WHERE e.created_at >= '2018-03-01' AND e.created_at < '2018-03-02'
就查询而言,WHERE子句中的谓词否定左连接的外部性。也就是说,
LEFT
关键字是多余的
在函数中包装列将禁用MySQL执行范围扫描操作的能力。状况
DATE(e.created_date) = '2018-03-01'
导致MySQL为表中的每一行(或者至少是尚未被其他谓词消除的每一行)计算左侧的表达式,然后将结果与右侧的文本进行比较
为了能够有效地使用索引,最好将其写入引用裸列
e.created_date >= '2018-03-01'
AND e.created_date < '2018-03-01' + INTERVAL 1 DAY
创建该索引后,我们可以在设备id上删除冗余索引。。。前导列为device_id的新索引足以支持外键约束
除非对事件(id)]上的冗余id\u UNIQUE
索引[有具体原因,否则我将删除它
不需要强制唯一性,主键约束已经做到了这一点。诚然,这可能是为一个有利的边缘情况创建的(在这种情况下,它是一个特定查询的覆盖索引。如果不这样做,它就不是必需的,并且会影响DML性能)
DROP INDEX id_UNIQUE ON event ;
对于这个查询,设备
表上的有益索引是
... ON `event` (`device_id`, `created_date`)
`ON device (zone_id, device)`
我们希望MySQL在Extra
列的解释输出中显示“使用索引”
有了合适的索引,我会把查询写得更清楚一点,消除多余的LEFT
关键字
SELECT e.data
FROM event e
JOIN device d
ON d.id = e.device_id
AND d.type = 'mdc'
AND d.zone_id = 'e451b2a1-5f6c-4a75-8038-30854926a9c0'
WHERE e.created_at >= '2018-03-01'
AND e.created_at < '2018-03-01' + INTERVAL 1 DAY
选择e.data
来自事件e
连接设备d
在d.id=e.device\u id上
和d.type='mdc'
和d.zone_id='e451b2a1-5f6c-4a75-8038-30854926a9c0'
其中e.创建于>='2018-03-01'
在<'2018-03-01'+间隔1天时创建e.U
为什么要在查询中保留LEFT
关键字?WHERE子句中d
列上的相等谓词意味着不会返回空值。结果相当于内部联接。@tim biegeleisen非常感谢!查询有了很大改进。为什么要从查询中删除LEFT-join
d呈现您的建议不可行。@TimBiegeleisen左
关键字是假的…查询正在进行内部联接。我在查询实际进行外部联接时指定外部联接,而不是在其进行内部联接时指定外部联接。在不执行外部联接时指定外部联接对我来说没有意义。另外,基于pa根据我的经验,我不会依赖SQL优化器来认识到它是一个内部连接,并且可能会将自己约束到一个效率较低的计划中,以支持不必要的外部连接。(我知道优化器现在比5.1及更早版本中的优化器要好得多,但仍然……)你说得对,很好。我想主要的区别是,强制左连接会强制扫描一个表而不是另一个表,而实际上扫描另一侧可能会更快。@TimBiegeleisen:没错。但在5.6及更高版本中,这对优化器来说可能不是问题……它可能会将其视为内部连接,但为什么要冒险呢另一个大问题是未来的读者,避免不必要的观察。@spencer7593非常感谢!这个查询改进了很多。