Karşılaştırma
Postgres ile en ilginç adayları Test 9.4 ve 9.5 arasında bir yarım gerçekçi tabloyla 200k satır halinde purchases
ve 10k ayrıcustomer_id
( ort. Müşteri başına 20 satır ).
Postgres 9.5 için etkin bir şekilde 86446 farklı müşteriyle 2. bir test yaptım. Aşağıya bakın ( müşteri başına ortalama 2,3 satır ).
Kurmak
Ana tablo
CREATE TABLE purchases (
id serial
, customer_id int -- REFERENCES customer
, total int -- could be amount of money in Cent
, some_column text -- to make the row bigger, more realistic
);
Daha tipik bir kurulum olduğu için serial
(aşağıda eklenen PK kısıtlaması) ve bir tam sayı kullanıyorum customer_id
. Ayrıca some_column
tipik olarak daha fazla sütun oluşturmak için de eklendi .
Kukla veriler, PK, dizin - tipik bir tabloda bazı ölü tupl'ler de vardır:
INSERT INTO purchases (customer_id, total, some_column) -- insert 200k rows
SELECT (random() * 10000)::int AS customer_id -- 10k customers
, (random() * random() * 100000)::int AS total
, 'note: ' || repeat('x', (random()^2 * random() * random() * 500)::int)
FROM generate_series(1,200000) g;
ALTER TABLE purchases ADD CONSTRAINT purchases_id_pkey PRIMARY KEY (id);
DELETE FROM purchases WHERE random() > 0.9; -- some dead rows
INSERT INTO purchases (customer_id, total, some_column)
SELECT (random() * 10000)::int AS customer_id -- 10k customers
, (random() * random() * 100000)::int AS total
, 'note: ' || repeat('x', (random()^2 * random() * random() * 500)::int)
FROM generate_series(1,20000) g; -- add 20k to make it ~ 200k
CREATE INDEX purchases_3c_idx ON purchases (customer_id, total DESC, id);
VACUUM ANALYZE purchases;
customer
tablo - üstün sorgu için
CREATE TABLE customer AS
SELECT customer_id, 'customer_' || customer_id AS customer
FROM purchases
GROUP BY 1
ORDER BY 1;
ALTER TABLE customer ADD CONSTRAINT customer_customer_id_pkey PRIMARY KEY (customer_id);
VACUUM ANALYZE customer;
Benim içinde ikinci testte 9.5 için ben aynı kurulumu uygulandı fakat random() * 100000
oluşturmak için customer_id
başına sadece birkaç satır olsun customer_id
.
Tablo için nesne boyutları purchases
Bu sorgu ile oluşturuldu .
what | bytes/ct | bytes_pretty | bytes_per_row
-----------------------------------+----------+--------------+---------------
core_relation_size | 20496384 | 20 MB | 102
visibility_map | 0 | 0 bytes | 0
free_space_map | 24576 | 24 kB | 0
table_size_incl_toast | 20529152 | 20 MB | 102
indexes_size | 10977280 | 10 MB | 54
total_size_incl_toast_and_indexes | 31506432 | 30 MB | 157
live_rows_in_text_representation | 13729802 | 13 MB | 68
------------------------------ | | |
row_count | 200045 | |
live_tuples | 200045 | |
dead_tuples | 19955 | |
Sorguları
WITH cte AS (
SELECT id, customer_id, total
, row_number() OVER(PARTITION BY customer_id ORDER BY total DESC) AS rn
FROM purchases
)
SELECT id, customer_id, total
FROM cte
WHERE rn = 1;
2. row_number()
alt sorguda (optimizasyonum)
SELECT id, customer_id, total
FROM (
SELECT id, customer_id, total
, row_number() OVER(PARTITION BY customer_id ORDER BY total DESC) AS rn
FROM purchases
) sub
WHERE rn = 1;
SELECT DISTINCT ON (customer_id)
id, customer_id, total
FROM purchases
ORDER BY customer_id, total DESC, id;
4. LATERAL
alt sorgu ile rCTE ( buraya bakınız )
WITH RECURSIVE cte AS (
( -- parentheses required
SELECT id, customer_id, total
FROM purchases
ORDER BY customer_id, total DESC
LIMIT 1
)
UNION ALL
SELECT u.*
FROM cte c
, LATERAL (
SELECT id, customer_id, total
FROM purchases
WHERE customer_id > c.customer_id -- lateral reference
ORDER BY customer_id, total DESC
LIMIT 1
) u
)
SELECT id, customer_id, total
FROM cte
ORDER BY customer_id;
5. ile customer
tablo LATERAL
( buraya bakınız )
SELECT l.*
FROM customer c
, LATERAL (
SELECT id, customer_id, total
FROM purchases
WHERE customer_id = c.customer_id -- lateral reference
ORDER BY total DESC
LIMIT 1
) l;
SELECT (array_agg(id ORDER BY total DESC))[1] AS id
, customer_id
, max(total) AS total
FROM purchases
GROUP BY customer_id;
Sonuçlar
Yukarıdaki sorgular için yürütme süresi EXPLAIN ANALYZE
(ve tüm seçenekler kapalı ), en iyi 5 çalıştırma .
Tüm sorgular bir kullanılmış Sadece Dizini Tara üzerinde purchases2_3c_idx
(diğer adımların yanı sıra). Bazıları sadece indeksin daha küçük boyutu için, diğerleri daha etkili.
A. 200k satırlı postgres 9.4 ve başına ~ 20 customer_id
1. 273.274 ms
2. 194.572 ms
3. 111.067 ms
4. 92.922 ms
5. 37.679 ms -- winner
6. 189.495 ms
B. Postgres 9.5 ile aynı
1. 288.006 ms
2. 223.032 ms
3. 107.074 ms
4. 78.032 ms
5. 33.944 ms -- winner
6. 211.540 ms
C.B ile aynı, ancak başına ~ 2.3 satır customer_id
1. 381.573 ms
2. 311.976 ms
3. 124.074 ms -- winner
4. 710.631 ms
5. 311.976 ms
6. 421.679 ms
İlgili kriterler
İşte "Öğr" ile test ederek yeni bir tanesidir 10M satır ve 60k benzersiz "müşteriler" üzerine Postgres 11.5 (Eylül 2019 itibariyle akımı). Sonuçlar hâlâ gördüğümüzle aynı doğrultuda:
2011'den orijinal (modası geçmiş) karşılaştırması
PostgreSQL 9.1 ile 65579 satırlık gerçek bir yaşam tablosunda üç test yaptım ve ilgili üç sütunun her birinde tek sütunlu btree dizinleri kullandım ve 5 çalışmanın en iyi yürütme süresini aldım .
Karşılaştırma @OMGPonies' ilk sorgu ( A
kadar) yukarıdaki DISTINCT ON
çözeltiye ( B
):
Tüm tabloyu seçin, bu durumda 5958 satırla sonuçlanır.
A: 567.218 ms
B: 386.673 ms
WHERE customer BETWEEN x AND y
1000 sıra ile sonuçlanan koşul kullanın .
A: 249.136 ms
B: 55.111 ms
İle tek bir müşteri seçin WHERE customer = x
.
A: 0.143 ms
B: 0.072 ms
Aynı cevap diğer cevapta açıklanan indeks ile tekrarlandı
CREATE INDEX purchases_3c_idx ON purchases (customer, total DESC, id);
1A: 277.953 ms
1B: 193.547 ms
2A: 249.796 ms -- special index not used
2B: 28.679 ms
3A: 0.120 ms
3B: 0.048 ms
MAX(total)
?