أوقات تحميل طويلة لصفحة ملخص المستخدم مع قاعدة بيانات بطيئة

مرحبًا،

نواجه أوقات تحميل طويلة جدًا عند الوصول إلى صفحة ملخص المستخدم. يبدو أن تحميل الملخص يستغرق وقتًا أطول كلما زادت تفاعلات المستخدم النشطة على موقعنا. وهذا، نعم، يبدو بديهيًا، لكن الفرق في أوقات التحميل على موقعنا هائل.
ففي حين أن تحميل صفحة ملخص لـ “مستخدم جديد” (بضعة أسابيع من النشاط) يستغرق حوالي ثانية واحدة، فإن تحميل صفحة مستخدم لديه نشاط يومي لسنوات يستغرق ما يصل إلى 30-40 ثانية.

لذلك، قمنا بفحص قاعدة البيانات وانبهرنا بحجم جدول topic_views (بما في ذلك الفهارس) الذي يبلغ 62 جيجابايت.

إليك أكبر 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

للتحقق من ذلك، اخترنا معرف مستخدم نعلم أن ملخصه يستغرق وقت تحميل طويل جدًا، وقمنا بتنفيذ عملية عداد (count) لهذا المعرف في جدول topic_views. استغرق هذا الاستعلام تقريبًا نفس الوقت الذي استغرقه تحميل ملخص المستخدم. إذن، فإن جزء المواضيع التي تم مشاهدتها هو المسؤول عن هذا التأخير.

كما هو موضح في المواضيع السابقة، قمنا بترقية موقع قديم يعتمد على Discourse (الإصدار 1.2.4 + تعديلات) إلى الإصدار 2.4.0-beta.2، ونعمل الآن بالإصدار Discourse v2.4.0-beta6. لذا، قد يكون هناك بعض البيانات المتبقية. لكننا لا نعرف ما الذي يمكن حذفه.

في هذه المرحلة، نحن غير متأكدين تمامًا مما يجب فعله أو تعديله بعد ذلك. يعمل تثبيتنا بالكامل على Azure باستخدام مثيل خارجي لقاعدة بيانات Azure لـ PostgreSQL، وقد طبقنا التكوين الموصوف في ملف postgres.10.template.yml. نحن لا نعرف حتى ما إذا كان حجم جدول topic_views (خاصة الفهرس) “كبيرًا جدًا” مقارنة بالجداول الأخرى أو إذا كان ذلك طبيعيًا تمامًا.

أي تلميح أو فكرة أو رأي موضع تقدير.

مع خالص التحية،
ساسا هوفمان

ملاحظة:
نسيت أن أذكر أنه بسبب هذا، نواجه عدة أخطاء 502 (تظهر على الموقع نفسه، وليس صفحة خطأ nginx أو بوابة التطبيق) عند تحميل /u/<username>/summary.json.

هل تشاهده كمسؤول أو موظف أم كمستخدم مجهول؟ عرض الموظفين أكثر تكلفة بكثير.

لا يوجد فرق سواء كنت أعرض /u/<username>/summary بصفتي مسؤولًا/موظفًا أو بحساب المستخدم العادي. لدينا أيضًا خيار “إخفاء ملفات المستخدمين عن الجمهور” مفعّل، لذا لا تظهر ملفات المستخدمين للزوار المجهولين (قمنا بتعطيله لأغراض الاختبار، لكن لا يغير ذلك من الأمر لأن المواضيع المُشاهدة موجودة أيضًا في الملخص).

هل قمت بنقل أو تعديل أو إعادة بناء قاعدة البيانات نفسها مرارًا وتكرارًا؟ من الممكن أن تحتاج قاعدة بياناتك إلى عملية تنظيف (VACUUM) لتحديث الإحصائيات. استعلامات تستغرق 30 ثانية تقع خارج النطاق الطبيعي الذي نلاحظه في تثبيتات Discourse (رغم اعترافنا بأن قاعدة بياناتك كبيرة).

تجربة الأمر VACUUM FULL تستحق إذا كنت تستطيع تقبل بعض التوقف المؤقت.

أيضًا، هل قاعدة البيانات الخاصة بك على تخزين SSD سريع مع ذاكرة وصول عشوائي (RAM) كبيرة جدًا؟ هذا أمر حاسم.

