Berikut adalah perbandingan kinerja cepat untuk kueri yang disebutkan dalam pos ini.
Penyiapan saat ini :
Tabel core_message
memiliki 10.904.283 baris dan ada 60.740 baris di test_boats
(atau 60.740 mmsi berbeda dalam core_message
).
Dan saya menggunakan PostgreSQL 11.5
Kueri menggunakan pemindaian hanya indeks :
1) menggunakan DISTINCT ON
:
SELECT DISTINCT ON (mmsi) mmsi
FROM core_message;
2) menggunakan RECURSIVE
dengan 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) Menggunakan tabel tambahan dengan 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;
Kueri tidak menggunakan pemindaian hanya indeks :
4) menggunakan DISTINCT ON
dengan mmsi,time DESC
INDEX
:
SELECT DISTINCT ON (mmsi) *
FROM core_message
ORDER BY mmsi, time desc;
5) menggunakan DISTINCT ON
dengan mundur mmsi,time
UNIQUE CONSTRAINT
:
SELECT DISTINCT ON (mmsi) *
FROM core_message
ORDER BY mmsi desc, time desc;
6) menggunakan RECURSIVE
dengan LATERAL
dan 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) menggunakan RECURSIVE
dengan LATERAL
dan mundur 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) Menggunakan tabel tambahan dengan 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;
Menggunakan tabel khusus untuk pesan terakhir:
9) Inilah solusi awal saya, menggunakan tabel berbeda dengan hanya pesan terakhir. Tabel ini diisi saat pesan baru tiba tetapi juga dapat dibuat seperti ini :
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);
Maka permintaan untuk mendapatkan pesan terbaru sesederhana itu :
SELECT * FROM core_shipinfos;
Hasil :
Rata-rata beberapa kueri (sekitar 5 untuk kueri cepat):
1) 9146 md
2) 728 md
3) 498 md
4) 51488 md
5) 54764 md
6) 729 md
7) 778 md
8) 516 md
9) 15 md
Kesimpulan:
Saya tidak akan mengomentari solusi tabel khusus, dan akan menyimpannya sampai akhir.
Tabel tambahan (test_boats
) solusi jelas merupakan pemenang di sini tetapi RECURSIVE
solusinya juga cukup efisien.
Ada kesenjangan besar dalam kinerja untuk DISTINCT ON
menggunakan pemindaian indeks-saja dan yang tidak menggunakannya tetapi, peningkatan kinerjanya agak kecil untuk kueri efisien lainnya.
Ini masuk akal karena peningkatan besar yang dibawa oleh kueri tersebut adalah fakta bahwa mereka tidak perlu mengulang seluruh core_message
tabel tetapi hanya pada subset dari mmsi
yang unik yang secara signifikan lebih kecil (60K+) dibandingkan dengan core_message
ukuran meja (10M+)
Sebagai catatan tambahan, tampaknya tidak ada peningkatan kinerja yang signifikan untuk kueri menggunakan UNIQUE CONSTRAINT
jika saya menjatuhkan mmsi,time DESC
INDEX
. Tetapi menghapus indeks itu tentu saja akan menghemat ruang saya (indeks ini saat ini membutuhkan 328MB)
Tentang solusi tabel khusus:
Setiap pesan disimpan di core_message
tabel memuat informasi posisi (posisi, kecepatan, pos, dll.) DAN informasi kapal (nama, tanda panggil, dimensi, dll.), serta pengidentifikasi kapal (mmsi).
Untuk memberikan sedikit lebih banyak latar belakang tentang apa yang sebenarnya saya coba lakukan :Saya menerapkan backend untuk menyimpan pesan yang dipancarkan oleh kapal melalui protokol AIS .
Dengan demikian, setiap mmsi unik yang saya dapatkan, saya mendapatkannya melalui protokol ini. Ini bukan daftar yang telah ditentukan sebelumnya. Itu terus menambahkan MMSI baru sampai saya mendapatkan setiap kapal di dunia menggunakan AIS.
Dalam konteks itu, tabel khusus dengan informasi kapal sebagai pesan terakhir yang diterima masuk akal.
Saya dapat menghindari penggunaan tabel seperti yang telah kita lihat dengan RECURSIVE
solusi, tapi... tabel khusus masih 50x lebih cepat dari RECURSIVE
solusi.
Tabel khusus itu sebenarnya mirip dengan test_boat
tabel, dengan informasi lebih dari sekedar mmsi
bidang. Seperti itu, memiliki tabel dengan mmsi
hanya bidang atau tabel dengan setiap informasi terakhir dari core_message
tabel menambahkan kompleksitas yang sama ke aplikasi saya.
Pada akhirnya, saya pikir saya akan memilih meja khusus ini. Ini akan memberi saya kecepatan yang tidak ada duanya dan saya masih memiliki kemungkinan untuk menggunakan LATERAL
trik core_message
, yang akan memberi saya lebih banyak fleksibilitas.