سؤال

لدينا طاولتان هكذا:

Event
    id
    type
    ... a bunch of other columns

ProcessedEvent
    event_id
    process

هناك فهارس محددة ل

  • الحدث (معرف) (PK)
  • ProcessedEvent (event_id ، العملية)

الأول يمثل الأحداث في التطبيق.

والثاني يمثل حقيقة أن حدث معين حصل على عمليات من خلال عملية معينة. هناك العديد من العمليات التي تحتاج إلى معالجة حدث معين ، لذلك هناك إدخالات متعددة في الجدول الثاني لكل إدخال في الأول.

من أجل العثور على جميع الأحداث التي تحتاج إلى معالجة ، نقوم بتنفيذ الاستعلام التالي:

select * // of course we do name the columns in the production code
from Event
where type in ( 'typeA', 'typeB', 'typeC')
and id not in (
    select event_id
    from ProcessedEvent
    where process = :1  
)

الإحصائيات محدثة

نظرًا لأن معظم الأحداث تتم معالجتها ، أعتقد أن أفضل خطة تنفيذ يجب أن تبدو مثل هذا

  • فحص الفهرس الكامل على فهرس المعالجة
  • فحص الفهرس الكامل على فهرس الحدث
  • مكافحة الانضمام بين الاثنين
  • الوصول إلى الجدول مع الباقي
  • منقي

بدلا من ذلك أوراكل يفعل ما يلي

  • فحص الفهرس الكامل على فهرس المعالجة
  • مسح الجدول الكامل على جدول الحدث
  • قم بتصفية جدول الحدث
  • مكافحة الانضمام بين المجموعتين

مع تلميح فهرس أحصل على Oracle للقيام بما يلي:

  • فحص الفهرس الكامل على فهرس المعالجة
  • فحص الفهرس الكامل على فهرس الحدث
  • الجدول على جدول الحدث
  • قم بتصفية جدول الحدث
  • مكافحة الانضمام بين المجموعتين

وهو حقا غبي IMHO.

إذن سؤالي هو: ما الذي قد يكون سبب الإصرار على Oracle على الوصول المبكر إلى الجدول؟


الإضافة: الأداء سيء. نحن نصلح مشكلة الأداء عن طريق اختيار الحدث فقط ثم إحضار الصفوف المطلوبة "يدويًا". ولكن بالطبع هذا مجرد عمل حوله.

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

المحلول

من المحتمل أن يكون فحص الفهرس الكامل الخاص بك أسرع من فحص الجدول الكامل لأن الفهرس من المحتمل "أرق" من الجدول. ومع ذلك ، فإن فحص الفهرس الكامل هو قراءة شريحة كاملة وسيكون بنفس تكلفة فحص الجدول الكامل.

ومع ذلك ، فأنت تضيف أيضًا الوصول إلى الجدول بواسطة Rowid Step. إنها خطوة باهظة الثمن: IO منطقي واحد لكل صف للوصول إلى RowID بينما تحصل على IO واحد منطقي واحد لكل كتل متعددة (اعتمادا على db_file_multiblock_read_count parameter) لمسح الجدول الكامل.

في الختام ، يحسب المُحسّن:

cost(FULL TABLE SCAN) < cost(FULL INDEX SCAN) + cost(TABLE ACCESS BY ROWID)

تحديث: يمكّن فحص الجدول الكامل أيضًا المرشح على النوع في وقت أقرب مما كان عليه في مسار فحص الفهرس الكامل (نظرًا لأن الفهرس لا يعرف نوع الحدث) ، وبالتالي تقليل حجم المجموعة المعادية للانضمام (آخر ميزة مسح الجدول الكامل).

نصائح أخرى

يقوم المحسن بالعديد من الأشياء التي لا معنى لها في البداية ، ولكنها لها أسباب. قد لا يكونون دائمًا حقا, ، لكنها مفهومة.

قد يكون جدول الحدث أسهل في المسح الكامل بدلاً من الوصول إلى Rowid بسبب حجمه. قد يكون هناك عدد أقل بكثير من عمليات IO المعنية لقراءة الجدول بأكمله بالتتابع أكثر من قراءة القطع والقطع.

هل الأداء سيء ، أم أنك فقط تسأل لماذا قام المحسن بذلك؟

لا أستطيع أن أشرح سلوك المحسن ، لكن تجربتي كانت تجنب "ليس" بأي ثمن ، واستبدله بدلاً من ذلك بالمنصير ، مثل ذلك:

select * from Event
where id in (
  select id from Event where type in ( 'typeA', 'typeB', 'typeC')
 minus
  select id from ProcessedEvent
)

لقد رأيت أوامر الحجم في أداء الاستعلام مع تحولات مماثلة.

شيء مثل:

WITH
  PROCEEDED AS
  (
    SELECT
      event_id
    FROM
      ProcessedEvent
    WHERE
      PROCESS = :1
  )
SELECT
  * // of course we do name the columns in the production code
FROM
  EVENT
LEFT JOIN PROCEEDED P
ON
  p.event_id = EVENT.event_id
WHERE
  type           IN ( 'typeA', 'typeB', 'typeC')
  AND p.event_id IS NULL; -- exclude already proceeded

يمكن أن تعمل بسرعة كافية (على الأقل أسرع بكثير من NOT IN).

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