Yürütme planları benzer (örtük dizini kullanarak hem sorgular) SELECT ... IN ...
bulunmaktadır. @Catcall iyi bir başlangıç yaptı, ancak deneyini birçok gerçek veri tabanından çok daha küçüktü. 300000 tek tamsayı satırları belleğe kolayca sığar, dolayısıyla IO oluşmaz; ayrıca gerçek sayıları paylaşmadı.
Benzer bir denemeyi oluşturdum, ancak örnek verisi, ana bilgisayarımdaki kullanılabilir bellek kadar 7 kat daha büyük olacak şekilde boyutlandırdı (1 GB'lık 1-işlemcili CPU VM'sindeki 7 GB veri kümesi, NFS'ye bağlı dosya sistemi). Tek bir dizinlenmiş bigint ve 0 ile 400 bayt arasında rastgele bir uzunluk dizesinden oluşan 30.000.000 satır vardır. aşağıdaki Ne
create table t(id bigint primary key, stuff text);
insert into t(id,stuff) select i, repeat('X',(random()*400)::integer)
from generate_series(0,30000000) i;
analyze t;
olan tuşlar etki 10, 100, 1000, 10000 ve 100000 rastgele tamsayılar setleri seçkin bir IN için sürelerini analiz açıklar. Her sorgu aşağıdaki formdadır, $ 1 set sayımları ile değiştirilir.
explain analyze
select id from t
where id in (
select (random()*30000000)::integer from generate_series(0,$1)
);
Özeti Kez
- ct, tot MS, MS/satır
- 10, 84, 8.4
- 100, 1185,
- 1.000 11.8, 12407, 12.4
- 10,000, 109747, 11.0
- 100.000, 1.016.842, 10,1
Not planı seti kardinalitesi İÇİNDE her biri için aynı kalır - Rastgele bir tamsayı karma agrega, daha sonra döngü ve inşa etmek ve her değer için tek endeksli arama yapmak. Alma süresi, 8-12 ms/satır aralığında IN kümesinin asallığı ile lineer yakındır. Daha hızlı bir depolama sistemi, bu zamanları önemli ölçüde geliştirebilir, ancak deney, Pg'nin IN yan tümcesinde çok büyük kümeleri aplomb ile gerçekleştirdiğini gösterir - en azından yürütme hızı açısından. Listeyi bind-parametresi veya sql ifadesinin değişmez enterpolasyonu ile sağladığınız takdirde, sorguyu sunucuya iletmek için ek ek yüke maruz kalırsınız ve ayrıştırma sürelerini artırırsınız, ancak IO'ya kıyasla önemsiz olduğundan şüphelenirim. sorguyu belirleme zamanı.
# fetch 10
Nested Loop (cost=30.00..2341.27 rows=15002521 width=8) (actual time=0.110..84.494 rows=11 loops=1)
-> HashAggregate (cost=30.00..32.00 rows=200 width=4) (actual time=0.046..0.054 rows=11 loops=1)
-> Function Scan on generate_series (cost=0.00..17.50 rows=1000 width=0) (actual time=0.036..0.039 rows=11 loops=1)
-> Index Scan using t_pkey on t (cost=0.00..11.53 rows=1 width=8) (actual time=7.672..7.673 rows=1 loops=11)
Index Cond: (t.id = (((random() * 30000000::double precision))::integer))
Total runtime: 84.580 ms
# fetch 100
Nested Loop (cost=30.00..2341.27 rows=15002521 width=8) (actual time=12.405..1184.758 rows=101 loops=1)
-> HashAggregate (cost=30.00..32.00 rows=200 width=4) (actual time=0.095..0.210 rows=101 loops=1)
-> Function Scan on generate_series (cost=0.00..17.50 rows=1000 width=0) (actual time=0.046..0.067 rows=101 loops=1)
-> Index Scan using t_pkey on t (cost=0.00..11.53 rows=1 width=8) (actual time=11.723..11.725 rows=1 loops=101)
Index Cond: (t.id = (((random() * 30000000::double precision))::integer))
Total runtime: 1184.843 ms
# fetch 1,000
Nested Loop (cost=30.00..2341.27 rows=15002521 width=8) (actual time=14.403..12406.667 rows=1001 loops=1)
-> HashAggregate (cost=30.00..32.00 rows=200 width=4) (actual time=0.609..1.689 rows=1001 loops=1)
-> Function Scan on generate_series (cost=0.00..17.50 rows=1000 width=0) (actual time=0.128..0.332 rows=1001 loops=1)
-> Index Scan using t_pkey on t (cost=0.00..11.53 rows=1 width=8) (actual time=12.381..12.390 rows=1 loops=1001)
Index Cond: (t.id = (((random() * 30000000::double precision))::integer))
Total runtime: 12407.059 ms
# fetch 10,000
Nested Loop (cost=30.00..2341.27 rows=15002521 width=8) (actual time=21.884..109743.854 rows=9998 loops=1)
-> HashAggregate (cost=30.00..32.00 rows=200 width=4) (actual time=5.761..18.090 rows=9998 loops=1)
-> Function Scan on generate_series (cost=0.00..17.50 rows=1000 width=0) (actual time=1.004..3.087 rows=10001 loops=1)
-> Index Scan using t_pkey on t (cost=0.00..11.53 rows=1 width=8) (actual time=10.968..10.972 rows=1 loops=9998)
Index Cond: (t.id = (((random() * 30000000::double precision))::integer))
Total runtime: 109747.169 ms
# fetch 100,000
Nested Loop (cost=30.00..2341.27 rows=15002521 width=8) (actual time=110.244..1016781.944 rows=99816 loops=1)
-> HashAggregate (cost=30.00..32.00 rows=200 width=4) (actual time=110.169..253.947 rows=99816 loops=1)
-> Function Scan on generate_series (cost=0.00..17.50 rows=1000 width=0) (actual time=51.141..77.482 rows=100001 loops=1)
-> Index Scan using t_pkey on t (cost=0.00..11.53 rows=1 width=8) (actual time=10.176..10.181 rows=1 loops=99816)
Index Cond: (t.id = (((random() * 30000000::double precision))::integer))
Total runtime: 1016842.772 ms
@Catcall 'un isteği üzerine CTE ve geçici tablo kullanarak benzer sorgulamalar yaptım. Her iki yaklaşım da karşılaştırmalı olarak basit yuva döngü indeksi tarama planlarına sahipti ve inline IN sorguları ile karşılaştırılabilir (ancak biraz daha yavaş) zamanlarda çalıştı. Tekrar çalıştırırsanız
-- CTE
EXPLAIN analyze
with ids as (select (random()*30000000)::integer as val from generate_series(0,1000))
select id from t where id in (select ids.val from ids);
Nested Loop (cost=40.00..2351.27 rows=15002521 width=8) (actual time=21.203..12878.329 rows=1001 loops=1)
CTE ids
-> Function Scan on generate_series (cost=0.00..17.50 rows=1000 width=0) (actual time=0.085..0.306 rows=1001 loops=1)
-> HashAggregate (cost=22.50..24.50 rows=200 width=4) (actual time=0.771..1.907 rows=1001 loops=1)
-> CTE Scan on ids (cost=0.00..20.00 rows=1000 width=4) (actual time=0.087..0.552 rows=1001 loops=1)
-> Index Scan using t_pkey on t (cost=0.00..11.53 rows=1 width=8) (actual time=12.859..12.861 rows=1 loops=1001)
Index Cond: (t.id = ids.val)
Total runtime: 12878.812 ms
(8 rows)
-- Temp table
create table temp_ids as select (random()*30000000)::bigint as val from generate_series(0,1000);
explain analyze select id from t where t.id in (select val from temp_ids);
Nested Loop (cost=17.51..11585.41 rows=1001 width=8) (actual time=7.062..15724.571 rows=1001 loops=1)
-> HashAggregate (cost=17.51..27.52 rows=1001 width=8) (actual time=0.268..1.356 rows=1001 loops=1)
-> Seq Scan on temp_ids (cost=0.00..15.01 rows=1001 width=8) (actual time=0.007..0.080 rows=1001 loops=1)
-> Index Scan using t_pkey on t (cost=0.00..11.53 rows=1 width=8) (actual time=15.703..15.705 rows=1 loops=1001)
Index Cond: (t.id = temp_ids.val)
Total runtime: 15725.063 ms
-- another way using join against temptable insteed of IN
explain analyze select id from t join temp_ids on (t.id = temp_ids.val);
Nested Loop (cost=0.00..24687.88 rows=2140 width=8) (actual time=22.594..16557.789 rows=1001 loops=1)
-> Seq Scan on temp_ids (cost=0.00..31.40 rows=2140 width=8) (actual time=0.014..0.872 rows=1001 loops=1)
-> Index Scan using t_pkey on t (cost=0.00..11.51 rows=1 width=8) (actual time=16.536..16.537 rows=1 loops=1001)
Index Cond: (t.id = temp_ids.val)
Total runtime: 16558.331 ms
geçici tablo sorguları çok daha hızlı koştu, ama id değeri seti sabittir, bu nedenle hedef veri önbellekte taze ve Pg ikinci kez çalıştırmak için hiçbir gerçek IO bunun nedeni de.
Belki, ama belki de tam olarak değil. Bu soru, IN yan tümcesinin maksimum boyutunu soruyor. Ben soruyorum, makul olan nedir? Yoksa bu çok öznel bir ayrım mı? Soruyu yeniden yazmayı göreceğim. –
Yapamazsınız: Nerede ... In (non_indexed_param1, non_indexed_param2, ...) Veya ... In (select ... search_index Burada ...) İki ayrı sorgu kullanmak yerine? – beny23
Belki de dilimle çok gevşek kaldım. "Arama dizini" ile arama sunucusunu, yani bu durumda Sfenks'i kastediyorum. Bunu açıklığa kavuşturdum. –