느린 데이터베이스로 사용자 요약 페이지 로딩 시간이 길어지는 문제

안녕하세요,

사용자 요약 페이지에 액세스할 때 매우 긴 로딩 시간을 겪고 있습니다. 사용자가 우리 사이트에서 더 활발하게 상호작용할수록 요약 페이지가 로딩되는 데 더 오래 걸리는 것 같습니다. 이는 당연해 보이지만, 우리 사이트에서 로딩 시간 차이는 매우 큽니다.
즉, ‘새로운’ 사용자(몇 주간의 활동)의 요약 페이지 로딩에는 약 1초가 걸리지만, 수년간 매일 활동한 사용자의 페이지를 로딩하는 데는 최대 30~40초가 걸립니다.

이에 따라 데이터베이스를 확인해 보았으며, topic_views 테이블의 크기(인덱스 포함)가 62 GB라는 사실에 꽤 놀랐습니다.

다음은 우리의 7개 가장 큰 테이블입니다:

relation total_size table_size index_size reltuples
topic_views 62 GB 17 GB 45 GB 399215000
posts 27 GB 22 GB 5022 MB 9123860
top_topics 15 GB 708 MB 15 GB 1602180
post_search_data 15 GB 12 GB 2849 MB 8181010
incoming_links 12 GB 5330 MB 6647 MB 90008900
user_actions 3184 MB 877 MB 2307 MB 11872800
topics 2117 MB 1035 MB 1082 MB 1613070

이를 테스트하기 위해 요약 페이지 로딩 시간이 매우 긴 것으로 알려진 사용자 ID를 선택하여 topic_views 테이블에서 해당 ID로 count를 수행했습니다. 이 쿼리는 사용자 요약 페이지 로딩에 걸린 시간과 거의 동일한 시간이 소요되었습니다. 따라서 본문 조회(topic viewed) 부분이 이 지연의 원인인 것으로 보입니다.

이전 주제에서 설명한 대로, 우리는 구버전(v1.2.4 + 수정 사항)의 Discourse 기반 사이트를 v2.4.0-beta.2로 업그레이드했으며, 현재 Discourse v2.4.0-beta6를 실행 중입니다. 따라서 일부 데이터가 남아 있을 수 있습니다. 그러나 무엇을 삭제할 수 있는지 알지 못합니다.

현재 우리는 다음으로 무엇을 해야 하거나 조정해야 할지 매우 불확실합니다. 우리의 설치 환경은 완전히 Azure에서 실행되며, 외부 Azure Database for PostgreSQL 인스턴스를 사용하고 있고, postgres.10.template.yml에 설명된 구성을 적용했습니다. topic_views 테이블의 크기(특히 인덱스)가 다른 테이블에 비해 “너무 큰” 것인지, 아니면 꽤 정상적인 것인지조차 알지 못합니다.

어떤 힌트, 생각, 아이디어든 환영합니다.

감사합니다
Sascha Hofmann

P.S.
이로 인해 /u/<username>/summary.json을 로딩할 때 여러 502 에러가 발생한다는 것을 언급하는 것을 잊었습니다. (사이트 자체에 표시되므로 nginx 에러 페이지나 애플리케이션 게이트웨이가 아닙니다.)

관리자/스태프 권한으로 보고 계신가요, 아니면 익명 상태로 보고 계신가요? 스태프 뷰는 훨씬 비용이 많이 듭니다.

관리자/스태프 계정으로 /u/<username>/summary를 보는지, 아니면 일반 사용자 계정으로 보는지에 따라 차이는 없습니다. 또한 hide user profiles from public 옵션이 활성화되어 있어匿名用户는 사용자 프로필을 볼 수 없습니다(테스트를 위해 비활성화해 보았지만, 요약에 topics viewed 항목도 포함되어 있어 차이가 없었습니다).

