Ето кратко сравнение на ефективността за заявките, споменати в тази публикация.
Текуща настройка:
Таблицата core_message
има 10 904 283 реда и има 60 740 реда в test_boats
(или 60 740 отделни mmsi в core_message
).
И аз използвам PostgreSQL 11.5
Запитване чрез сканиране само за индекс :
1) използвайки DISTINCT ON
:
SELECT DISTINCT ON (mmsi) mmsi
FROM core_message;
2) използване на RECURSIVE
с 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) Използване на допълнителна таблица с 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;
Заявката не използва сканиране само за индекс :
4) използване на DISTINCT ON
с mmsi,time DESC
INDEX
:
SELECT DISTINCT ON (mmsi) *
FROM core_message
ORDER BY mmsi, time desc;
5) използване на DISTINCT ON
с обратен mmsi,time
UNIQUE CONSTRAINT
:
SELECT DISTINCT ON (mmsi) *
FROM core_message
ORDER BY mmsi desc, time desc;
6) използване на RECURSIVE
с LATERAL
и 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) използване на RECURSIVE
с LATERAL
и назад 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) Използване на допълнителна таблица с 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;
Използване на специална таблица за последното съобщение:
9) Ето моето първоначално решение, използвайки отделна таблица само с последното съобщение. Тази таблица се попълва, когато пристигат нови съобщения, но може да се създаде и така:
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);
Тогава заявката за получаване на последното съобщение е толкова проста:
SELECT * FROM core_shipinfos;
Резултати:
Средна стойност на множество заявки (около 5 за бързата):
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
Заключение:
Няма да коментирам специалното решение за таблица и ще го запазя за края.
Допълнителната таблица (test_boats
) решението определено е победителят тук, но RECURSIVE
решението също е доста ефективно.
Има огромна разлика в производителността за DISTINCT ON
използвайки сканиране само за индекс и този, който не го използва, но печалбата в производителността е доста малка за другата ефективна заявка.
Това има смисъл, тъй като основното подобрение, което носят тези заявки, е фактът, че не е необходимо да преминават през целия core_message
таблица, но само на подмножество от уникалния mmsi
което е значително по-малко (60K+) в сравнение с core_message
размер на таблицата (10M+)
Като допълнителна бележка, не изглежда да има значително подобрение в производителността за заявките, използващи UNIQUE CONSTRAINT
ако пусна mmsi,time DESC
INDEX
. Но премахването на този индекс, разбира се, ще ми спести малко място (този индекс в момента заема 328MB)
Относно решението за специална маса:
Всяко съобщение, съхранено в core_message
Таблицата съдържа както позиционна информация (позиция, скорост, курс и т.н.), И информация за кораба (име, позивна, размери и т.н.), както и идентификатор на кораба (mmsi).
За да дам малко повече информация за това, което всъщност се опитвам да направя:внедрявам бекенд за съхраняване на съобщения, излъчвани от кораби чрез AIS протокол .
Като такъв, всеки уникален mmsi, който получих, го получих чрез този протокол. Това не е предварително определен списък. Продължава да добавя нови MMSI, докато не накарам всички кораби в света да използват AIS.
В този контекст специална таблица с информация за кораба като последно получено съобщение има смисъл.
Мога да избегна използването на такава таблица, каквато видяхме с RECURSIVE
решение, но... специална таблица все още е 50 пъти по-бърза от тази RECURSIVE
решение.
Тази специална таблица всъщност е подобна на test_boat
таблица, с повече информация от mmsi
поле. Както е, имайки таблица с mmsi
единствено поле или таблица с всяка последна информация от core_message
таблица добави същата сложност към моето приложение.
В крайна сметка мисля да се спра на тази специална маса. Това ще ми даде ненадмината скорост и все още ще имам възможността да използвам LATERAL
трик с core_message
, което ще ми даде повече гъвкавост.