Struktur DB sederhana (untuk forum online):
CREATE TABLE users (
id integer NOT NULL PRIMARY KEY,
username text
);
CREATE INDEX ON users (username);
CREATE TABLE posts (
id integer NOT NULL PRIMARY KEY,
thread_id integer NOT NULL REFERENCES threads (id),
user_id integer NOT NULL REFERENCES users (id),
date timestamp without time zone NOT NULL,
content text
);
CREATE INDEX ON posts (thread_id);
CREATE INDEX ON posts (user_id);
Sekitar 80 users
ribu entri dalam dan 2,6 juta entri dalam posts
tabel. Permintaan sederhana ini untuk mendapatkan 100 pengguna teratas melalui pos mereka membutuhkan 2,4 detik :
EXPLAIN ANALYZE SELECT u.id, u.username, COUNT(p.id) AS PostCount FROM users u
INNER JOIN posts p on p.user_id = u.id
WHERE u.username IS NOT NULL
GROUP BY u.id
ORDER BY PostCount DESC LIMIT 100;
Limit (cost=316926.14..316926.39 rows=100 width=20) (actual time=2326.812..2326.830 rows=100 loops=1)
-> Sort (cost=316926.14..317014.83 rows=35476 width=20) (actual time=2326.809..2326.820 rows=100 loops=1)
Sort Key: (count(p.id)) DESC
Sort Method: top-N heapsort Memory: 32kB
-> HashAggregate (cost=315215.51..315570.27 rows=35476 width=20) (actual time=2311.296..2321.739 rows=34608 loops=1)
Group Key: u.id
-> Hash Join (cost=1176.89..308201.88 rows=1402727 width=16) (actual time=16.538..1784.546 rows=1910831 loops=1)
Hash Cond: (p.user_id = u.id)
-> Seq Scan on posts p (cost=0.00..286185.34 rows=1816634 width=8) (actual time=0.103..1144.681 rows=2173916 loops=1)
-> Hash (cost=733.44..733.44 rows=35476 width=12) (actual time=15.763..15.763 rows=34609 loops=1)
Buckets: 65536 Batches: 1 Memory Usage: 2021kB
-> Seq Scan on users u (cost=0.00..733.44 rows=35476 width=12) (actual time=0.033..6.521 rows=34609 loops=1)
Filter: (username IS NOT NULL)
Rows Removed by Filter: 11335
Execution time: 2301.357 ms
Dengan set enable_seqscan = false
lebih buruk lagi:
Limit (cost=1160881.74..1160881.99 rows=100 width=20) (actual time=2758.086..2758.107 rows=100 loops=1)
-> Sort (cost=1160881.74..1160970.43 rows=35476 width=20) (actual time=2758.084..2758.098 rows=100 loops=1)
Sort Key: (count(p.id)) DESC
Sort Method: top-N heapsort Memory: 32kB
-> GroupAggregate (cost=0.79..1159525.87 rows=35476 width=20) (actual time=0.095..2749.859 rows=34608 loops=1)
Group Key: u.id
-> Merge Join (cost=0.79..1152157.48 rows=1402727 width=16) (actual time=0.036..2537.064 rows=1910831 loops=1)
Merge Cond: (u.id = p.user_id)
-> Index Scan using users_pkey on users u (cost=0.29..2404.83 rows=35476 width=12) (actual time=0.016..41.163 rows=34609 loops=1)
Filter: (username IS NOT NULL)
Rows Removed by Filter: 11335
-> Index Scan using posts_user_id_index on posts p (cost=0.43..1131472.19 rows=1816634 width=8) (actual time=0.012..2191.856 rows=2173916 loops=1)
Planning time: 1.281 ms
Execution time: 2758.187 ms
Dikelompokkan oleh username
hilang di Postgres, karena itu tidak diperlukan (SQL Server mengatakan saya harus mengelompokkan berdasarkan username
jika saya ingin memilih nama pengguna). Pengelompokan dengan username
menambahkan sedikit ms ke waktu eksekusi di Postgres atau tidak melakukan apa-apa.
Untuk ilmu pengetahuan, saya telah menginstal Microsoft SQL Server ke server yang sama (yang berjalan archlinux, 8 inti xeon, 24 gb ram, SSD) dan bermigrasi semua data dari Postgres - sama struktur tabel, yang sama indeks, yang sama data. Permintaan yang sama untuk mendapatkan 100 poster teratas berjalan dalam 0,3 detik :
SELECT TOP 100 u.id, u.username, COUNT(p.id) AS PostCount FROM dbo.users u
INNER JOIN dbo.posts p on p.user_id = u.id
WHERE u.username IS NOT NULL
GROUP BY u.id, u.username
ORDER BY PostCount DESC
Menghasilkan hasil yang sama dari data yang sama, tetapi melakukannya 8 kali lebih cepat. Dan ini adalah versi beta MS SQL di Linux, saya kira menjalankannya di OS "home" - Windows Server - masih bisa lebih cepat.
Apakah permintaan PostgreSQL saya benar-benar salah, atau apakah PostgreSQL lambat?
informasi tambahan
Versi hampir merupakan yang terbaru (9.6.1, saat ini yang terbaru adalah 9.6.2, ArchLinux hanya memiliki paket yang sudah ketinggalan zaman dan sangat lambat untuk diperbarui). Konfigurasi:
max_connections = 75
shared_buffers = 3584MB
effective_cache_size = 10752MB
work_mem = 24466kB
maintenance_work_mem = 896MB
dynamic_shared_memory_type = posix
min_wal_size = 1GB
max_wal_size = 2GB
checkpoint_completion_target = 0.9
wal_buffers = 16MB
default_statistics_target = 100
EXPLAIN ANALYZE
keluaran: https://pastebin.com/HxucRgnk
Mencoba semua indeks, digunakan bahkan GIN dan GIST, cara tercepat untuk PostgreSQL (dan Googling mengonfirmasi dengan banyak baris) adalah dengan menggunakan pemindaian berurutan.
MS SQL Server 14.0.405.200-1, konfigurasi default.
Saya menggunakan ini dalam API (dengan polos pilih tanpa menganalisis), dan memanggil titik akhir API ini dengan chrome katanya butuh 2500 ms + -, tambahkan 50 ms HTTP dan overhead server web (API dan SQL berjalan di server yang sama) - itu sama. Saya tidak peduli 100 ms di sini atau di sana, yang saya pedulikan adalah dua detik penuh.
explain analyze SELECT user_id, count(9) FROM posts group by user_id;
membutuhkan 700 ms. Ukuran posts
meja adalah 2154 MB.
GROUP BY u.id
menjadi ini GROUP BY p.user_id
dan mencobanya? Dugaan saya adalah, bahwa Postgres bergabung terlebih dahulu dan dikelompokkan berdasarkan urutan kedua karena Anda mengelompokkan berdasarkan pengidentifikasi tabel pengguna, meskipun Anda hanya perlu memposting user_id untuk mendapatkan baris - N teratas.
posts
tabel, menggunakan tabel sepertiCREATE TABLE post_content (post_id PRIMARY KEY REFERENCES posts (id), content text);
itu, sebagian besar I / O yang 'terbuang' pada jenis pertanyaan ini dapat dihindarkan. Jika postingan lebih kecil dari ini, sebuahVACUUM FULL
onposts
dapat membantu.