같은 데이터베이스를 반복해서 마이그레이션하거나 수정, 재구축해 보신 적 있으신가요? 통계 정보를 업데이트하기 위해 데이터베이스에 vacuum이 필요할 수 있습니다. 30초에 걸리는 쿼리는 Discourse 설치 환경에서 일반적으로 관찰되는 범위를 크게 벗어나는 수준입니다(물론 데이터베이스가 크다는 점은 인정합니다).

일부 다운타임을 감수할 수 있다면 VACUUM FULL을 시도해 볼 가치가 있습니다.

그리고 데이터베이스가 빠른 SSD 저장장치에 있고 RAM도 충분히 많나요? 그 부분이 정말 중요합니다.

문제가 정확히 무엇인지 파악하기 위해:

  1. app.yml 파일을 편집하여 관리자 사용자 이메일을 DISCOURSE_DEVELOPER_EMAILS에 추가하세요.

  2. 재빌드(Rebuild)를 수행하세요.

  3. 1단계에서 설정한 사용자로 로그인한 상태에서 느린 페이지를 로드하세요.

  4. 미니 프로파일러(mini-profiler)의 결과를 공유하세요.

이 화면과 확장된 창에서 표시되는 내용을 공유해 주세요:

여러 번 테스트했지만 항상 이전 데이터셋의 새 복사본으로 진행했습니다. 몇 주 후, 이전 소유자의 변경 사항을 성공적으로 되돌렸다고 확신이 들자 업그레이드와 마이그레이션을 한 번 실행했습니다.

그것은 확실히 시도해 볼 가치가 있지만, 얼마나 걸릴지 정확히 확신은 없습니다. 하지만 네, 전체 vacuum을 고려해야 합니다. 특히 이전 데이터베이스에서 몇 가지 문제가 있었기 때문입니다 이미.

네. 모든 데이터는 서버 SSD에 저장되어 있으며 40GB의 RAM(전체 부하 시 100%에 도달하는 플래토 없이 약 60% 사용)으로 지원되고 있습니다.

이 조언에 감사드립니다. 시도해 보겠습니다.
우리는 app.yml이 없는 자체 Docker 이미지를 사용하고 있으므로, discourse.conf의 developer_emails에 이메일 주소를 추가하여 동일한 동작을 달성해야 합니다.

rails 콘솔에 들어가서 Developers.create!(user_id: 123)를 사용할 수 있습니다. 여기서 123은 사용자의 user id입니다.

불행히도 작동하지 않습니다 (네, 123은 이미 교체했습니다 :slight_smile:):

discourse@2d5c3bf04550:~$ rails console --environment=production
Loading production environment (Rails 6.0.0)
irb(main):001:0> Developers.create!(user_id: 123)
Traceback (most recent call last):
        1: from (irb):1
NameError (uninitialized constant Developers)

다른 방법:

