Saya menggunakan postgres 9.4.
The messages
memiliki skema berikut: pesan milik FEED_ID, dan memiliki posted_at, juga pesan dapat memiliki pesan orang tua (dalam kasus balasan).
Table "public.messages"
Column | Type | Modifiers
------------------------------+-----------------------------+-----------
message_id | character varying(255) | not null
feed_id | integer |
parent_id | character varying(255) |
posted_at | timestamp without time zone |
share_count | integer |
Indexes:
"messages_pkey" PRIMARY KEY, btree (message_id)
"index_messages_on_feed_id_posted_at" btree (feed_id, posted_at DESC NULLS LAST)
Saya ingin mengembalikan semua pesan yang dipesan share_count
, tetapi untuk masing-masing parent_id
, saya hanya ingin mengembalikan satu pesan. yaitu, jika beberapa pesan memiliki yang sama parent_id
, maka hanya yang terbaru ( posted_at
) yang dikembalikan. The parent_id
dapat null, pesan dengan nol parent_id
semua harus kembali.
Permintaan yang saya gunakan adalah:
WITH filtered_messages AS (SELECT *
FROM messages
WHERE feed_id IN (7)
AND (posted_at >= '2015-01-01 04:00:00.000000')
AND (posted_at < '2015-04-28 04:00:00.000000'))
SELECT *
FROM (SELECT DISTINCT ON(COALESCE(parent_id, message_id)) parent_id,
message_id,
posted_at,
share_count
FROM filtered_messages
ORDER BY COALESCE(parent_id, message_id), posted_at DESC NULLS LAST
) messages
ORDER BY share_count DESC NULLS LAST, posted_at DESC NULLS LAST;
Inilah http://sqlfiddle.com/#!15/588e5/1/0 , dalam SQL Fiddle, saya telah mendefinisikan skema, kueri yang tepat, dan hasil yang diharapkan.
Tetapi kinerja permintaan lambat begitu tabel pesan menjadi besar. Saya mencoba menambahkan beberapa indeks penyortiran, tetapi sepertinya tidak menggunakan indeks. Inilah penjelasannya: http://explain.depesz.com/s/Sv2
Bagaimana saya bisa membuat indeks yang benar?
sumber
ORDER BY
dalam subquery sama sekali tidak berguna. Lebih lanjut, rencana tertaut tidak dapat merupakan hasil dari permintaan yang diposting - tidak ada penyebutanmetadata
, misalnya.feed_id
danposted_at
dan Anda tidak menyebutkanmetadata
sama sekali, yang tampaknya merupakan tipe JSON? Harap perbaiki pertanyaan Anda agar konsisten. Anda pilih> 500k baris dalam CTE ... Berapa banyak baris dalam tabel? Berapa persentase baris yang biasanya Anda pilih dalam CTE? Berapa persentase baris yang dimilikiparent_id IS NULL
? Pertimbangkan info dalam tag [kinerja-postgresql] untuk pertanyaan kinerja.parent_id
? (minimal / rata-rata)metadata
. Saat ini tabel pesan memiliki 10 juta data, tetapi bertambah cepat. Saya pikir untuk memisahkan ke dalam tabel partisi untuk setiap feed_id. Karena saya hanya mengambil per id umpan. persentase parent_id null vs bukan null adalah sekitar 60% / 40%. pengambilan tipikal adalah sekitar 1-2% dari tabel. (sekitar 100K pesan) Kinerja untuk 100K adalah sekitar 1s, tetapi sekali sampai 500K + itu menggunakan indeks bitmap dan biasanya membutuhkan 10s.Jawaban:
Pertanyaan
Pertanyaan ini seharusnya jauh lebih cepat dalam hal apa pun:
CTE tidak melakukan apa pun di sini yang tidak dapat disampaikan oleh subquery biasa. Dan CTE memperkenalkan penghalang optimasi karena dijalankan secara terpisah dan hasilnya terwujud.
Anda memiliki satu tingkat subquery lebih dari yang sebenarnya Anda butuhkan.
Ekspresi
(COALESCE(parent_id, message_id)
tidak kompatibel dengan indeks biasa, Anda perlu indeks pada ekspresi itu. Tetapi itu mungkin juga tidak terlalu berguna, tergantung pada distribusi data. Ikuti tautan saya di bawah ini untuk informasi terperinci.Membagi kasus sederhana
parent_id IS NULL
menjadi terpisahSELECT
mungkin atau tidak memberikan yang optimal. Terutama tidak, jika itu kasus yang jarang terjadi, dalam hal ini permintaan gabungan dengan indeks aktif(COALESCE(parent_id, message_id)
dapat bekerja lebih baik. Pertimbangan lain berlaku ...Indeks
Terutama ketika didukung dengan indeks ini:
Dua indeks parsial mencakup seluruh tabel bersama - sama dan hampir sama ukurannya sebagai satu indeks total.
Dua kolom terakhir
parent_id, message_id
hanya masuk akal jika Anda hanya mendapatkan scan indeks saja . Hapus mereka dari kedua indeks.SQL Fiddle.
Bergantung pada detail yang hilang,
DISTINCT ON
mungkin atau mungkin bukan teknik permintaan terbaik untuk tujuan tersebut. Dapatkan penjelasan rinci di sini:Dan mungkin alternatif yang lebih cepat di sini:
sumber