من أجل معرفة المشكلة بدقة:

  1. قم بتعديل ملف app.yml وأضف بريدك الإلكتروني كمسؤول إلى DISCOURSE_DEVELOPER_EMAILS.

  2. أعد البناء.

  3. افتح الصفحة البطيئة بينما أنت مسجل الدخول كالمستخدم المحدد في الخطوة 1.

  4. شارك نتائج mini-profiler.

شارك المحتويات من هنا ومن النافذة الموسعة:

لقد اختبرناها عدة مرات، لكن دائمًا بنسخة جديدة من مجموعة البيانات القديمة. بعد بضعة أسابيع، وبمجرد التأكد من أننا قمنا بعكس تغييرات المالك السابق بنجاح، قمنا بتشغيل الترقية والهجرة مرة واحدة.

هذا بالتأكيد يستحق التجربة، لكنني لست متأكدًا تمامًا من المدة التي سيستغرقها. نعم، يجب أن نأخذ في الاعتبار عملية تفريغ كاملة (full vacuum). خاصة أننا واجهنا بعض المشاكل مع قاعدة البيانات الأقدم من قبل.

نعم. جميع البيانات مخزنة على خادم SSD مدعوم بذاكرة وصول عشوائي (RAM) سعتها 40 جيجابايت (يتم استخدام حوالي 60% منها بشكل عام دون وصول إلى 100% حتى تحت أحمال أعلى).

شكرًا على هذه النصيحة، سأجربها.
بما أننا نستخدم صورة Docker خاصة بنا، حيث يكون ملف app.yml مفقودًا، فيجب أن نحقق نفس السلوك بإضافة عناوين البريد الإلكتروني إلى developer_emails في ملف discourse.conf.

يمكنك الدخول إلى وحدة تحكم Rails واستخدام Developers.create!(user_id: 123) حيث 123 هو معرف المستخدم الخاص بك.

للأسف، لا يعمل الأمر (نعم، قمت باستبدال 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 كيلوبايت)

استغرقت هذه الطلبات 13 ثانية، لكنني اضطررت إلى تحميل الموقع مرتين. مرة أخرى، استغرق المحاولة الأولى وقتًا طويلاً جدًا مما أدى إلى ظهور خطأ 502. لذا ربما تم تخزين بعض العناصر مسبقًا.

استغرق الأمر أكثر من 13 ثانية لتنفيذ 51 استعلام SQL. يبدو أن قاعدة بياناتك إما غير مُعدّة بشكل صحيح أو أن قدراتها غير كافية.

أبرز الجناة
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.3 مللي ثانية

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.3 مللي ثانية

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.5 مللي ثانية

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.2 مللي ثانية

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.6 مللي ثانية

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.3 مللي ثانية

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.5 مللي ثانية

أفترض أن خدمة Azure توفر نوعًا ما إحصائيات قاعدة بيانات للخدمة المستضافة، يجب عليك التحقق منها وتقييم ما يحتاج إلى تغيير.

كما تحقق من عنوان URL /sidekiq في تثبيتك لمعرفة ما إذا كان هناك أي تنافس في الطابور يجعل قاعدة البيانات مشغولة.

هذا هو أول ما قمنا بفحصه. مثيلنا يمتلك:

  • 8 أنوية (استخدام حوالي 16.18%، مع ارتفاعات تصل إلى 60%)
  • 40 جيجابايت من ذاكرة الوصول العشوائي (مستخدمة بنسبة 60% تقريبًا، دون ارتفاعات مفاجئة)
  • 2244 عملية إدخال/إخراج متاحة في الثانية (IOPS) (استخدام حوالي 2.5%، مع ارتفاعات تصل إلى 24%)

كما ذكرنا، استخدمنا التكوين الموصوف في ملف postgres.10.template.yml. كل شيء آخر على إعداداته الافتراضية. يبدو أننا بحاجة إلى إلقاء نظرة أخرى عليه.

يبدو Sidekiq في حالة جيدة في الوقت الحالي. 0 مشغول، 0 في قائمة الانتظار، 0 إعادة محاولة، 1 مخطط، 0 ميت. قائمة المهام فارغة حاليًا.

لذا، للوصول إلى استنتاج أولي. سنقوم أولاً بفحص تكوين PostgreSQL حيث توجد بعض التحسينات التي يمكننا تطبيقها. إذا لم ينجح ذلك، فيجب علينا النظر في ترقية مثيل PostgreSQL و/أو إجراء عملية تفريغ كامل (full vacuum).

