it-swarm.asia

أمثلية الانضمام على طاولة كبيرة

أحاول إقناع المزيد من الأداء من خلال طلب بحث يصل إلى جدول يحتوي على ~ 250 مليون سجل. من قراءتي لخطة التنفيذ الفعلية (غير المقدرة) ، فإن الاختناق الأول هو استعلام يبدو كما يلي:

select
    b.stuff,
    a.added,
    a.value
from
    dbo.hugetable a
    inner join
    #smalltable b on a.fk = b.pk
where
    a.added between @start and @end;

انظر إلى مزيد من أسفل للحصول على تعريفات الجداول والفهارس المعنية.

تشير خطة التنفيذ إلى أنه يتم استخدام حلقة متداخلة على #salltable ، وأن فحص الفهرس عبر hugetable يتم تنفيذه 480 مرة (لكل صف في #salltable). يبدو هذا عكسيًا بالنسبة لي ، لذا حاولت فرض استخدام دمج الدمج بدلاً من ذلك:

select
    b.stuff,
    a.added,
    a.value
from
    dbo.hugetable a with(index = ix_hugetable)
    inner merge join
    #smalltable b with(index(1)) on a.fk = b.pk
where
    a.added between @start and @end;

الفهرس المعني (انظر أدناه للحصول على تعريف كامل) يغطي الأعمدة fk (مسند الصلة) ، مضاف (يستخدم في شرط حيث) & id = (عديم الفائدة) بترتيب تصاعدي ، ويشمل القيمة.

عندما أقوم بذلك ، فإن الاستعلام ينفجر من 2 1/2 دقيقة إلى أكثر من 9. كنت أتمنى أن تفرض التلميحات صلة أكثر فعالية لا تمر إلا مرة واحدة فوق كل جدول ، ولكن من الواضح أنها لا تفعل ذلك.

نرحب بأي توجيه. يتم توفير معلومات إضافية إذا لزم الأمر.

تحديث (2011/06/02)

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

select
    b.stuff,
    datediff(month, 0, a.added),
    count(a.value),
    sum(case when a.value > 0 else 1 end) -- this triples the running time!
from
    dbo.hugetable a
    inner join
    #smalltable b on a.fk = b.pk
group by
    b.stuff,
    datediff(month, 0, a.added);

في الوقت الحاضر ، hugetable يحتوي على فهرس متفاوت المسافات pk_hugetable (added, fk) (المفتاح الأساسي) ، ومؤشر غير متفاوت المسافات يسير في الاتجاه الآخر ix_hugetable (fk, added).

بدون العمود الرابع أعلاه ، يستخدم المُحسِّن وصلة حلقة متداخلة كما كان من قبل ، باستخدام #salltable كمدخل خارجي ، ويبحث الفهرس غير المتجمع عن الحلقة الداخلية (تنفيذ 480 مرة أخرى). ما يهمني هو التفاوت بين الصفوف المقدرة (12،958.4) والصفوف الفعلية (74،668،468). التكلفة النسبية لهذه المطالب هي 45٪. وقت التشغيل هو أقل من دقيقة.

مع العمود الرابع ، يرتفع وقت التشغيل إلى 4 دقائق. وهي تبحث في الفهرس العنقودي هذه المرة (عمليتان تنفيذيتان) بنفس التكلفة النسبية (45٪) ، ويتم تجميعها عبر تطابق التجزئة (30٪) ، ثم تقوم بربط التجزئة على #Smalltable (0٪).

لست متأكدًا من مسار عملي التالي. ما يقلقني هو أنه لا يتم ضمان البحث في النطاق الزمني ولا مسند الانضمام أو حتى كل ما من المرجح أن يقلل بشكل كبير من مجموعة النتائج. سيقتطع النطاق الزمني في معظم الحالات من 10 إلى 15٪ فقط من السجلات ، وقد ينضم الرابط الداخلي في fk ربما بنسبة 20-30٪.


بناءً على طلب Will A ، فإن نتائج sp_spaceused:

name      | rows      | reserved    | data        | index_size  | unused
hugetable | 261774373 | 93552920 KB | 18373816 KB | 75167432 KB | 11672 KB

# طاولة صغيرة تُعرف على أنها:

create table #endpoints (
    pk uniqueidentifier primary key clustered,
    stuff varchar(6) null
);

بينما dbo.hugetable تُعرّف على أنها:

create table dbo.hugetable (
    id uniqueidentifier not null,
    fk uniqueidentifier not null,
    added datetime not null,
    value decimal(13, 3) not null,

    constraint pk_hugetable primary key clustered (
        fk asc,
        added asc,
        id asc
    )
    with (
        pad_index = off, statistics_norecompute = off,
        ignore_dup_key = off, allow_row_locks = on,
        allow_page_locks = on
    )
    on [primary]
)
on [primary];

