나는 적어도 (나는 적어도 생각할 때) 데이터베이스 구조를 가지고있다. 뉴스는 (News(id, source_id)
)이고, 각 뉴스에는 소스 (Source(id, url)
)가있다. 소스는 TopicSource(source_id, topic_id)
을 통해 주제 (Topic(id, title)
)로 집계됩니다. 또한 NewsRead(news_id, user_id)
을 통해 뉴스를 읽음으로 표시 할 수있는 사용자 (User(id, name)
)가 있습니다. 커다란 테이블에 읽지 않은 뉴스 계산하기
나는이 주제에 수를 읽지 않은 뉴스를 원하는 특정 사용자에 대한 : 여기에 물건을 정리하기위한 도면이다. 문제는 News
테이블이 큰 테이블 (10^6 - 10^7 행)입니다. 다행스럽게도 정확히 카운트를 알 필요가 없습니다.이 임계 값을 계산 된 값으로 반환하는 임계 값 이후에 카운트를 중지하는 것이 좋습니다.
하나의 주제에 대한 this answer에 따라 나는 다음 쿼리를 내놓았다 :
SELECT t.topic_id, count(1) as unread_count
FROM (
SELECT 1, topic_id
FROM news n
JOIN topic_source t ON n.source_id = t.source_id
-- join news_read to filter already read news
LEFT JOIN news_read r
ON (n.id = r.news_id AND r.user_id = 1)
WHERE t.topic_id = 3 AND r.user_id IS NULL
LIMIT 10 -- Threshold
) t GROUP BY t.topic_id;
(query plan 1). 이 쿼리는 테스트 데이터베이스에서 약 50ms가 걸립니다.
이제 개의 여러 주제에 대해 읽지 않은 메일을 선택하고 싶습니다.. 나는 그런 선택을 시도 :
SELECT
t.topic_id,
(SELECT count(1)
FROM (SELECT 1 FROM news n
JOIN topic_source tt ON n.source_id = tt.source_id
LEFT JOIN news_read r
ON (n.id = r.news_id AND r.user_id = 1)
WHERE tt.topic_id = t.topic_id AND r.user_id IS NULL
LIMIT 10 -- Threshold
) t) AS unread_count
FROM topic_source t WHERE t.topic_id IN (1, 2) GROUP BY t.topic_id;
(query plan 2). 그러나 나에게 알려지지 않은 이유 때문에 테스트 데이터에 대해 약 1.5 초가 걸리고 개별 쿼리의 합계는 약 0.2-0.3 초가 걸립니다.
나는 분명히 뭔가를 여기에서 놓치고있다. 두 번째 쿼리에 실수가 있습니까? 읽지 않은 뉴스를 선택하는 더 좋은 (더 빠른) 방법이 있습니까?
추가 정보 : 여기
- 는 fiddle with DB structure and queries이다.
- 저는 SQLAlchemy에서 PostgresSQL 10을 사용하고 있습니다 (그러나 아직 원시 SQL은 괜찮습니다).
테이블 크기 :
News - 10^6 - 10^7
User - 10^3
Source - 10^4
Topic - 10^3
TopicSource - 10^5
NewsRead - 10^6
UPD : 쿼리 계획은 분명히 내가 두 번째 쿼리를 엉망으로 보여줍니다. 모든 단서는 높이 평가됩니다.
UPD2 :
SELECT
id,
count(*)
FROM topic t
LEFT JOIN LATERAL (
SELECT ts.topic_id
FROM news n
LEFT JOIN news_read r
ON (n.id = r.news_id AND r.user_id = 1)
JOIN topic_source ts ON n.source_id = ts.source_id
WHERE ts.topic_id = t.id AND r.user_id IS NULL
LIMIT 10
) p ON TRUE
WHERE t.id IN (4, 10, 12, 16)
GROUP BY t.id;
(query plan 3) :는 단순히 먼저 각 topic_id
에 대한 (가장 빠른) 쿼리를 실행하도록되어있는 가입 횡 방향이 쿼리를 시도했다. 그러나 Pg planner는 이에 대해 다른 견해를 가지고있는 것으로 보입니다. 인덱스 스캔과 조인 조인 대신 매우 느린 seq 스캔과 해시 조인을 실행합니다.
내가 [이] (https://paste.ofcode.org/TMhZbxCGqiSgc3ijhzZwfX) 쿼리가 데이터에 운임 얼마나 궁금합니다. 비슷한 볼륨의 샘플 데이터를 만들려고 시도했지만 배포본이 너무 다르기 때문에 원래 쿼리에 대해서도 매우 다른 결과를 얻었습니다. 예를 들어, 다중 주제 쿼리는 ~ 19ms 만 소요됩니다 (여러 항목 중에서 가장 좋음). –
@ IljaEverilä, 의견을 보내 주셔서 감사합니다! 이 쿼리는 내 데이터에서 ~ 3.5 초 걸립니다. 나는 우리의 배포판이 꺼져있는 것 같아요. [여기에 설명이 있습니다.] (https://explain.depesz.com/s/q740). 갑자기 여러 UNION ALL이 매우 빠르다. 나는 짧은 연구 끝에 나의 포스트를 업데이트 할 것이다. – 9dogs
가장 안쪽의 하위 쿼리에서 LIMIT 10을 잊어 버렸습니다. 그것은 많은 시도 중 일부를 복사하기 위해 얻은 것입니다. 아마 그 자리에서 그걸로 조금 더 빨리 달릴 것입니다. –