شكرًا لك على وقتك حتى الآن، وأتمنى لك عطلة نهاية أسبوع ممتعة!
مع خالص التحيات
ساسا

إذا كان لديك 40 جيجابايت من ذاكرة الوصول العشوائي على الجهاز، فإن الإعدادات الافتراضية في postgres.10.template.yml لم تعد مناسبة، كما يُظهر samples/standalone.yml:

مرحبًا،

شكرًا لك على ردك. هناك بعض القيود عند استخدام مثيل PostgreSQL على Azure. لا يمكنك تعيين بعض الإعدادات مثل effective_cache_size وmax_connections وmaintenance_worker_mem أوshared_buffers.

في وقت كتابة هذا الطلب للدعم، كنا نستخدم طبقة “General Purpose” التي منحتنا 40 جيجابايت من ذاكرة الوصول العشوائي (RAM) ولكن فقط 1 جيجابايت من shared_buffers و2.5 جيجابايت من effective_cache_size.

منذ أمس، انتقلنا إلى طبقة “Memory Optimized” مما أدى إلى مضاعفة ذاكرة الوصول العشوائي إلى 80 جيجابايت، و2 جيجابايت من shared_buffers، و5 جيجابايت من effective_cache_size.

أما إعداد work_mem، وهو إعداد يمكننا تعديله، فهو مضبوط حاليًا على 128 ميجابايت، نظرًا لعدم وجود إدخالات لملفات مؤقتة (temporary file) في سجلاتنا.

المثير للاهتمام هو أنه بمجرد تغيير الطبقة، انخفضت استهلاك وحدة المعالجة المركزية (CPU) وعمليات الإدخال/الإخراج (IOPS) دون زيادة عدد النوى الافتراضية (vCores) أو مساحة التخزين. لذا، فإن المشكلة كانت بالتأكيد ناتجة عن ذاكرة التخزين المؤقت (caches) والمخازن المؤقتة (buffers) التي كانت صغيرة جدًا.

لا تزال لدينا أوقات تحميل طويلة عند عرض صفحات الملخص لبعض المستخدمين (خاصة المستخدمين من الموظفين أو المستخدمين طويلي الأمد) أو صفحة about، لكنها أفضل بكثير مما كانت عليه من قبل (حوالي 50% من أوقات التحميل السابقة).

مع أطيب التحيات،

ملاحظة:
بالنسبة لأولئك الذين يواجهون مشكلات مماثلة ولديهم تحكم أكبر في تثبيت PostgreSQL، قد تكون هذه الأدلة الصغيرة مفيدة:

Architecture and Tuning of Memory in PostgreSQL Databases | Severalnines

سعداء لسماع ذلك. الخطوات التالية ستكون التحقق مما إذا كان بإمكانك الاكتفاء بوحدة معالجة مركزية افتراضية (4vCPU) محسّنة للذاكرة كإجراء لتحسين التكلفة، أو الانتقال إلى تشغيلها بنفسك.

أوه، نحن نعمل بـ 8 أنوية افتراضية (vCores) مما ينتج عنه 80 جيجابايت من ذاكرة الوصول العشوائي (RAM). إعدادات shared_buffers مضبوطة على 2 جيجابايت، لكن يجب أن تكون 16 جيجابايت لهذا المستوى وعدد الأنوية الافتراضية. ومع ذلك، نحن على تواصل مع دعم Azure لإصلاح ذلك.

ملاحظة:
نحن بالفعل نستخدم 16 جيجابايت من shared_buffers. لقد أسأت تفسير مخرجات الأمر SELECT * FROM pg_settings WHERE name = 'shared_buffers';. الوحدة هي 8 كيلوبايت.

لذا أعتقد أنه يمكن إغلاق هذا الموضوع. أشكركم جميعًا على جهودكم، وآمل أن نحقق أداءً أفضل في الأيام القادمة بالتعاون مع دعم Azure.

ملاحظة إضافية:
آسف يا @riking، لقد فهمت كلامك خطأ. أعتقد أننا جاهزون للمضي قدمًا مع 8 أنوية افتراضية بدلاً من تخفيضها إلى 4. هل يمكننا تجربتها بمجرد تحقيق الأداء الذي نسعى إليه؟