Saya menjalankan PostgresSQL 9.2 dan memiliki hubungan 12 kolom dengan sekitar 6.700.000 baris. Ini berisi node dalam ruang 3D, masing-masing referensi pengguna (yang membuatnya). Untuk menanyakan pengguna yang telah membuat berapa node, saya melakukan hal berikut (ditambahkan explain analyze
untuk informasi lebih lanjut):
EXPLAIN ANALYZE SELECT user_id, count(user_id) FROM treenode WHERE project_id=1 GROUP BY user_id;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------
HashAggregate (cost=253668.70..253669.07 rows=37 width=8) (actual time=1747.620..1747.623 rows=38 loops=1)
-> Seq Scan on treenode (cost=0.00..220278.79 rows=6677983 width=8) (actual time=0.019..886.803 rows=6677983 loops=1)
Filter: (project_id = 1)
Total runtime: 1747.653 ms
Seperti yang Anda lihat, ini membutuhkan waktu sekitar 1,7 detik. Ini tidak terlalu buruk mengingat jumlah data, tetapi saya ingin tahu apakah ini dapat ditingkatkan. Saya mencoba menambahkan indeks BTree pada kolom pengguna, tetapi ini tidak membantu sama sekali.
Apakah Anda punya saran alternatif?
Demi kelengkapan, ini adalah definisi tabel lengkap dengan semua indeksnya (tanpa batasan, referensi, dan pemicu kunci asing):
Column | Type | Modifiers
---------------+--------------------------+------------------------------------------------------
id | bigint | not null default nextval('concept_id_seq'::regclass)
user_id | bigint | not null
creation_time | timestamp with time zone | not null default now()
edition_time | timestamp with time zone | not null default now()
project_id | bigint | not null
location | double3d | not null
reviewer_id | integer | not null default (-1)
review_time | timestamp with time zone |
editor_id | integer |
parent_id | bigint |
radius | double precision | not null default 0
confidence | integer | not null default 5
skeleton_id | bigint |
Indexes:
"treenode_pkey" PRIMARY KEY, btree (id)
"treenode_id_key" UNIQUE CONSTRAINT, btree (id)
"skeleton_id_treenode_index" btree (skeleton_id)
"treenode_editor_index" btree (editor_id)
"treenode_location_x_index" btree (((location).x))
"treenode_location_y_index" btree (((location).y))
"treenode_location_z_index" btree (((location).z))
"treenode_parent_id" btree (parent_id)
"treenode_user_index" btree (user_id)
Sunting: Ini hasilnya, ketika saya menggunakan kueri (dan indeks) yang diajukan oleh @ypercube (kueri membutuhkan waktu sekitar 5,3 detik tanpa EXPLAIN ANALYZE
):
EXPLAIN ANALYZE SELECT u.id, ( SELECT COUNT(*) FROM treenode AS t WHERE t.project_id=1 AND t.user_id = u.id ) AS number_of_nodes FROM auth_user As u;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------------------------
Seq Scan on auth_user u (cost=0.00..6987937.85 rows=46 width=4) (actual time=29.934..5556.147 rows=46 loops=1)
SubPlan 1
-> Aggregate (cost=151911.65..151911.66 rows=1 width=0) (actual time=120.780..120.780 rows=1 loops=46)
-> Bitmap Heap Scan on treenode t (cost=4634.41..151460.44 rows=180486 width=0) (actual time=13.785..114.021 rows=145174 loops=46)
Recheck Cond: ((project_id = 1) AND (user_id = u.id))
Rows Removed by Index Recheck: 461076
-> Bitmap Index Scan on treenode_user_index (cost=0.00..4589.29 rows=180486 width=0) (actual time=13.082..13.082 rows=145174 loops=46)
Index Cond: ((project_id = 1) AND (user_id = u.id))
Total runtime: 5556.190 ms
(9 rows)
Time: 5556.804 ms
Sunting 2: Ini adalah hasilnya, ketika saya menggunakan index
on project_id, user_id
(tapi belum ada optimasi skema, belum) seperti yang disarankan oleh @ erwin-brandstetter (kueri berjalan dengan 1,5 detik pada kecepatan yang sama dengan permintaan asli saya):
EXPLAIN ANALYZE SELECT user_id, count(user_id) as ct FROM treenode WHERE project_id=1 GROUP BY user_id;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------
HashAggregate (cost=253670.88..253671.24 rows=37 width=8) (actual time=1807.334..1807.339 rows=38 loops=1)
-> Seq Scan on treenode (cost=0.00..220280.62 rows=6678050 width=8) (actual time=0.183..893.491 rows=6678050 loops=1)
Filter: (project_id = 1)
Total runtime: 1807.368 ms
(4 rows)
Users
denganuser_id
sebagai kunci utama?project_id
danuser_id
? Apakah tabel diperbarui terus menerus atau dapatkah Anda bekerja dengan tampilan yang terwujud (untuk beberapa waktu)?Jawaban:
Masalah utama adalah indeks yang hilang. Tetapi masih ada lagi.
Anda memiliki banyak
bigint
kolom. Mungkin berlebihan. Biasanya,integer
lebih dari cukup untuk kolom sepertiproject_id
danuser_id
. Ini juga akan membantu item berikutnya.Saat mengoptimalkan definisi tabel, pertimbangkan jawaban terkait ini, dengan penekanan pada perataan dan padding data . Tetapi sebagian besar sisanya berlaku juga:
The gajah di dalam ruangan : tidak ada indeks pada
project_id
. Buat satu. Ini lebih penting daripada sisa jawaban ini.Sementara berada di sana, buatlah indeks multikolom:
Jika Anda mengikuti saran saya,
integer
akan sempurna di sini:user_id
didefinisikanNOT NULL
, jadicount(user_id)
sama dengancount(*)
, tetapi yang terakhir sedikit lebih pendek dan lebih cepat. (Dalam permintaan khusus ini, ini bahkan akan berlaku tanpauser_id
ditentukanNOT NULL
.)id
sudah menjadi kunci utama,UNIQUE
kendala tambahan adalah pemberat yang tidak berguna . Jatuhkan:Selain: Saya tidak akan menggunakan
id
nama kolom. Gunakan sesuatu yang deskriptiftreenode_id
.Informasi tambahan
Q:
How many different project_id and user_id?
A:
not more than five different project_id
.Itu berarti Postgres harus membaca sekitar 20% dari seluruh tabel untuk memenuhi permintaan Anda. Kecuali itu dapat menggunakan pemindaian hanya indeks , pemindaian berurutan pada tabel akan lebih cepat daripada melibatkan indeks apa pun. Tidak ada lagi kinerja yang diperoleh di sini - kecuali dengan mengoptimalkan pengaturan tabel dan server.
Adapun pemindaian hanya indeks : Untuk melihat seberapa efektif itu, jalankan
VACUUM ANALYZE
jika Anda mampu (mengunci tabel secara eksklusif). Kemudian coba kueri Anda lagi. Seharusnya sekarang lebih cepat dengan hanya menggunakan indeks. Baca jawaban terkait ini terlebih dahulu:Serta halaman buku panduan ditambahkan dengan Postgres 9.6 dan Postgres Wiki pada pemindaian indeks saja .
sumber
user_id
danproject_id
integer
harus lebih dari cukup. Menggunakancount(*)
alih-alihcount(user_id)
menghemat sekitar 70 ms di sini, itu bagus untuk diketahui. Saya telah menambahkanEXPLAIN ANALYZE
kueri setelah saya menambahkan saran Andaindex
ke posting pertama. Itu tidak meningkatkan kinerja, meskipun (tetapi juga tidak ada salahnya). Sepertinyaindex
tidak digunakan sama sekali. Saya akan segera menguji optimisasi skema.seqscan
, indeks digunakan (Index Only Scan using treenode_project_id_user_id_index on treenode
), tetapi permintaan memakan waktu sekitar 2,5 detik kemudian (yaitu sekitar 1 detik lebih lama daripada dengan seqscan).Saya pertama-tama akan menambahkan indeks
(project_id, user_id)
dan kemudian dalam versi 9.3, coba kueri ini:Di 9.2, coba yang ini:
Saya menganggap Anda punya
users
meja. Jika tidak, gantiusers
dengan:(SELECT DISTINCT user_id FROM treenode)
sumber
CREATE INDEX treenode_user_index ON treenode USING btree (project_id, user_id);
tetapi saya mencoba juga tanpaUSING
klausa. Apakah saya melewatkan sesuatu?users
tabel dan berapa banyak baris yang dikembalikan oleh kueri (jadi, berapa banyak pengguna yang memilikinyaproject_id=1
)? Bisakah Anda menunjukkan penjelasan dari permintaan ini, setelah Anda menambahkan indeks?index
di tempat. Maaf bila membingungkan. Diusers
meja saya, saya punya 46 entri. Permintaan hanya mengembalikan 9 baris. Anehnya,SELECT DISTINCT user_id FROM treenode WHERE project_id=1;
mengembalikan 38 baris. Saya telah menambahkannyaexplain
ke posting pertama saya. Dan untuk mencegah kebingungan: mejakuusers
sebenarnya dipanggilauth_user
.SELECT DISTINCT user_id FROM treenode WHERE project_id=1;
mengembalikan 38 baris sedangkan permintaan hanya mengembalikan 9. Digosok.SET enable_seqscan = OFF; (Query); SET enable_seqscan = ON;