Her er en hurtig ydelsessammenligning for de forespørgsler, der er nævnt i dette indlæg.
Nuværende opsætning:
Tabellen core_message
har 10.904.283 rækker, og der er 60.740 rækker i test_boats
(eller 60.740 forskellige mmsi i core_message
).
Og jeg bruger PostgreSQL 11.5
Forespørgsel ved hjælp af kun indeksscanning:
1) ved at bruge DISTINCT ON
:
SELECT DISTINCT ON (mmsi) mmsi
FROM core_message;
2) ved at bruge 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) Brug af en ekstra tabel 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;
Forespørgsel bruger ikke kun indeksscanning :
4) ved at bruge DISTINCT ON
med mmsi,time DESC
INDEX
:
SELECT DISTINCT ON (mmsi) *
FROM core_message
ORDER BY mmsi, time desc;
5) ved at bruge DISTINCT ON
med baglæns mmsi,time
UNIQUE CONSTRAINT
:
SELECT DISTINCT ON (mmsi) *
FROM core_message
ORDER BY mmsi desc, time desc;
6) ved at bruge RECURSIVE
med LATERAL
og 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) ved at bruge RECURSIVE
med LATERAL
og baglæns 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) Brug af en ekstra tabel 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;
Brug af en dedikeret tabel til den sidste besked:
9) Her er min første løsning ved at bruge en særskilt tabel med kun den sidste besked. Denne tabel udfyldes efterhånden som nye meddelelser ankommer, men kan også oprettes som sådan :
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);
Så er anmodningen om at få den seneste besked så simpel som den :
SELECT * FROM core_shipinfos;
Resultater:
Gennemsnit af flere forespørgsler (omkring 5 for den hurtige):
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
Konklusion:
Jeg vil ikke kommentere på den dedikerede bordløsning og vil beholde den til sidst.
Den ekstra tabel (test_boats
) løsningen er absolut vinderen her, men den RECURSIVE
løsningen er også ret effektiv.
Der er et stort hul i ydeevnen for DISTINCT ON
bruger kun indeksscanning, og den der ikke bruger det, men ydeevneforøgelsen er ret lille for den anden effektive forespørgsel.
Dette giver mening, da den største forbedring, disse forespørgsler medfører, er det faktum, at de ikke behøver at gå over hele core_message
tabel, men kun på en delmængde af den unikke mmsi
der er væsentligt mindre (60K+) sammenlignet med core_message
bordstørrelse (10M+)
Som en yderligere bemærkning, synes der ikke at være væsentlig forbedring i ydeevnen for forespørgsler, der bruger UNIQUE CONSTRAINT
hvis jeg dropper mmsi,time DESC
INDEX
. Men at droppe det indeks vil selvfølgelig spare mig noget plads (dette indeks tager i øjeblikket 328 MB)
Om den dedikerede bordløsning:
Hver meddelelse er gemt i core_message
tabel indeholder både positionsoplysninger (position, hastighed, kurs osv.) OG skibsinformationer (navn, kaldesignal, dimensioner osv.), såvel som skibsidentifikator (mmsi).
For at give lidt mere baggrund om, hvad jeg faktisk forsøger at gøre:Jeg implementerer en backend til at gemme beskeder udsendt af skibe via AIS-protokol .
Som sådan, hver unik mmsi, jeg fik, fik jeg den via denne protokol. Det er ikke en foruddefineret liste. Det bliver ved med at tilføje nyt MMSI, indtil jeg fik alle skibe i verden ved hjælp af AIS.
I den sammenhæng giver en dedikeret tabel med skibsinformationer som sidst modtaget besked mening.
Jeg kunne undgå at bruge sådan en tabel, som vi har set med RECURSIVE
løsning, men... en dedikeret tabel er stadig 50 gange hurtigere end denne RECURSIVE
løsning.
Den dedikerede tabel ligner faktisk test_boat
tabel med flere oplysninger end blot mmsi
Mark. Som det er, at have en tabel med mmsi
eneste felt eller en tabel med hver sidste information om core_message
tabel tilføje den samme kompleksitet til min ansøgning.
I sidste ende tror jeg, at jeg vil gå efter dette dedikerede bord. Det vil give mig uovertruffen hastighed, og jeg vil stadig have mulighed for at bruge LATERAL
trick på core_message
, hvilket vil give mig mere fleksibilitet.