Här är en snabb prestandajämförelse för de frågor som nämns i det här inlägget.
Nuvarande inställningar:
Tabellen core_message
har 10 904 283 rader och det finns 60 740 rader i test_boats
(eller 60 740 distinkta mmsi i core_message
).
Och jag använder PostgreSQL 11.5
Fråga med enbart index-skanning:
1) med DISTINCT ON
:
SELECT DISTINCT ON (mmsi) mmsi
FROM core_message;
2) med RECURSIVE
med LATERAL
:
WITH RECURSIVE cte AS (
(
SELECT mmsi
FROM core_message
ORDER BY mmsi
LIMIT 1
)
UNION ALL
SELECT m.*
FROM cte c
CROSS JOIN LATERAL (
SELECT mmsi
FROM core_message
WHERE mmsi > c.mmsi
ORDER BY mmsi
LIMIT 1
) m
)
TABLE cte;
3) Använda en extra tabell med LATERAL
:
SELECT a.mmsi
FROM test_boats a
CROSS JOIN LATERAL(
SELECT b.time
FROM core_message b
WHERE a.mmsi = b.mmsi
ORDER BY b.time DESC
LIMIT 1
) b;
Frågan använder inte enbart indexsökning:
4) med DISTINCT ON
med mmsi,time DESC
INDEX
:
SELECT DISTINCT ON (mmsi) *
FROM core_message
ORDER BY mmsi, time desc;
5) med DISTINCT ON
med bakåt mmsi,time
UNIQUE CONSTRAINT
:
SELECT DISTINCT ON (mmsi) *
FROM core_message
ORDER BY mmsi desc, time desc;
6) med RECURSIVE
med LATERAL
och mmsi,time DESC
INDEX
:
WITH RECURSIVE cte AS (
(
SELECT *
FROM core_message
ORDER BY mmsi , time DESC
LIMIT 1
)
UNION ALL
SELECT m.*
FROM cte c
CROSS JOIN LATERAL (
SELECT *
FROM core_message
WHERE mmsi > c.mmsi
ORDER BY mmsi , time DESC
LIMIT 1
) m
)
TABLE cte;
7) med RECURSIVE
med LATERAL
och bakåt mmsi,time
UNIQUE CONSTRAINT
:
WITH RECURSIVE cte AS (
(
SELECT *
FROM core_message
ORDER BY mmsi DESC , time DESC
LIMIT 1
)
UNION ALL
SELECT m.*
FROM cte c
CROSS JOIN LATERAL (
SELECT *
FROM core_message
WHERE mmsi < c.mmsi
ORDER BY mmsi DESC , time DESC
LIMIT 1
) m
)
TABLE cte;
8) Använda en extra tabell med LATERAL
:
SELECT b.*
FROM test_boats a
CROSS JOIN LATERAL(
SELECT b.*
FROM core_message b
WHERE a.mmsi = b.mmsi
ORDER BY b.time DESC
LIMIT 1
) b;
Använda en dedikerad tabell för det senaste meddelandet:
9) Här är min första lösning, med hjälp av en distinkt tabell med endast det sista meddelandet. Den här tabellen fylls i när nya meddelanden kommer men kan också skapas så här :
CREATE TABLE core_shipinfos AS (
WITH RECURSIVE cte AS (
(
SELECT *
FROM core_message
ORDER BY mmsi DESC , time DESC
LIMIT 1
)
UNION ALL
SELECT m.*
FROM cte c
CROSS JOIN LATERAL (
SELECT *
FROM core_message
WHERE mmsi < c.mmsi
ORDER BY mmsi DESC , time DESC
LIMIT 1
) m
)
TABLE cte);
Då är begäran om att få det senaste meddelandet så enkelt:
SELECT * FROM core_shipinfos;
Resultat :
Genomsnitt av flera frågor (cirka 5 för den snabba):
1) 9146 ms
2) 728 ms
3) 498 ms
4) 51488 ms
5) 54764 ms
6) 729 ms
7) 778 ms
8) 516 ms
9) 15 ms
Slutsats:
Jag kommer inte att kommentera den dedikerade tabelllösningen och kommer att behålla den till slutet.
Tilläggstabellen (test_boats
) lösningen är definitivt vinnaren här men RECURSIVE
lösningen är också ganska effektiv.
Det finns ett stort gap i prestanda för DISTINCT ON
använder endast index-skanning och den som inte använder den, men prestandavinsten är ganska liten för den andra effektiva frågan.
Detta är vettigt eftersom den största förbättringen dessa frågor ger är det faktum att de inte behöver gå över hela core_message
tabell men bara på en delmängd av den unika mmsi
som är betydligt mindre (60K+) jämfört med core_message
bordsstorlek (10M+)
Som en ytterligare notering verkar det inte vara någon betydande förbättring av prestanda för frågor som använder UNIQUE CONSTRAINT
om jag släpper mmsi,time DESC
INDEX
. Men att släppa det indexet kommer naturligtvis att spara lite utrymme (det här indexet tar för närvarande 328 MB)
Om den dedikerade bordslösningen:
Varje meddelande lagras i core_message
Tabellen innehåller både positionsinformation (position, hastighet, kurs, etc.) OCH fartygsinformation (namn, anropssignal, dimensioner, etc.), såväl som fartygsidentifierare (mmsi).
För att ge lite mer bakgrund om vad jag faktiskt försöker göra:Jag implementerar en backend för att lagra meddelanden som sänds ut av fartyg via AIS-protokoll .
Som sådan, varje unik mmsi jag fick, fick jag det via detta protokoll. Det är inte en fördefinierad lista. Det fortsätter att lägga till nytt MMSI tills jag fick alla fartyg i världen med AIS.
I det sammanhanget är en dedikerad tabell med fartygsinformation som det senaste mottagna meddelandet vettigt.
Jag skulle kunna undvika att använda en sådan tabell som vi har sett med RECURSIVE
lösning, men... en dedikerad tabell är fortfarande 50 gånger snabbare än denna RECURSIVE
lösning.
Den dedikerade tabellen liknar faktiskt test_boat
tabell, med mer information än bara mmsi
fält. Som det är, att ha en tabell med mmsi
enda fält eller en tabell med alla sista uppgifterna i core_message
tabell lägga till samma komplexitet till min ansökan.
I slutändan tror jag att jag kommer att gå för detta dedikerade bord. Det kommer att ge mig oslagbar hastighet och jag kommer fortfarande att ha möjligheten att använda LATERAL
knep på core_message
, vilket ger mig mer flexibilitet.