كيفية تسريع "تحديد العد (*)" من خلال "التجميع حسب" و"أين"؟

StackOverflow https://stackoverflow.com/questions/1031312

سؤال

كيفية تسريع select count(*) مع group by?
إنه بطيء جدًا ويتم استخدامه بشكل متكرر جدًا.
لدي مشكلة كبيرة في استخدام select count(*) و group by مع جدول يحتوي على أكثر من 3,000,000 صف.

select object_title,count(*) as hot_num   
from  relations 
where relation_title='XXXX'   
group by object_title  

Relation_title, object_title هو فارشار.حيث العلاقة_العنوان='XXXX', ، والذي يقوم بإرجاع أكثر من 1,000,000 صف، يؤدي إلى تشغيل الفهارس object_title لا يمكن أن تعمل بشكل جيد.

هل كانت مفيدة؟

المحلول

إليك عدة أشياء سأحاول تجربتها، من أجل زيادة الصعوبة:

(أسهل) - تأكد من أن لديك مؤشر التغطية الصحيح

CREATE INDEX ix_temp ON relations (relation_title, object_title);

من المفترض أن يؤدي هذا إلى زيادة الأداء في ضوء مخططك الحالي، لأنه (ما لم يكن إصدار مُحسِّن mySQL الخاص بك غبيًا حقًا!) فإنه سيقلل من مقدار عمليات الإدخال/الإخراج اللازمة لتلبية استعلامك (على عكس ما إذا كان الفهرس في الترتيب العكسي حيث يكون الفهرس بأكمله يجب مسحه ضوئيًا) وسيغطي الاستعلام لذا لن تضطر إلى لمس الفهرس المجمع.

(أصعب قليلاً) - تأكد من أن حقول varchar الخاصة بك صغيرة قدر الإمكان

أحد تحديات الأداء مع فهارس varchar على MySQL هو أنه عند معالجة استعلام، سيتم سحب الحجم الكامل المعلن للحقل إلى ذاكرة الوصول العشوائي (RAM).لذا، إذا كان لديك varchar(256) ولكنك تستخدم 4 أحرف فقط، فستظل تدفع رسوم استخدام ذاكرة الوصول العشوائي (RAM) البالغة 256 بايت أثناء معالجة الاستعلام.أوه!لذلك، إذا كان بإمكانك تقليص حدود varchar الخاصة بك بسهولة، فمن المفترض أن يؤدي ذلك إلى تسريع استفساراتك.

(أصعب) - تطبيع

30% من صفوفك التي تحتوي على قيمة سلسلة واحدة هي صرخة واضحة للتطبيع في جدول آخر حتى لا تقوم بتكرار السلاسل ملايين المرات.ضع في اعتبارك التطبيع في ثلاثة جداول واستخدام معرفات صحيحة لضمها.

في بعض الحالات، يمكنك التسوية تحت الأغطية وإخفاء التسوية بطرق العرض التي تطابق اسم الجدول الحالي...فأنت تحتاج فقط إلى جعل استعلامات INSERT/UPDATE/DELETE الخاصة بك على علم بالتطبيع ولكن يمكنك ترك اختياراتك بمفردها.

(الأصعب) - قم بتجزئة أعمدة السلسلة وفهرسة التجزئة

إذا كانت التسوية تعني تغيير الكثير من التعليمات البرمجية، ولكن يمكنك تغيير مخططك قليلاً، فقد ترغب في التفكير في إنشاء تجزئات 128 بت لأعمدة السلسلة الخاصة بك (باستخدام وظيفة MD5).في هذه الحالة (على عكس التطبيع) لا يتعين عليك تغيير جميع استعلاماتك، فقط INSERTs وبعض التحديدات.على أي حال، ستحتاج إلى تجزئة حقول السلسلة الخاصة بك، ثم إنشاء فهرس للتجزئات، على سبيل المثال.

CREATE INDEX ix_temp ON relations (relation_title_hash, object_title_hash);

لاحظ أنك ستحتاج إلى التلاعب باستخدام SELECT للتأكد من أنك تقوم بالحساب عبر فهرس التجزئة وعدم سحب الفهرس المجمع (مطلوب لحل القيمة النصية الفعلية لـ object_title من أجل تلبية الاستعلام).

أيضًا، إذا كان Relation_title يحتوي على حجم varchar صغير ولكن عنوان الكائن له حجم طويل، فمن المحتمل أن تتمكن من تجزئة object_title فقط وإنشاء الفهرس عليه (relation_title, object_title_hash).

لاحظ أن هذا الحل يساعد فقط إذا كان أحد هذين الحقلين أو كليهما طويلًا جدًا بالنسبة لحجم التجزئات.

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

نصائح أخرى

إن فهرسة الأعمدة في عبارة GROUP BY هي أول شيء يجب تجربته، باستخدام فهرس مركب.يمكن الإجابة على استعلام مثل هذا باستخدام بيانات الفهرس فقط، مما يتجنب الحاجة إلى فحص الجدول على الإطلاق.نظرًا لأنه يتم فرز السجلات الموجودة في الفهرس، فلن يحتاج نظام إدارة قواعد البيانات (DBMS) إلى إجراء فرز منفصل كجزء من معالجة المجموعة.ومع ذلك، سيؤدي الفهرس إلى إبطاء تحديثات الجدول، لذا كن حذرًا في هذا إذا كان الجدول الخاص بك يواجه تحديثات كثيفة.

