Tolok ukur
Menguji kandidat yang paling menarik dengan Postgres 9.4 dan 9.5 dengan meja setengah realistis 200k baris di purchases
dan 10k yang berbedacustomer_id
( rata-rata. 20 baris per pelanggan ).
Untuk Postgres 9.5 saya menjalankan tes ke-2 dengan 86446 pelanggan yang berbeda secara efektif. Lihat di bawah ( rata-rata 2.3 baris per pelanggan ).
Mendirikan
Meja utama
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
);
Saya menggunakan serial
(batasan PK yang ditambahkan di bawah) dan bilangan bulat customer_id
karena itu pengaturan yang lebih umum. Juga ditambahkan some_column
untuk menebus kolom biasanya lebih banyak.
Data dummy, PK, indeks - tabel khas juga memiliki beberapa tupel mati:
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
tabel - untuk kueri superior
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;
Dalam pengujian kedua saya untuk 9,5 saya menggunakan setup yang sama, tetapi dengan random() * 100000
menghasilkan customer_id
hanya beberapa baris per customer_id
.
Ukuran objek untuk tabel purchases
Dihasilkan dengan kueri ini .
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 | |
Pertanyaan
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()
di subquery (optimasi saya)
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. rCTE dengan LATERAL
subquery ( lihat di sini )
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. customer
tabel dengan LATERAL
( lihat di sini )
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;
Hasil
Waktu eksekusi untuk kueri di atas dengan EXPLAIN ANALYZE
(dan semua opsi tidak aktif ), terbaik dari 5 berjalan .
Semua pertanyaan menggunakan Pemindaian Hanya Indeks aktif purchases2_3c_idx
(di antara langkah-langkah lain). Beberapa dari mereka hanya untuk ukuran indeks yang lebih kecil, yang lain lebih efektif.
A. Postgres 9.4 dengan 200.000 baris dan ~ 20 per 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. Sama dengan Postgres 9.5
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. Sama seperti B., tetapi dengan ~ 2,3 baris per 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
Tolok ukur terkait
Inilah yang baru dengan pengujian "ogr" dengan baris 10M dan "pelanggan" 60r unik di Postgres 11.5 (saat ini pada September 2019). Hasil masih sejalan dengan apa yang telah kita lihat sejauh ini:
Benchmark asli (kedaluwarsa) dari 2011
Saya menjalankan tiga tes dengan PostgreSQL 9.1 pada tabel kehidupan nyata dari 65579 baris dan indeks btree satu kolom pada masing-masing dari tiga kolom yang terlibat dan mengambil waktu eksekusi terbaik dari 5 berjalan.
Membandingkan permintaan pertama @OMGPonies ( A
) dengan solusi di atasDISTINCT ON
( B
):
Pilih seluruh tabel, hasil dalam 5958 baris dalam kasus ini.
A: 567.218 ms
B: 386.673 ms
Gunakan kondisi yang WHERE customer BETWEEN x AND y
menghasilkan 1000 baris.
A: 249.136 ms
B: 55.111 ms
Pilih satu pelanggan dengan WHERE customer = x
.
A: 0.143 ms
B: 0.072 ms
Tes yang sama diulang dengan indeks yang dijelaskan dalam jawaban lainnya
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)
?