Pemindaian Sekuensial PostgreSQL alih-alih Pemindaian Indeks Mengapa?

11

Hai Semua Saya punya masalah dengan permintaan basis data PostgreSQL saya dan bertanya-tanya apakah ada yang bisa membantu. Dalam beberapa skenario kueri saya tampaknya mengabaikan indeks yang telah saya buat yang digunakan untuk bergabung dengan dua tabel datadan data_area. Ketika ini terjadi, ia menggunakan pemindaian berurutan dan menghasilkan permintaan yang jauh lebih lambat.

Pemindaian Berurutan (~ 5 menit)

Unique  (cost=15368261.82..15369053.96 rows=200 width=1942) (actual time=301266.832..301346.936 rows=153812 loops=1)
   CTE data
     ->  Bitmap Heap Scan on data  (cost=6086.77..610089.54 rows=321976 width=297) (actual time=26.286..197.625 rows=335130 loops=1)
           Recheck Cond: (datasetid = 1)
           Filter: ((readingdatetime >= '1920-01-01 00:00:00'::timestamp without time zone) AND (readingdatetime <= '2013-03-11 00:00:00'::timestamp without time zone) AND (depth >= 0::double precision) AND (depth <= 99999::double precision))
           ->  Bitmap Index Scan on data_datasetid_index  (cost=0.00..6006.27 rows=324789 width=0) (actual time=25.462..25.462 rows=335130 loops=1)
                 Index Cond: (datasetid = 1)
   ->  Sort  (cost=15368261.82..15368657.89 rows=158427 width=1942) (actual time=301266.829..301287.110 rows=155194 loops=1)
         Sort Key: data.id
         Sort Method: quicksort  Memory: 81999kB
         ->  Hash Left Join  (cost=15174943.29..15354578.91 rows=158427 width=1942) (actual time=300068.588..301052.832 rows=155194 loops=1)
               Hash Cond: (data_area.area_id = area.id)
               ->  Hash Join  (cost=15174792.93..15351854.12 rows=158427 width=684) (actual time=300066.288..300971.644 rows=155194 loops=1)
                     Hash Cond: (data.id = data_area.data_id)
                     ->  CTE Scan on data  (cost=0.00..6439.52 rows=321976 width=676) (actual time=26.290..313.842 rows=335130 loops=1)
                     ->  Hash  (cost=14857017.62..14857017.62 rows=25422025 width=8) (actual time=300028.260..300028.260 rows=26709939 loops=1)
                           Buckets: 4194304  Batches: 1  Memory Usage: 1043357kB
                           ->  Seq Scan on data_area  (cost=0.00..14857017.62 rows=25422025 width=8) (actual time=182921.056..291687.996 rows=26709939 loops=1)
                                 Filter: (area_id = ANY ('{28,29,30,31,32,33,25,26,27,18,19,20,21,12,13,14,15,16,17,34,35,1,2,3,4,5,6,22,23,24,7,8,9,10,11}'::integer[]))
               ->  Hash  (cost=108.49..108.49 rows=3349 width=1258) (actual time=2.256..2.256 rows=3349 loops=1)
                     Buckets: 1024  Batches: 1  Memory Usage: 584kB
                     ->  Seq Scan on area  (cost=0.00..108.49 rows=3349 width=1258) (actual time=0.007..0.666 rows=3349 loops=1)
 Total runtime: 301493.379 ms

Pemindaian Indeks (~ 3 detik) ( di jelas.depesz.com )

