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.