I’m using InfluxDB 2.1.1 OSS.
The problem I want to solve is
- data is organized by a tag
host
and has a fieldoffabs
. - one node is declared as reference
- for all other nodes the difference of their
offabs
to the value of the reference node is calculated
A clear case of two queries, first the reference node, second all other nodes, join()
them by _time
, and calculate the difference with map()
.
Should be straight forward Flux
.
The query time was much higher than expected.
So I used the python influxdb_client
and looked into profile data, with puzzling results.
The investigated query was
v = {timeRangeStart: -14d, timeRangeStop: -0d, windowPeriod: 10m}
offmst = from(bucket: "dca")
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r["_measurement"] == "TfcMonitor")
|> filter(fn: (r) => r["host"] == "cbmin00y")
|> filter(fn: (r) => r["_field"] == "offabs")
|> drop(columns: ["_measurement","_field","_start","_stop","host","oid"])
|> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: true)
|> rename(columns: {_value: "off_mst"})
offend = from(bucket: "dca")
|> range(start: v.timeRangeStart, stop: v.timeRangeStop)
|> filter(fn: (r) => r["_measurement"] == "TfcMonitor")
|> filter(fn: (r) => r["host"] != "cbmin00y")
|> filter(fn: (r) => r["_field"] == "offabs")
|> aggregateWindow(every: v.windowPeriod, fn: mean, createEmpty: true)
|> drop(columns: ["_measurement","_field","_start","_stop"])
|> rename(columns: {_value: "off_end"})
join(tables: {mst:offmst, end:offend}, on: ["_time"])
|> map(fn: (r) => ({ r with _value: r.off_end - r.off_mst}))
|> drop(columns: ["off_mst","off_end"])
|> yield()
That selects 2017 rows for offmst
and 6 tables with 2017 rows for offend
.
I’ve also looked at windowPeriod
of 20m
and 60m
, that gives 1009 and 337 rows, respectively,
From the profiling data I extracted
- total CPU time
- maximal allocated memory
- CPU time of
*universe.mergeJoinTransformation
Wind #rec cpu_tot mem_tot cpu_join
10m 2017 3.746 665990080 3.514
20m 1009 1.148 167680832 1.000
60m 337 0.279 19480896 0.188
Both the CPU time and the memory grow with the square or the row count.
I’m really puzzled.
Why does a join
of two tables by a unique key (here _time
) have a complexity of O(N*N)
?
I’d expect that both tables are first sorted (O(N*lnN)
) and than merged (O(N+N)
).
The _time
columns are sorted, and even if they weren’t, it’s trivial to build a sorted index table.
Even the operation primitive is called mergeJoinTransformation
.
So I see no reason for an O(N*N)
complexity here. But that’s what I get.
What am I overlooking here ?
Should the query be written differently ?
Any help/hint highly welcome.