Zde je rychlé srovnání výkonu pro dotazy uvedené v tomto příspěvku.
Aktuální nastavení:
Tabulka core_message
má 10 904 283 řádků a v test_boats
je 60 740 řádků (nebo 60 740 odlišných mmsi v core_message
).
A já používám PostgreSQL 11.5
Dotaz pomocí skenování pouze indexu:
1) pomocí DISTINCT ON
:
SELECT DISTINCT ON (mmsi) mmsi
FROM core_message;
2) pomocí RECURSIVE
s 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) Použití další tabulky s 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;
Dotaz nepoužívá skenování pouze na základě indexu:
4) pomocí DISTINCT ON
s mmsi,time DESC
INDEX
:
SELECT DISTINCT ON (mmsi) *
FROM core_message
ORDER BY mmsi, time desc;
5) pomocí DISTINCT ON
se zpětným mmsi,time
UNIQUE CONSTRAINT
:
SELECT DISTINCT ON (mmsi) *
FROM core_message
ORDER BY mmsi desc, time desc;
6) pomocí RECURSIVE
s LATERAL
a 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) pomocí RECURSIVE
s LATERAL
a zpětně 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) Použití další tabulky s 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;
Použití vyhrazené tabulky pro poslední zprávu:
9) Zde je moje počáteční řešení s použitím odlišné tabulky pouze s poslední zprávou. Tato tabulka se vyplňuje při příchodu nových zpráv, ale lze ji vytvořit také takto:
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);
Pak je požadavek na získání nejnovější zprávy tak jednoduchý:
SELECT * FROM core_shipinfos;
Výsledky:
Průměr více dotazů (asi 5 pro rychlý):
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
Závěr:
Řešení vyhrazené tabulky nebudu komentovat a nechám si to na konec.
Dodatečná tabulka (test_boats
) řešení je zde rozhodně vítězem, ale RECURSIVE
řešení je také docela efektivní.
U DISTINCT ON
existuje obrovská mezera ve výkonu používající pouze indexové skenování a ten, který jej nepoužívá, ale nárůst výkonu je u druhého efektivního dotazu poměrně malý.
To dává smysl, protože hlavním zlepšením, které tyto dotazy přinášejí, je skutečnost, že nemusí opakovat celou core_message
tabulce, ale pouze na podmnožině jedinečné mmsi
která je výrazně menší (60 000+) ve srovnání s core_message
velikost stolu (10M+)
Jako další poznámka se nezdá, že by došlo k výraznému zlepšení výkonu pro dotazy pomocí UNIQUE CONSTRAINT
pokud vypustím mmsi,time DESC
INDEX
. Ale zrušením tohoto indexu mi samozřejmě ušetří místo (tento index v současné době zabírá 328 MB)
O řešení vyhrazené tabulky:
Každá zpráva uložená v core_message
tabulka obsahuje jak poziční informace (poloha, rychlost, kurz atd.) A informace o lodi (jméno, volací znak, rozměry atd.), tak i identifikátor lodi (mmsi).
Abych uvedl trochu více pozadí o tom, co se vlastně snažím dělat:implementuji backend pro ukládání zpráv vysílaných loděmi přes protokol AIS .
Každý jedinečný mmsi, který jsem dostal, jsem dostal prostřednictvím tohoto protokolu. Nejedná se o předem definovaný seznam. Neustále přidává nové MMSI, dokud nezískám všechny lodě na světě pomocí AIS.
V tomto kontextu dává smysl vyhrazená tabulka s informacemi o lodi jako poslední přijatá zpráva.
Mohl bych se vyhnout použití takové tabulky, jak jsme viděli u RECURSIVE
řešení, ale... vyhrazená tabulka je stále 50x rychlejší než tato RECURSIVE
řešení.
Tato vyhrazená tabulka je ve skutečnosti podobná test_boat
tabulka s více informacemi než jen mmsi
pole. Jak to je, mít tabulku s mmsi
pouze pole nebo tabulka s posledními informacemi core_message
tabulka přidává mé aplikaci stejnou složitost.
Nakonec si myslím, že půjdu pro tento vyhrazený stůl. Poskytne mi to nepřekonatelnou rychlost a stále budu mít možnost používat LATERAL
trik na core_message
, což mi poskytne větší flexibilitu.