Hier is een snelle prestatievergelijking voor de zoekopdrachten die in dit bericht worden genoemd.
Huidige instellingen:
De tabel core_message
heeft 10.904.283 rijen en er zijn 60.740 rijen in test_boats
(of 60.740 verschillende mmsi in core_message
).
En ik gebruik PostgreSQL 11.5
Query met alleen-index scan :
1) met DISTINCT ON
:
SELECT DISTINCT ON (mmsi) mmsi
FROM core_message;
2) met behulp van RECURSIVE
met 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) Een extra tabel gebruiken met 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;
Query zonder alleen-indexscan:
4) met behulp van DISTINCT ON
met mmsi,time DESC
INDEX
:
SELECT DISTINCT ON (mmsi) *
FROM core_message
ORDER BY mmsi, time desc;
5) met DISTINCT ON
met achterwaartse mmsi,time
UNIQUE CONSTRAINT
:
SELECT DISTINCT ON (mmsi) *
FROM core_message
ORDER BY mmsi desc, time desc;
6) met behulp van RECURSIVE
met LATERAL
en 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) met behulp van RECURSIVE
met LATERAL
en achteruit 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) Een extra tabel gebruiken met 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;
Een speciale tabel gebruiken voor het laatste bericht:
9) Hier is mijn eerste oplossing, met behulp van een aparte tabel met alleen het laatste bericht. Deze tabel wordt gevuld als er nieuwe berichten binnenkomen, maar kan ook als volgt worden aangemaakt:
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);
Dan is het verzoek om het laatste bericht te ontvangen zo simpel als dat:
SELECT * FROM core_shipinfos;
Resultaten:
Gemiddelde van meerdere zoekopdrachten (ongeveer 5 voor de snelle):
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
Conclusie:
Ik zal geen commentaar geven op de speciale tafeloplossing en zal die tot het einde bewaren.
De aanvullende tabel (test_boats
) oplossing is hier zeker de winnaar, maar de RECURSIVE
oplossing is ook behoorlijk efficiënt.
Er is een enorm prestatieverschil voor de DISTINCT ON
met alleen-indexscan en degene die het niet gebruikt, maar de prestatiewinst is vrij klein voor de andere efficiënte zoekopdracht.
Dit is logisch, aangezien de belangrijkste verbetering die deze zoekopdrachten met zich meebrengen, het feit is dat ze niet het hele core_message
hoeven te doorlopen tabel, maar alleen op een subset van de unieke mmsi
dat is aanzienlijk kleiner (60K+) in vergelijking met de core_message
tafelgrootte (10M+)
Als aanvullende opmerking lijkt er geen significante verbetering in de prestaties te zijn voor de query's die de UNIQUE CONSTRAINT
gebruiken als ik de mmsi,time DESC
. laat vallen INDEX
. Maar als ik die index weglaat, bespaar ik natuurlijk wat ruimte (deze index neemt momenteel 328 MB in beslag)
Over de speciale tafeloplossing:
Elk bericht opgeslagen in de core_message
tabel bevat zowel positie-informatie (positie, snelheid, koers, enz.) EN scheepsinformatie (naam, roepnaam, afmetingen, enz.) als scheepsidentificatie (mmsi).
Om wat meer achtergrondinformatie te geven over wat ik eigenlijk probeer te doen:ik implementeer een backend om berichten op te slaan die door schepen worden verzonden via de AIS-protocol .
Dus elke unieke mmsi die ik kreeg, kreeg ik via dit protocol. Het is geen vooraf gedefinieerde lijst. Het blijft nieuwe MMSI toevoegen totdat ik alle schepen ter wereld met AIS heb gekregen.
In die context is een speciale tabel met scheepsinformatie als laatste ontvangen bericht zinvol.
Ik zou kunnen vermijden om zo'n tabel te gebruiken zoals we hebben gezien met de RECURSIVE
oplossing, maar... een speciale tafel is nog steeds 50x sneller dan deze RECURSIVE
oplossing.
Die speciale tabel is in feite vergelijkbaar met de test_boat
tabel, met meer informatie dan alleen de mmsi
veld. Zoals het is, een tabel hebben met mmsi
alleen veld of een tabel met alle laatste informatie van de core_message
tabel voeg dezelfde complexiteit toe aan mijn applicatie.
Uiteindelijk denk ik dat ik voor deze speciale tafel ga. Het geeft me een onverslaanbare snelheid en ik heb nog steeds de mogelijkheid om de LATERAL
te gebruiken truc op core_message
, wat me meer flexibiliteit geeft.