postgres sıralamayı hızlandırmak için dizin nasıl kullanılır


10

Postgres 9.4 kullanıyorum.

messagesAşağıdaki şema vardır: mesajlar FEED_ID aittir ve posted_at vardır, ayrıca mesajlar (cevapların durumunda) bir üst mesajı olabilir.

                    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)

Tarafından sipariş edilen tüm mesajları iade etmek istiyorum share_count, ancak her biri için parent_idsadece bir mesaj geri dönmek istiyorum. örneğin, birden fazla ileti aynı ise parent_id, yalnızca en son gelen ileti ( posted_at) döndürülür. parent_idOlabilir null, null mesajlar parent_idtüm iade gerektiği.

Kullandığım sorgu:

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;

İşte http://sqlfiddle.com/#!15/588e5/1/0 , SQL Fiddle'da şemayı, tam sorguyu ve beklenen sonucu tanımladım.

Ancak iletiler tablosu büyüdükçe sorgunun performansı yavaşlar. Birden çok sıralama dizini eklemeyi denedim, ancak dizini kullanmak gibi görünmüyor. İşte açıklama: http://explain.depesz.com/s/Sv2

Nasıl doğru dizin oluşturabilirim?


İlk bakışta, ORDER BYalt sorgudaki tamamen işe yaramaz. Ayrıca, bağlantılı plan gönderilen sorgunun bir sonucu olamaz - metadataörneğin , bundan bahsedilmez .
dezso

Açıklamanız rolünü kapsamaz feed_idve posted_atve söz etmedi metadataJSON türü gibi görünüyor, hangi hiç? Tutarlı hale getirmek için lütfen sorunuzu onarın. CTE'de> 500 bin satır seçin ... Tabloda kaç satır var? CTE'de tipik olarak yüzde kaç satır seçersiniz? Satırların yüzde kaçı var parent_id IS NULL? Performans soruları için [postgresql-performance] etiketindeki bilgileri göz önünde bulundurun .
Erwin Brandstetter

Ayrıca önemli: Her biri için kaç satır var parent_id? (min / ort / maks)
Erwin Brandstetter

üzgünüm, bazı sütunları azaltarak soruyu daha açık hale getirmeye çalışıyordum, share_count aslında hstore'daydı metadata. Şu anda mesajlar tablosunda 10 mil veri var, ancak hızla artıyor. Her feed_id için bölüm tabloları ayırmak düşünüyorum. Yalnızca feed kimliği başına getirildiğimden. parent_id null yerine null değil yüzdesi yaklaşık% 60 /% 40'tır. tipik bir getirme tablonun yaklaşık% 1-2'sidir. (100K civarında mesajlar) 100K performansı 1s civarındadır, ancak bir kez 500K + 'ya ulaşır, bitmap dizini kullanır ve normalde 10s alır.
Zhaohan Weng

Yanıtlar:


9

Sorgu

Bu sorgu her durumda önemli ölçüde daha hızlı olmalıdır:

SELECT parent_id, message_id, posted_at, share_count
FROM   messages
WHERE  feed_id = 7
AND    posted_at >= '2015-01-01 4:0:0'
AND    posted_at <  '2015-04-28 4:0:0'
AND    parent_id IS NULL  -- match index condition
UNION ALL
(
SELECT DISTINCT ON(parent_id)
       parent_id, message_id, posted_at, share_count
FROM   messages
WHERE  feed_id = 7
AND    posted_at >= '2015-01-01 4:0:0'
AND    posted_at <  '2015-04-28 4:0:0'
AND    parent_id IS NOT NULL  -- match index condition
ORDER  BY parent_id, posted_at DESC NULLS LAST
)
ORDER  BY share_count DESC NULLS LAST, posted_at DESC NULLS LAST;
  • CTE burada basit bir alt sorgunun da sağlayamadığı hiçbir şey yapmaz. Bir CTE, ayrı olarak yürütüldüğü ve sonucu gerçekleştiği için bir optimizasyon bariyeri sunar.

  • İhtiyacınız olandan bir alt sorgu seviyeniz daha var.

  • İfade (COALESCE(parent_id, message_id)düz bir dizinle uyumlu değil, bu ifade üzerinde bir dizine ihtiyacınız olacaktır. Ancak bu, veri dağıtımına bağlı olarak çok yararlı olmayabilir. Ayrıntılı bilgi için aşağıdaki bağlantılarımı takip edin.

  • Basit kasayı parent_id IS NULLayrı bir bölmeye ayırmak SELECT, optimumu sağlayabilir veya vermeyebilir. Özellikle de, bu nadir bir durumsa, bu durumda, üzerinde bir endeksi olan birleştirilmiş bir sorgu (COALESCE(parent_id, message_id)daha iyi performans gösterebilir. Diğer hususlar geçerlidir ...

Endeksler

Özellikle bu endekslerle desteklendiğinde:

CREATE INDEX messages_idx_null ON messages (
  feed_id
, posted_at DESC NULLS LAST
, share_count DESC NULLS LAST
, parent_id, message_id
)
WHERE parent_id IS NULL;

CREATE INDEX messages_idx_notnull ON messages (
  feed_id
, posted_at DESC NULLS LAST
, share_count DESC NULLS LAST
, parent_id, message_id
)
WHERE parent_id IS NOT NULL;

İki kısmi indeks tüm tabloyu birlikte kapsar ve tek bir toplam indeks ile yaklaşık olarak aynı boyuttadır.

Son iki sütun, parent_id, message_idyalnızca dizinden yalnızca taramalar alırsanız mantıklıdır . Else onları her iki endeksten kaldırın.

SQL Fiddle.

Eksik detaylara bağlı olarak DISTINCT ON, amaç için en iyi sorgu tekniği olabilir veya olmayabilir. Ayrıntılı açıklamayı buradan okuyun:

Ve muhtemelen burada daha hızlı alternatifler:

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.