优化大表的联合查询

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';

确保 table2table3datetime 列上有索引。

这可能仍然很慢,除非您使用 WHERE 条件限制要从 table1 检索的行数。