Unique  (cost=17352256.47..17353067.50 rows=200 width=1942) (actual time=3603.303..3681.619 rows=153812 loops=1)
   CTE data
     ->  Bitmap Heap Scan on data  (cost=6284.60..619979.56 rows=332340 width=297) (actual time=26.201..262.314 rows=335130 loops=1)
           Recheck Cond: (datasetid = 1)
           Filter: ((readingdatetime >= '1920-01-01 00:00:00'::timestamp without time zone) AND (readingdatetime <= '2013-03-11 00:00:00'::timestamp without time zone) AND (depth >= 0::double precision) AND (depth <= 99999::double precision))
           ->  Bitmap Index Scan on data_datasetid_index  (cost=0.00..6201.51 rows=335354 width=0) (actual time=25.381..25.381 rows=335130 loops=1)
                 Index Cond: (datasetid = 1)
   ->  Sort  (cost=17352256.47..17352661.98 rows=162206 width=1942) (actual time=3603.302..3623.113 rows=155194 loops=1)
         Sort Key: data.id
         Sort Method: quicksort  Memory: 81999kB
         ->  Hash Left Join  (cost=1296.08..17338219.59 rows=162206 width=1942) (actual time=29.980..3375.921 rows=155194 loops=1)
               Hash Cond: (data_area.area_id = area.id)
               ->  Nested Loop  (cost=0.00..17334287.66 rows=162206 width=684) (actual time=26.903..3268.674 rows=155194 loops=1)
                     ->  CTE Scan on data  (cost=0.00..6646.80 rows=332340 width=676) (actual time=26.205..421.858 rows=335130 loops=1)
                     ->  Index Scan using data_area_pkey on data_area  (cost=0.00..52.13 rows=1 width=8) (actual time=0.006..0.008 rows=0 loops=335130)
                           Index Cond: (data_id = data.id)
                           Filter: (area_id = ANY ('{28,29,30,31,32,33,25,26,27,18,19,20,21,12,13,14,15,16,17,34,35,1,2,3,4,5,6,22,23,24,7,8,9,10,11}'::integer[]))
               ->  Hash  (cost=1254.22..1254.22 rows=3349 width=1258) (actual time=3.057..3.057 rows=3349 loops=1)
                     Buckets: 1024  Batches: 1  Memory Usage: 584kB
                     ->  Index Scan using area_primary_key on area  (cost=0.00..1254.22 rows=3349 width=1258) (actual time=0.012..1.429 rows=3349 loops=1)
 Total runtime: 3706.630 ms

Struktur Tabel

Ini adalah struktur tabel untuk data_areatabel. Saya bisa menyediakan tabel lain jika perlu.