إذا كنت تستخدم InnoDB لتخزين الجدول، فسيتم تجميع صفوف الجدول فعليًا بواسطة فهرس المفتاح الأساسي.إذا حدث أن هذا (أو الجزء الأول منه) يطابق مفتاح GROUP BY الخاص بك، فمن المفترض أن يؤدي ذلك إلى تسريع استعلام مثل هذا لأنه سيتم استرداد السجلات ذات الصلة معًا.مرة أخرى، هذا يتجنب الاضطرار إلى إجراء فرز منفصل.

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

سيكون العرض المتجسد طريقة أخرى ممكنة، ولكن مرة أخرى هذا غير مدعوم مباشرة في MySQL.ومع ذلك، إذا لم تطلب أن تكون إحصائيات COUNT محدثة بالكامل، فيمكنك تشغيل ملف بشكل دوري CREATE TABLE ... AS SELECT ... بيان للتخزين المؤقت للنتائج يدويًا.وهذا أمر قبيح بعض الشيء لأنه غير شفاف، ولكنه قد يكون مقبولاً في حالتك.

يمكنك أيضًا الاحتفاظ بجدول ذاكرة التخزين المؤقت على المستوى المنطقي باستخدام المشغلات.سيحتوي هذا الجدول على عمود لكل عمود في عبارة GROUP BY، مع عمود Count لتخزين عدد الصفوف لقيمة مفتاح التجميع المحددة.في كل مرة تتم إضافة صف إلى الجدول الأساسي أو تحديثه، قم بإدراج أو زيادة/إنقاص صف العداد في جدول الملخص لمفتاح التجميع المعين هذا.قد يكون هذا أفضل من أسلوب العرض المادي الزائف، حيث سيكون الملخص المخزن مؤقتًا محدثًا دائمًا، ويتم إجراء كل تحديث بشكل تدريجي ويجب أن يكون له تأثير أقل على الموارد.أعتقد أنه سيتعين عليك الحذر من تنافس القفل على جدول ذاكرة التخزين المؤقت.

إذا كان لديك InnoDB، فستقوم count(*) وأي وظيفة تجميعية أخرى بإجراء فحص للجدول.أرى بعض الحلول هنا:

  1. استخدم المشغلات وقم بتخزين المجاميع في جدول منفصل.الايجابيات:نزاهة.سلبيات:تحديثات بطيئة
  2. استخدام طوابير المعالجة.الايجابيات:تحديثات سريعة.سلبيات:يمكن أن تستمر الحالة القديمة حتى تتم معالجة قائمة الانتظار، لذلك قد يشعر المستخدم بعدم النزاهة.
  3. افصل طبقة الوصول إلى التخزين بالكامل وقم بتخزين المجاميع في جدول منفصل.ستكون طبقة التخزين على دراية ببنية البيانات ويمكنها تطبيق دلتا بدلاً من إجراء عمليات تعداد كاملة.على سبيل المثال، إذا قمت بتوفير وظيفة "addObject" فستعرف متى تمت إضافة كائن وبالتالي سيتأثر التجميع.ثم تفعل فقط update table set count = count + 1.الايجابيات:التحديثات السريعة والنزاهة (قد ترغب في استخدام القفل في حالة تمكن العديد من العملاء من تغيير نفس السجل).سلبيات:يمكنك الجمع بين القليل من منطق العمل والتخزين.

أرى أن بعض الأفراد قد سألوا عن المحرك الذي كنت تستخدمه للاستعلام.أوصي بشدة باستخدام MyISAM للأسباب التالية:

InnoDB - حدد @Sorin Mocanu بشكل صحيح أنك ستقوم بإجراء مسح كامل للجدول بغض النظر عن الفهارس.

MyISAM - يحافظ دائمًا على عدد الصفوف الحالي في متناول يديك.

وأخيرًا، كما ذكر @justin، تأكد من أن لديك فهرس التغطية المناسب:

CREATE INDEX ix_temp ON relations (relation_title, object_title);

test count(myprimaryindexcolumn) and compare performance to your count(*)

هناك نقطة تحتاج فيها حقًا إلى المزيد من ذاكرة الوصول العشوائي/وحدات المعالجة المركزية/IO.ربما تكون قد وصلت إلى ذلك بالنسبة لجهازك.

سألاحظ أنه ليس من الفعال عادة استخدام الفهارس (ما لم تكن تغطي) للاستعلامات التي تصل إلى أكثر من 1-2 ٪ من إجمالي الصفوف في الجدول.

خذ هذا من :http://www.microsoft.com/communities/newsgroups/en-us/default.aspx?dg=microsoft.public.sqlserver.programming&tid=4631bab4-0104-47aa-b548-e8428073b6e6&cat=&lang=&cr=&sloc=&p= 1

إذا كان حجم الجدول بأكمله، فيجب عليك الاستعلام عن الجداول التعريفية أو مخطط المعلومات (الموجود في كل أنظمة إدارة قواعد البيانات التي أعرفها، لكنني لست متأكدًا من MySQL).إذا كان استعلامك انتقائيًا، فيجب عليك التأكد من وجود فهرس له.

AFAIK لا يوجد شيء آخر يمكنك القيام به.

أود أن أقترح أرشفة البيانات ما لم يكن هناك أي سبب محدد للاحتفاظ بها في قاعدة البيانات أو يمكنك تقسيم البيانات وتشغيل الاستعلامات بشكل منفصل.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top