discourse@2d5c3bf04550:~$ rails runner --environment=production make_me_dev.rb
Traceback (most recent call last):
/var/www/discourse/bin/rails: Bootsnap::LoadPathCache::FallbackScan
	19: from /var/www/discourse/bin/rails:17:in `<main>'
	18: from /var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.0/lib/active_support/dependencies.rb:325:in `require'
	17: from /var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.0/lib/active_support/dependencies.rb:291:in `load_dependency'
	16: from /var/www/discourse/vendor/bundle/ruby/2.6.0/gems/activesupport-6.0.0/lib/active_support/dependencies.rb:325:in `block in require'
	15: from /var/www/discourse/vendor/bundle/ruby/2.6.0/gems/bootsnap-1.4.4/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
	14: from /var/www/discourse/vendor/bundle/ruby/2.6.0/gems/bootsnap-1.4.4/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:21:in `require_with_bootsnap_lfi'
	13: from /var/www/discourse/vendor/bundle/ruby/2.6.0/gems/bootsnap-1.4.4/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register'
	12: from /var/www/discourse/vendor/bundle/ruby/2.6.0/gems/bootsnap-1.4.4/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `block in require_with_bootsnap_lfi'
	11: from /var/www/discourse/vendor/bundle/ruby/2.6.0/gems/bootsnap-1.4.4/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require'
	10: from /var/www/discourse/vendor/bundle/ruby/2.6.0/gems/railties-6.0.0/lib/rails/commands.rb:18:in `<main>'
	 9: from /var/www/discourse/vendor/bundle/ruby/2.6.0/gems/railties-6.0.0/lib/rails/command.rb:46:in `invoke'
	 8: from /var/www/discourse/vendor/bundle/ruby/2.6.0/gems/railties-6.0.0/lib/rails/command/base.rb:65:in `perform'
	 7: from /var/www/discourse/vendor/bundle/ruby/2.6.0/gems/thor-0.20.3/lib/thor.rb:387:in `dispatch'
	 6: from /var/www/discourse/vendor/bundle/ruby/2.6.0/gems/thor-0.20.3/lib/thor/invocation.rb:126:in `invoke_command'
	 5: from /var/www/discourse/vendor/bundle/ruby/2.6.0/gems/thor-0.20.3/lib/thor/command.rb:27:in `run'
	 4: from /var/www/discourse/vendor/bundle/ruby/2.6.0/gems/railties-6.0.0/lib/rails/commands/runner/runner_command.rb:42:in `perform'
	 3: from /var/www/discourse/vendor/bundle/ruby/2.6.0/gems/bootsnap-1.4.4/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:52:in `load'
	 2: from /var/www/discourse/vendor/bundle/ruby/2.6.0/gems/bootsnap-1.4.4/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:69:in `rescue in load'
	 1: from /var/www/discourse/vendor/bundle/ruby/2.6.0/gems/bootsnap-1.4.4/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:69:in `load'
