优化大表的联合查询
Optimized Joined query for large tables
我有一个分析事件日志的项目。
目标是基于父 table(Table 1) 以 30 秒为间隔比较时间戳。
Table1
id command datetime
--------------------- ------------------- ----------------------
1 cat 2018-11-03 23:29:31
2 nmap 2018-11-03 23:22:32
3 ssh 2018-11-03 23:22:40
事件日志Tables
id raw datetime
--------------- ------------ --------------------
1 text 2018-11-03 23:23:10
2 text 2018-11-03 23:23:20
所以基于 table1 日期时间,我想输出在一个时间间隔内触发的所有事件日志,例如 30 秒
现在我使用这个左连接语句,它适用于小尺寸 tables(小于 1 MB):
SELECT table1.command as Command,table2.raw as Nginx,Table3.raw as Apache
FROM Table1
left join Table2
on Table1.datetime::timestamp>= Table2.datetime::timestamp - interval '30 seconds'
and Table1.datetime::timestamp<= Table2.datetime::timestamp + interval '30 seconds'
left join
Table3 on table1.datetime::timestamp>= Table3.datetime::timestamp - interval '1 seconds'
and Table1.datetime::timestamp<= Table3.datetime::timestamp + interval '30 seconds'
它工作正常并给了我想要的输出,问题是我有 tables 和 200K+ 行并且执行查询需要很多时间,
这不是真正快速的关键,但例如,如果我加入 3 tables (示例中的 Table1)和其他 2 table 包含 200k+ 行查询时间是超过 5 个小时。
Bellow 是一个 Explain 语句来得到一个想法:
Nested Loop Left Join (cost=0.00..930881273799.88 rows=1202913100267 width=1819)
Join Filter: ((b1.datetime >= (s1.datetime - '00:00:30'::interval)) AND (b1.datetime <= (s1.datetime + '00:00:30'::interval)))
-> Nested Loop Left Join (cost=0.00..60290628.13 rows=36384533 width=1343)
Join Filter: ((b1.datetime <= s2.datetime) AND (b1.datetime >= (s2.datetime - '00:00:30'::interval)))
-> Seq Scan on bash b1 (cost=0.00..75.13 rows=4013 width=34)
-> Materialize (cost=0.00..28885.00 rows=81600 width=1317)
-> Seq Scan on suricata__alert s2 (cost=0.00..15089.00 rows=81600 width=1317)
-> Materialize (cost=0.00..43131.25 rows=297550 width=492)
-> Seq Scan on suricata__http s1 (cost=0.00..22755.50 rows=297550 width=492)
我可以优化Join语句吗?我是否应该采用不同的方法来解决问题(使用视图、索引?)
仅当 WHERE
条件的形式为
时才能使用索引
<em><索引表达式></em> <em><运算符></em> <em><常量></em>
其中 <em><operator></em>
必须在定义索引的运算符 class 中,并且 <em><constant></em>
不一定是常量,但在索引扫描期间具有固定值。
因此您应该将查询重写为
SELECT table1.command AS Command,
table2.raw AS Nginx,
table3.raw AS Apache
FROM Table1
LEFT JOIN table2
ON table2.datetime::timestamp
BETWEEN table1.datetime::timestamp - interval '30 seconds'
AND table1.datetime::timestamp + interval '30 seconds'
LEFT JOIN table3
ON table3.datetime::timestamp
BETWEEN table1.datetime::timestamp - interval '30 seconds'
AND table1.datetime::timestamp + interval '30 seconds';
确保 table2
和 table3
的 datetime
列上有索引。
这可能仍然很慢,除非您使用 WHERE
条件限制要从 table1
检索的行数。
我有一个分析事件日志的项目。 目标是基于父 table(Table 1) 以 30 秒为间隔比较时间戳。
Table1
id command datetime
--------------------- ------------------- ----------------------
1 cat 2018-11-03 23:29:31
2 nmap 2018-11-03 23:22:32
3 ssh 2018-11-03 23:22:40
事件日志Tables
id raw datetime
--------------- ------------ --------------------
1 text 2018-11-03 23:23:10
2 text 2018-11-03 23:23:20
所以基于 table1 日期时间,我想输出在一个时间间隔内触发的所有事件日志,例如 30 秒
现在我使用这个左连接语句,它适用于小尺寸 tables(小于 1 MB):
SELECT table1.command as Command,table2.raw as Nginx,Table3.raw as Apache
FROM Table1
left join Table2
on Table1.datetime::timestamp>= Table2.datetime::timestamp - interval '30 seconds'
and Table1.datetime::timestamp<= Table2.datetime::timestamp + interval '30 seconds'
left join
Table3 on table1.datetime::timestamp>= Table3.datetime::timestamp - interval '1 seconds'
and Table1.datetime::timestamp<= Table3.datetime::timestamp + interval '30 seconds'
它工作正常并给了我想要的输出,问题是我有 tables 和 200K+ 行并且执行查询需要很多时间, 这不是真正快速的关键,但例如,如果我加入 3 tables (示例中的 Table1)和其他 2 table 包含 200k+ 行查询时间是超过 5 个小时。
Bellow 是一个 Explain 语句来得到一个想法:
Nested Loop Left Join (cost=0.00..930881273799.88 rows=1202913100267 width=1819)
Join Filter: ((b1.datetime >= (s1.datetime - '00:00:30'::interval)) AND (b1.datetime <= (s1.datetime + '00:00:30'::interval)))
-> Nested Loop Left Join (cost=0.00..60290628.13 rows=36384533 width=1343)
Join Filter: ((b1.datetime <= s2.datetime) AND (b1.datetime >= (s2.datetime - '00:00:30'::interval)))
-> Seq Scan on bash b1 (cost=0.00..75.13 rows=4013 width=34)
-> Materialize (cost=0.00..28885.00 rows=81600 width=1317)
-> Seq Scan on suricata__alert s2 (cost=0.00..15089.00 rows=81600 width=1317)
-> Materialize (cost=0.00..43131.25 rows=297550 width=492)
-> Seq Scan on suricata__http s1 (cost=0.00..22755.50 rows=297550 width=492)
我可以优化Join语句吗?我是否应该采用不同的方法来解决问题(使用视图、索引?)
仅当 WHERE
条件的形式为
<em><索引表达式></em> <em><运算符></em> <em><常量></em>
其中 <em><operator></em>
必须在定义索引的运算符 class 中,并且 <em><constant></em>
不一定是常量,但在索引扫描期间具有固定值。
因此您应该将查询重写为
SELECT table1.command AS Command,
table2.raw AS Nginx,
table3.raw AS Apache
FROM Table1
LEFT JOIN table2
ON table2.datetime::timestamp
BETWEEN table1.datetime::timestamp - interval '30 seconds'
AND table1.datetime::timestamp + interval '30 seconds'
LEFT JOIN table3
ON table3.datetime::timestamp
BETWEEN table1.datetime::timestamp - interval '30 seconds'
AND table1.datetime::timestamp + interval '30 seconds';
确保 table2
和 table3
的 datetime
列上有索引。
这可能仍然很慢,除非您使用 WHERE
条件限制要从 table1
检索的行数。