간단한 DB 구조 (온라인 포럼) :
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);
약 80k 개의 항목 users
과 2,6 백만 개의 항목이 posts
테이블 에 있습니다. 게시물로 상위 100 명의 사용자를 확보하는이 간단한 쿼리는 2,4 초가 걸립니다 .
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
로 set enable_seqscan = false
더 악화 :
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
username
Postgres에서 Group by 가 필요하지 않기 때문에 누락되었습니다 (SQL Server는 username
사용자 이름을 선택하려면 그룹화해야한다고 말합니다 ). 그룹화하면 username
Postgres의 실행 시간에 약간의 ms 가 추가되거나 아무것도 수행되지 않습니다.
과학을 위해 Microsoft SQL Server를 동일한 서버 (archlinux, 8 core xeon, 24 기가 바이트 램, ssd 실행)에 설치하고 Postgres의 모든 데이터를 동일한 테이블 구조, 동일한 인덱스 및 동일한 데이터 로 마이그레이션했습니다 . 0.3 초 안에 상위 100 개 포스터를 얻는 동일한 쿼리 :
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
산출 같은 동일한 데이터의 결과를하지만, 8 배 빠르게 작업을 수행합니다. 그리고 그것은 리눅스에서 MS SQL의 베타 버전이며, "홈"OS-Windows Server에서 실행하는 것이 더 빠를 수 있습니다.
PostgreSQL 쿼리가 완전히 잘못되었거나 PostgreSQL이 느리게 작동합니까?
추가 정보
버전은 거의 최신 버전입니다 (9.6.1, 현재 최신 버전은 9.6.2, ArchLinux는 오래된 패키지 만 있으며 업데이트 속도가 매우 느립니다). 구성 :
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
출력 : https://pastebin.com/HxucRgnk
PostgreSQL을위한 가장 빠른 방법 인 GIN 및 GIST까지 사용 된 모든 인덱스를 사용해 보았습니다.
MS SQL Server 14.0.405.200-1, 기본 conf.
API에서 (분석없이 일반 선택으로) 이것을 사용하고 크롬 으로이 API 엔드 포인트를 호출하면 2500ms +-, 50ms의 HTTP 및 웹 서버 오버 헤드 오버 헤드 추가 (API 및 SQL이 동일한 서버에서 실행 됨) - 그것은 동일합니다. 나는 여기 또는 저기에 100ms를 신경 쓰지 않고, 내가 관심있는 것은 2 초 전체입니다.
explain analyze SELECT user_id, count(9) FROM posts group by user_id;
700ms가 걸립니다. posts
테이블 크기 는 2154MB입니다.
GROUP BY u.id
으로 바꾸고 GROUP BY p.user_id
시도해 볼 수 있습니까? 내 생각에, Postgres는 사용자 테이블 식별자별로 그룹화하기 때문에 먼저 N2 행을 조인합니다. 사용자 N_ 행을 가져 오기 위해 user_id 만 게시해야합니다.
posts
같은 테이블을 사용하여 나머지 테이블 에서 분리하는 것이 이치에 맞을CREATE TABLE post_content (post_id PRIMARY KEY REFERENCES posts (id), content text);
수 있습니다. 이러한 유형의 쿼리에서 '낭비되는'대부분의 I / O가 절약 될 수 있습니다. 게시물이 이보다 작 으면VACUUM FULL
onposts
이 도움 이 될 수 있습니다.