make_me_dev.rb:1:in `<main>': uninitialized constant Developers (NameError)
discourse@2d5c3bf04550:~$ cat make_me_dev.rb
Developers.create!(user_id: 123)

discourse.conf 방식을 시도해 보겠으며, 가능한 한 빨리 요청된 데이터를 제공하겠습니다.

지금까지 조언해 주셔서 감사합니다!

그래서 우리는 항상 사람들에게 Discourse를 설치할 때 공식적으로 지원하는 방법을 사용하도록 권고합니다. 이렇게 하면 도움이 필요할 때 표준 도구를 이용해 도움을 드릴 수 있기 때문입니다.

네, 모든 프로세스를 supervisord로 생성하기 때문일 수도 있습니다.

어쨌든 요청하신 출력 결과(압축된 html 콘텐츠)를 첨부합니다:
_u_x_strom_summary.json-13498.2ms-ProfilingResults.zip (94.5 KB)

이 요청에는 13초가 걸렸지만 사이트를 두 번 로드해야 했습니다. 다시 말하지만, 첫 번째 시도가 너무 오래 걸려 502 오류가 발생했습니다. 따라서 일부 항목이 이미 캐시에 저장되어 있었을 수도 있습니다.

51개의 SQL 쿼리를 실행하는 데 13초가 넘게 걸렸습니다. 데이터베이스가 심각하게 잘못 설정되었거나 성능이 부족합니다.

주요 문제 발생 쿼리
SELECT "posts"."id" AS t0_r0, "posts"."user_id" AS t0_r1, "posts"."topic_id" AS t0_r2, "posts"."post_number" AS t0_r3, "posts"."raw" AS t0_r4, "posts"."cooked" AS t0_r5, "posts"."created_at" AS t0_r6, "posts"."updated_at" AS t0_r7, "posts"."reply_to_post_number" AS t0_r8, "posts"."reply_count" AS t0_r9, "posts"."quote_count" AS t0_r10, "posts"."deleted_at" AS t0_r11, "posts"."off_topic_count" AS t0_r12, "posts"."like_count" AS t0_r13, "posts"."incoming_link_count" AS t0_r14, "posts"."bookmark_count" AS t0_r15, "posts"."avg_time" AS t0_r16, "posts"."score" AS t0_r17, "posts"."reads" AS t0_r18, "posts"."post_type" AS t0_r19, "posts"."sort_order" AS t0_r20, "posts"."last_editor_id" AS t0_r21, "posts"."hidden" AS t0_r22, "posts"."hidden_reason_id" AS t0_r23, "posts"."notify_moderators_count" AS t0_r24, "posts"."spam_count" AS t0_r25, "posts"."illegal_count" AS t0_r26, "posts"."inappropriate_count" AS t0_r27, "posts"."last_version_at" AS t0_r28, "posts"."user_deleted" AS t0_r29, "posts"."reply_to_user_id" AS t0_r30, "posts"."percent_rank" AS t0_r31, "posts"."notify_user_count" AS t0_r32, "posts"."like_score" AS t0_r33, "posts"."deleted_by_id" AS t0_r34, "posts"."edit_reason" AS t0_r35, "posts"."word_count" AS t0_r36, "posts"."version" AS t0_r37, "posts"."cook_method" AS t0_r38, "posts"."wiki" AS t0_r39, "posts"."baked_at" AS t0_r40, "posts"."baked_version" AS t0_r41, "posts"."hidden_at" AS t0_r42, "posts"."self_edits" AS t0_r43, "posts"."reply_quoted" AS t0_r44, "posts"."via_email" AS t0_r45, "posts"."raw_email" AS t0_r46, "posts"."public_version" AS t0_r47, "posts"."action_code" AS t0_r48, "posts"."image_url" AS t0_r49, "posts"."locked_by_id" AS t0_r50, "topics"."id" AS t1_r0, "topics"."title" AS t1_r1, "topics"."last_posted_at" AS t1_r2, "topics"."created_at" AS t1_r3, "topics"."updated_at" AS t1_r4, "topics"."views" AS t1_r5, "topics"."posts_count" AS t1_r6, "topics"."user_id" AS t1_r7, "topics"."last_post_user_id" AS t1_r8, "topics"."reply_count" AS t1_r9, "topics"."featured_user1_id" AS t1_r10, "topics"."featured_user2_id" AS t1_r11, "topics"."featured_user3_id" AS t1_r12, "topics"."avg_time" AS t1_r13, "topics"."deleted_at" AS t1_r14, "topics"."highest_post_number" AS t1_r15, "topics"."image_url" AS t1_r16, "topics"."like_count" AS t1_r17, "topics"."incoming_link_count" AS t1_r18, "topics"."category_id" AS t1_r19, "topics"."visible" AS t1_r20, "topics"."moderator_posts_count" AS t1_r21, "topics"."closed" AS t1_r22, "topics"."archived" AS t1_r23, "topics"."bumped_at" AS t1_r24, "topics"."has_summary" AS t1_r25, "topics"."archetype" AS t1_r26, "topics"."featured_user4_id" AS t1_r27, "topics"."notify_moderators_count" AS t1_r28, "topics"."spam_count" AS t1_r29, "topics"."pinned_at" AS t1_r30, "topics"."score" AS t1_r31, "topics"."percent_rank" AS t1_r32, "topics"."subtype" AS t1_r33, "topics"."slug" AS t1_r34, "topics"."deleted_by_id" AS t1_r35, "topics"."participant_count" AS t1_r36, "topics"."word_count" AS t1_r37, "topics"."excerpt" AS t1_r38, "topics"."pinned_globally" AS t1_r39, "topics"."pinned_until" AS t1_r40, "topics"."fancy_title" AS t1_r41, "topics"."highest_staff_post_number" AS t1_r42, "topics"."featured_link" AS t1_r43, "topics"."reviewable_score" AS t1_r44 FROM "posts" INNER JOIN "topics" ON ("topics"."deleted_at" IS NULL) AND "topics"."id" = "posts"."topic_id" WHERE ("posts"."deleted_at" IS NULL) AND (posts.post_type IN (1,2,3,4)) AND ("topics"."deleted_at" IS NULL) AND (topics.archetype <> 'private_message') AND "topics"."visible" = TRUE AND (topics.category_id IS NULL OR topics.category_id IN (SELECT id FROM categories WHERE NOT read_restricted OR id IN (1,3,6,100,141,202,229,233,235,248,262,594,595,609))) AND "posts"."user_id" = 643476 AND (post_number > 1) ORDER BY posts.like_count DESC, posts.created_at DESC LIMIT 6; 

소요 시간: 6248.3ms

SELECT "topic_links"."id" AS t0_r0, "topic_links"."topic_id" AS t0_r1, "topic_links"."post_id" AS t0_r2, "topic_links"."user_id" AS t0_r3, "topic_links"."url" AS t0_r4, "topic_links"."domain" AS t0_r5, "topic_links"."internal" AS t0_r6, "topic_links"."link_topic_id" AS t0_r7, "topic_links"."created_at" AS t0_r8, "topic_links"."updated_at" AS t0_r9, "topic_links"."reflection" AS t0_r10, "topic_links"."clicks" AS t0_r11, "topic_links"."link_post_id" AS t0_r12, "topic_links"."title" AS t0_r13, "topic_links"."crawled_at" AS t0_r14, "topic_links"."quote" AS t0_r15, "topic_links"."extension" AS t0_r16, "topics"."id" AS t1_r0, "topics"."title" AS t1_r1, "topics"."last_posted_at" AS t1_r2, "topics"."created_at" AS t1_r3, "topics"."updated_at" AS t1_r4, "topics"."views" AS t1_r5, "topics"."posts_count" AS t1_r6, "topics"."user_id" AS t1_r7, "topics"."last_post_user_id" AS t1_r8, "topics"."reply_count" AS t1_r9, "topics"."featured_user1_id" AS t1_r10, "topics"."featured_user2_id" AS t1_r11, "topics"."featured_user3_id" AS t1_r12, "topics"."avg_time" AS t1_r13, "topics"."deleted_at" AS t1_r14, "topics"."highest_post_number" AS t1_r15, "topics"."image_url" AS t1_r16, "topics"."like_count" AS t1_r17, "topics"."incoming_link_count" AS t1_r18, "topics"."category_id" AS t1_r19, "topics"."visible" AS t1_r20, "topics"."moderator_posts_count" AS t1_r21, "topics"."closed" AS t1_r22, "topics"."archived" AS t1_r23, "topics"."bumped_at" AS t1_r24, "topics"."has_summary" AS t1_r25, "topics"."archetype" AS t1_r26, "topics"."featured_user4_id" AS t1_r27, "topics"."notify_moderators_count" AS t1_r28, "topics"."spam_count" AS t1_r29, "topics"."pinned_at" AS t1_r30, "topics"."score" AS t1_r31, "topics"."percent_rank" AS t1_r32, "topics"."subtype" AS t1_r33, "topics"."slug" AS t1_r34, "topics"."deleted_by_id" AS t1_r35, "topics"."participant_count" AS t1_r36, "topics"."word_count" AS t1_r37, "topics"."excerpt" AS t1_r38, "topics"."pinned_globally" AS t1_r39, "topics"."pinned_until" AS t1_r40, "topics"."fancy_title" AS t1_r41, "topics"."highest_staff_post_number" AS t1_r42, "topics"."featured_link" AS t1_r43, "topics"."reviewable_score" AS t1_r44, "posts"."id" AS t2_r0, "posts"."user_id" AS t2_r1, "posts"."topic_id" AS t2_r2, "posts"."post_number" AS t2_r3, "posts"."raw" AS t2_r4, "posts"."cooked" AS t2_r5, "posts"."created_at" AS t2_r6, "posts"."updated_at" AS t2_r7, "posts"."reply_to_post_number" AS t2_r8, "posts"."reply_count" AS t2_r9, "posts"."quote_count" AS t2_r10, "posts"."deleted_at" AS t2_r11, "posts"."off_topic_count" AS t2_r12, "posts"."like_count" AS t2_r13, "posts"."incoming_link_count" AS t2_r14, "posts"."bookmark_count" AS t2_r15, "posts"."avg_time" AS t2_r16, "posts"."score" AS t2_r17, "posts"."reads" AS t2_r18, "posts"."post_type" AS t2_r19, "posts"."sort_order" AS t2_r20, "posts"."last_editor_id" AS t2_r21, "posts"."hidden" AS t2_r22, "posts"."hidden_reason_id" AS t2_r23, "posts"."notify_moderators_count" AS t2_r24, "posts"."spam_count" AS t2_r25, "posts"."illegal_count" AS t2_r26, "posts"."inappropriate_count" AS t2_r27, "posts"."last_version_at" AS t2_r28, "posts"."user_deleted" AS t2_r29, "posts"."reply_to_user_id" AS t2_r30, "posts"."percent_rank" AS t2_r31, "posts"."notify_user_count" AS t2_r32, "posts"."like_score" AS t2_r33, "posts"."deleted_by_id" AS t2_r34, "posts"."edit_reason" AS t2_r35, "posts"."word_count" AS t2_r36, "posts"."version" AS t2_r37, "posts"."cook_method" AS t2_r38, "posts"."wiki" AS t2_r39, "posts"."baked_at" AS t2_r40, "posts"."baked_version" AS t2_r41, "posts"."hidden_at" AS t2_r42, "posts"."self_edits" AS t2_r43, "posts"."reply_quoted" AS t2_r44, "posts"."via_email" AS t2_r45, "posts"."raw_email" AS t2_r46, "posts"."public_version" AS t2_r47, "posts"."action_code" AS t2_r48, "posts"."image_url" AS t2_r49, "posts"."locked_by_id" AS t2_r50 FROM "topic_links" INNER JOIN "topics" ON ("topics"."deleted_at" IS NULL) AND "topics"."id" = "topic_links"."topic_id" INNER JOIN "posts" ON ("posts"."deleted_at" IS NULL) AND "posts"."id" = "topic_links"."post_id" WHERE (posts.post_type IN (1,2,3,4)) AND ("topics"."deleted_at" IS NULL) AND (topics.archetype <> 'private_message') AND "topics"."visible" = TRUE AND (topics.category_id IS NULL OR topics.category_id IN (SELECT id FROM categories WHERE NOT read_restricted OR id IN (1,3,6,100,141,202,229,233,235,248,262,594,595,609))) AND "topic_links"."user_id" = 643476 AND "topic_links"."internal" = FALSE AND "topic_links"."reflection" = FALSE AND "topic_links"."quote" = FALSE ORDER BY clicks DESC, topic_links.created_at DESC LIMIT 6; 

소요 시간: 258.3ms

SELECT acting_user_id, COUNT(*) FROM "user_actions" INNER JOIN "topics" ON ("topics"."deleted_at" IS NULL) AND "topics"."id" = "user_actions"."target_topic_id" INNER JOIN "posts" ON ("posts"."deleted_at" IS NULL) AND "posts"."id" = "user_actions"."target_post_id" WHERE ("topics"."deleted_at" IS NULL) AND (topics.archetype <> 'private_message') AND "topics"."visible" = TRUE AND (topics.category_id IS NULL OR topics.category_id IN (SELECT id FROM categories WHERE NOT read_restricted OR id IN (1,3,6,100,141,202,229,233,235,248,262,594,595,609))) AND "user_actions"."user_id" = 643476 AND "user_actions"."action_type" = 2 GROUP BY "user_actions"."acting_user_id" ORDER BY COUNT(*) DESC LIMIT 6; 

소요 시간: 245.5ms

SELECT user_actions.user_id, COUNT(*) FROM "user_actions" INNER JOIN "topics" ON ("topics"."deleted_at" IS NULL) AND "topics"."id" = "user_actions"."target_topic_id" INNER JOIN "posts" ON ("posts"."deleted_at" IS NULL) AND "posts"."id" = "user_actions"."target_post_id" WHERE ("topics"."deleted_at" IS NULL) AND (topics.archetype <> 'private_message') AND "topics"."visible" = TRUE AND (topics.category_id IS NULL OR topics.category_id IN (SELECT id FROM categories WHERE NOT read_restricted OR id IN (1,3,6,100,141,202,229,233,235,248,262,594,595,609))) AND "user_actions"."action_type" = 2 AND "user_actions"."acting_user_id" = 643476 GROUP BY "user_actions"."user_id" ORDER BY COUNT(*) DESC LIMIT 6; 

소요 시간: 277.2ms

SELECT replies.user_id, COUNT(*) FROM "posts" INNER JOIN "topics" ON ("topics"."deleted_at" IS NULL) AND "topics"."id" = "posts"."topic_id" JOIN posts replies ON posts.topic_id = replies.topic_id AND posts.reply_to_post_number = replies.post_number WHERE ("posts"."deleted_at" IS NULL) AND (posts.post_type IN (1,2,3,4)) AND ("topics"."deleted_at" IS NULL) AND (topics.archetype <> 'private_message') AND "topics"."visible" = TRUE AND (topics.category_id IS NULL OR topics.category_id IN (SELECT id FROM categories WHERE NOT read_restricted OR id IN (1,3,6,100,141,202,229,233,235,248,262,594,595,609))) AND "posts"."user_id" = 643476 AND (replies.user_id <> 643476) GROUP BY replies.user_id ORDER BY COUNT(*) DESC LIMIT 6; 

소요 시간: 3064.6ms

SELECT category_id FROM "posts" INNER JOIN "topics" ON ("topics"."deleted_at" IS NULL) AND "topics"."id" = "posts"."topic_id" WHERE ("posts"."deleted_at" IS NULL) AND (posts.post_type IN (1,2,3,4)) AND ("topics"."deleted_at" IS NULL) AND (topics.archetype <> 'private_message') AND "topics"."visible" = TRUE AND (topics.category_id IS NULL OR topics.category_id IN (SELECT id FROM categories WHERE NOT read_restricted OR id IN (1,3,6,100,141,202,229,233,235,248,262,594,595,609))) AND "posts"."user_id" = 643476 GROUP BY topics.category_id ORDER BY COUNT(*) DESC LIMIT 6; 

소요 시간: 2283.3ms

SELECT category_id, COUNT(*) FROM "posts" INNER JOIN "topics" ON ("topics"."deleted_at" IS NULL) AND "topics"."id" = "posts"."topic_id" WHERE ("posts"."deleted_at" IS NULL) AND (posts.post_type IN (1,2,3,4)) AND ("topics"."deleted_at" IS NULL) AND (topics.archetype <> 'private_message') AND "topics"."visible" = TRUE AND (topics.category_id IS NULL OR topics.category_id IN (SELECT id FROM categories WHERE NOT read_restricted OR id IN (1,3,6,100,141,202,229,233,235,248,262,594,595,609))) AND "posts"."user_id" = 643476 AND (post_number > 1) AND (topics.category_id in (64,13,59,124,327,122)) GROUP BY topics.category_id ORDER BY COUNT(*) DESC; 

소요 시간: 811.5ms

Azure에는 호스팅 서비스에 대한 데이터베이스 통계 기능이 있을 것으로 생각됩니다. 해당 통계를 확인하고 변경이 필요한 사항을 평가해 보세요.

또한 설치 환경의 /sidekiq URL을 확인하여 데이터베이스를 바쁘게 만드는 큐 경쟁이 없는지 살펴보세요.

우리는 이것을 가장 먼저 확인했습니다. 저희 인스턴스는 다음과 같은 사양을 갖추고 있습니다.

  • 8개 코어 (약 16.18% 사용률, 최대 60%까지 스파이크)
  • 40GB RAM (약 60% 사용, 스파이크 없음)
  • 2,244개의 사용 가능한 IOPS (약 2.5% 사용률, 최대 24%까지 스파이크)

말씀드린 대로, postgres.10.template.yml에 설명된 설정을 사용했습니다. 그 외 모든 항목은 기본값으로 설정되어 있습니다. 다시 한번 살펴볼 필요가 있어 보입니다.

현재 Sidekiq 상태는 양호합니다. 바쁨 0, 대기열 0, 재시도 0, 예약 1, 실패 0. 작업 목록은 현재 비어 있습니다.

따라서 첫 번째 결론을 내리기 위해, 먼저 몇 가지 튜닝을 적용할 수 있으므로 PostgreSQL 설정을 확인하겠습니다. 만약 효과가 없다면 PostgreSQL 인스턴스를 업그레이드하고/또는 전체 vacuum을 수행하는 것을 고려해야 할 것입니다.

지금까지 시간 내주셔서 감사합니다. 즐거운 주말 보내세요!
감사합니다
Sascha

머신에 40GB의 RAM이 있는 경우, samples/standalone.yml에서 지적하듯 postgres.10.template.yml의 기본값은 더 이상 적합하지 않습니다:

안녕하세요,

답변 주셔서 감사합니다. Azure에서 PostgreSQL 인스턴스를 사용할 때 몇 가지 제한 사항이 있습니다. effective_cache_size, max_connections, maintenance_worker_mem, shared_buffers와 같은 일부 설정을 직접 변경할 수 없습니다.
이 지원 요청을 작성한 시점에는 “General Purpose” 티어를 사용하고 있었는데, 이 티어는 40GB의 RAM을 제공하지만 shared_buffers는 1GB, effective_cache_size는 2.5GB만 할당되었습니다.
어제부터 “Memory Optimized” 티어로 전환하여 RAM이 80GB로 두 배가 되었으며, shared_buffers는 2GB, effective_cache_size는 5GB로 증가했습니다.
직접 조정할 수 있는 설정인 work_mem은 현재 128MB로 설정되어 있으며, 이는 로그에 temporary file 항목이 없기 때문입니다.

흥미로운 점은 티어를 전환하자마자 vCore와 스토리지 용량을 늘리지 않았음에도 CPU와 IOPS 사용률이 떨어졌다는 것입니다. 따라서 캐시와 버퍼가 너무 작아서 문제가 발생했던 것으로 분명히 보입니다.

일부 사용자(특히 직원 계정/장기 사용자로 추정되는 계정)의 요약 페이지나 about 페이지의 로딩 시간이 여전히 긴 편이지만, 이전보다 훨씬 개선되었습니다(이전 로딩 시간의 약 50% 수준).

감사합니다.

P.S.
유사한 문제를 겪고 있으며 PostgreSQL 설치에 대한 더 많은 제어 권한을 가진 분들을 위해, 다음 작은 가이드들이 도움이 될 수 있습니다:

Architecture and Tuning of Memory in PostgreSQL Databases | Severalnines

좋습니다. 다음 단계로는 가격 최적화를 위해 4vCPU 메모리 최적화 인스턴스를 사용하는 것으로 충분할지 확인하거나, 직접 운영 방식으로 전환하는 것을 고려해 볼 수 있습니다.

우리는 현재 8 v코어를 사용 중이며, 이로 인해 80GB의 RAM이 할당됩니다. shared_buffers는 현재 2GB로 설정되어 있지만, 이 티어와 v코어 수를 고려하면 16GB로 설정해야 합니다. 현재 Azure 지원팀과 연락을 취하여 이 문제를 수정하고 있습니다.

P.S.
실제로는 이미 16GB의 shared_buffers를 사용하고 있었습니다. SELECT * FROM pg_settings WHERE name = 'shared_buffers'; 쿼리의 출력을 잘못 해석했네요. 단위가 8kb입니다.

따라서 이 주제를 닫아도 될 것 같습니다. 모든 분들의 노고에 감사드리며, Azure 지원팀과 함께 앞으로 며칠 안에 원하는 성능을 달성할 수 있기를 희망합니다.

P.P.S.
@riking님, 죄송합니다. 제가 오해하고 있었습니다. 4로 줄이는 대신 8 v코어를 유지하는 것이 더 나은 것 같습니다. 우리가 원하는 성능을 달성하는 대로 바로 시도해 볼 수 있을까요?