CREATE TABLE data_area
(
  data_id integer NOT NULL,
  area_id integer NOT NULL,
  CONSTRAINT data_area_pkey PRIMARY KEY (data_id , area_id ),
  CONSTRAINT data_area_area_id_fk FOREIGN KEY (area_id)
      REFERENCES area (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT data_area_data_id_fk FOREIGN KEY (data_id)
      REFERENCES data (id) MATCH SIMPLE
      ON UPDATE CASCADE ON DELETE CASCADE
);

PERTANYAAN

WITH data AS (
    SELECT * 
    FROM data 
    WHERE 
        datasetid IN (1) 
        AND (readingdatetime BETWEEN '1920-01-01' AND '2013-03-11') 
        AND depth BETWEEN 0 AND 99999
)
SELECT * 
FROM ( 
    SELECT DISTINCT ON (data.id) data.id, * 
    FROM 
        data, 
        data_area 
        LEFT JOIN area ON area_id = area.id 
    WHERE 
        data_id = data.id 
        AND area_id IN (28,29,30,31,32,33,25,26,27,18,19,20,21,12,13,14,15,16,17,34,35,1,2,3,4,5,6,22,23,24,7,8,9,10,11) 
) as s;

Mengembalikan 153812baris. Apakah set enable_seqscan= false;untuk menonaktifkan scan sekuensial dan mendapatkan hasil indeks.

Saya sudah mencoba melakukan ANALYSEpada database dan meningkatkan statistik yang dikumpulkan pada kolom yang digunakan dalam query, tetapi sepertinya tidak ada yang membantu.

Adakah yang bisa menyebarkan dan menjelaskan ini atau menyarankan hal lain yang harus saya coba?

Mark Davidson
sumber
Itu akan membantu saya jika Anda memasukkan pertanyaan yang menghasilkan masing-masing rencana eksekusi tersebut.
Mike Sherrill 'Cat Recall'
Perbedaan 2 urutan besarnya dalam estimasi jumlah baris dan jumlah aktual baris? Apakah saya membacanya?
Mike Sherrill 'Cat Recall'
@Catcall Telah menambahkan kueri (agak mendasar untuk dapat mengetahui apa yang terjadi). Ketika Anda merujuk pada baris yang diperkirakan adalah bahwa 200 dan kemudian benar-benar mengembalikan 153812?
Mark Davidson
2
Ya, sekilas 200 vs 150k tampak aneh. Apakah ada alasan kuat untuk mencampur gabungan kiri dengan produk Cartesian ( FROM data, data_area)? Pada pandangan pertama, menggunakan DISTINCT ON tanpa klausa ORDER BY tampaknya merupakan ide yang buruk.
Mike Sherrill 'Cat Recall'
jelaskan.depesz.com/s/Uzin mungkin informatif.
Craig Ringer

Jawaban:

7

Perhatikan baris ini:

->  Index Scan using data_area_pkey on data_area  (cost=0.00..52.13 rows=1 width=8) 
    (actual time=0.006..0.008 rows=0 loops=335130)

Jika Anda menghitung biaya total, mempertimbangkan loop, itu adalah 52.3 * 335130 = 17527299. Ini lebih besar dari 14857017,62 untuk seq_scanalternatif. Itu sebabnya tidak menggunakan indeks.

Jadi optimizer terlalu tinggi biaya pemindaian indeks. Saya menduga bahwa data Anda diurutkan pada indeks (baik karena indeks berkerumun atau bagaimana itu dimuat) dan / atau Anda memiliki banyak memori cache dan / atau disk cepat yang bagus. Oleh karena itu ada sedikit I / O acak yang terjadi.

Anda juga harus memeriksa correlationdalam pg_stats, yang digunakan oleh pengoptimal untuk menilai pengelompokan ketika menghitung biaya indeks, dan akhirnya mencoba mengubah random_page_costdan cpu_index_tuple_cost, untuk mencocokkan sistem Anda.

pel
sumber
Kecuali saya melewatkan sesuatu, saya pikir @jop berarti 52.13, tidak 52.3, yang akan menghasilkan 17470326.9 (masih lebih besar dari seq_scan)
BotNet
2

CTE Anda sebenarnya tidak melakukan hal lain selain 'mengalihdayakan' beberapa WHEREkondisi, kebanyakan dari mereka terlihat setara WHERE TRUE. Karena CTE biasanya berada di belakang pagar pengoptimalan (yang berarti dioptimalkan sendiri), mereka dapat banyak membantu dengan pertanyaan tertentu. Namun dalam hal ini, saya akan mengharapkan efek sebaliknya yang tepat.

Apa yang akan saya coba adalah menulis ulang kueri menjadi sesederhana mungkin:

SELECT d.id, * 
FROM 
    data d 
    JOIN data_area da ON da.data_id = d.id
    LEFT JOIN area a ON da.area_id = a.id 
WHERE 
    d.datasetid IN (1) 
    AND da.area_id IN (28,29,30,31,32,33,25,26,27,18,19,20,21,12,13,14,15,16,17,34,35,1,2,3,4,5,6,22,23,24,7,8,9,10,11) 
    AND (readingdatetime BETWEEN '1920-01-01' AND '2013-03-11') -- this and the next condition don't do anything, I think
    AND depth BETWEEN 0 AND 99999
;

dan kemudian periksa apakah indeks digunakan atau tidak. Masih sangat mungkin bahwa Anda tidak memerlukan semua kolom output (setidaknya dua kolom dari tabel junction tidak perlu).

Silakan laporkan kembali dan beri tahu kami versi PostgreSQL mana yang Anda gunakan.

dezso
sumber
Terima kasih atas saran Anda, permintaan maaf saya atas balasan saya yang tertunda pada posting Anda, saya telah mengerjakan proyek lain. Saran Anda memang berarti bahwa permintaan sekarang tampaknya dapat diandalkan menggunakan indeks untuk semua pertanyaan tapi saya masih belum mendapatkan kinerja yang saya harapkan dengan itu. Saya sudah melakukan analisa pada query yang memiliki data lebih banyak explain.depesz.com/s/1yu dibutuhkan seperti 4 menit dengan 95% dari waktu yang dihabiskan di INDEX scan.
Mark Davidson
Lupa menyebutkan saya menggunakan versi 9.1.4
Mark Davidson
Pada dasarnya pemindaian indeks cukup cepat, masalahnya adalah diulang beberapa juta kali. Apa yang Anda dapatkan jika Anda SET enable_nestloop=offsebelum menjalankan kueri?
dezso
-1

Untuk pengikut, saya punya masalah serupa itu

select * from table where bigint_column between x and y and mod(bigint_column, 10000) == z

Masalahnya adalah bahwa bigint_column saya "antara x dan y" memiliki indeks, tetapi permintaan saya pada dasarnya adalah "semua baris" di tabel itu, jadi itu tidak menggunakan indeks [karena itu harus memindai seluruh tabel juga] tetapi sedang melakukan scan sekuensial seq_scan. Perbaikan bagi saya adalah membuat indeks baru untuk sisi "mod" dari persamaan, sehingga bisa menggunakannya pada ekspresi .

rogerdpack
sumber
downvoters merasa bebas untuk meninggalkan komentar mengapa :)
rogerdpack