مع الفهرس التالي المحدد:

create nonclustered index ix_hugetable on dbo.hugetable (
    fk asc, added asc, id asc
) include(value) with (
    pad_index = off, statistics_norecompute = off,
    sort_in_tempdb = off, ignore_dup_key = off,
    drop_existing = off, online = off,
    allow_row_locks = on, allow_page_locks = on
)
on [primary];

يعد الحقل id متكررًا ، وهو عنصر من DBA سابق أصر على أنه يجب أن تحتوي جميع الجداول في كل مكان على GUID ، ولا توجد استثناءات .

10
Quick Joe Smith

يبدو ix_hugetable عديم الفائدة إلى حد كبير للأسباب التالية:

  • هو الفهرس العنقودي (PK)
  • لا يشتمل INCLUDE على أي اختلاف لأن الفهرس العنقودي يشمل جميع الأعمدة غير الرئيسية (القيم غير الرئيسية عند أدنى حد = = INCLUDEd = ما هو الفهرس المجمع)

بالإضافة إلى ذلك: - يجب أن يكون المضاف أو fk أولاً - المعرف أولاً = ليس كثيرًا

حاول تغيير المفتاح المجمع إلى (added, fk, id) وأسقط ix_hugetable. لقد حاولت بالفعل (fk, added, id). إذا لم يكن هناك شيء آخر ، فستوفر الكثير من مساحة القرص وصيانة الفهرس

قد يكون خيار آخر هو محاولة تلميح FORCE ORDER مع طرق ترتيب الجدول boh ولا تلميحات JOIN/INDEX. أحاول عدم استخدام تلميحات JOIN/INDEX شخصيًا لأنك تزيل خيارات للمحسن. قيل لي منذ سنوات عديدة (ندوة مع جورو SQL) أن FORCE ORDER يمكن أن يساعد عندما يكون لديك طاولة ضخمة JOIN طاولة صغيرة: YMMV بعد 7 سنوات ...

أوه ، ودعنا نعرف أين يعيش DBA حتى نتمكن من ترتيب بعض تعديل الإيقاع

تحرير ، بعد تحديث 02 يونيو

لا يمثل العمود الرابع جزءًا من الفهرس غير العنقودي لذا يستخدم الفهرس العنقودي.

حاول تغيير فهرس NC إلى INCLUDE لعمود القيمة بحيث لا يضطر إلى الوصول إلى عمود القيمة للفهرس العنقودي

create nonclustered index ix_hugetable on dbo.hugetable (
    fk asc, added asc
) include(value)

ملاحظة: إذا كانت القيمة غير قابلة للإلغاء ، فإنها تكون مماثلة لـ COUNT(*) دلالة. ولكن بالنسبة لـ SUM فإنها تحتاج إلى القيمة الفعلية ، وليس الوجود .

كمثال ، إذا قمت بتغيير COUNT(value) إلى COUNT(DISTINCT value) بدون تغيير الفهرس ، يجب أن يكسر الاستعلام مرة أخرى لأنه يجب عليها معالجة القيمة كقيمة وليس كوجود.

يحتاج الاستعلام إلى 3 أعمدة: المضافة ، fk ، القيمة. يتم تصفية/ربط الأولين الأولين وكذلك الأعمدة الرئيسية. يتم استخدام القيمة فقط بحيث يمكن تضمينها. الاستخدام الكلاسيكي لمؤشر التغطية.

5
gbn

قم بتعريف فهرس على hugetable فقط على العمود added.

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

2
Bohemian

تشير خطة التنفيذ إلى أنه يتم استخدام حلقة متداخلة على #salltable ، وأن فحص الفهرس عبر hugetable يتم تنفيذه 480 مرة (لكل صف في #salltable).

هذا هو الترتيب الذي أتوقع أن يستخدمه مُحسِّن الاستعلام ، على افتراض أن هناك حلقة في الاختيار الصحيح. والبديل هو تكرار الحلقة 250 مليون مرة وإجراء بحث في جدول #temp في كل مرة - الأمر الذي قد يستغرق ساعات/أيام.

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

2
Will A

الفهرس الخاص بك غير صحيح. انظر فهارس دوس و donts .

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

حاول إضافة فهرس مجمع على hugetable(added, fk). هذا يجب أن يجعل المخطط يبحث عن صفوف قابلة للتطبيق من الجدول الضخم ، وربط حلقة أو دمجها مع الطاولة الصغيرة.

1
Denis de Bernardy