AQO - تحسين استعلام PostgreSQL التكيفي

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



ولكن ماذا لو أن المُحسِّن قد وفر القيم الحقيقية للتكلفة والانتقائية والمعلمات الضرورية الأخرى لتنفيذ استعلام ، وعند تنفيذه مرة أخرى ، لم يتم توجيهه فقط بالإحصاءات القياسية التي تم جمعها ، ولكن أيضًا بتلك التي تم حفظها بعد التنفيذ السابق؟



وهذا ما يسمى تحسين الاستعلام التكيفي ، وأسلوب التحسين هذا واعد. تستخدم بعض DBMSs بالفعل مثل هذه التقنيات.



شركة Postgres Professional لعدة سنوات تعمل على تمديد AQO لـ PostgreSQL ، والتي تنفذ (في شكل ما) التحسين التكيفي. العمل لا يزال جاريا ، ولكن هناك بالفعل شيء لاختباره.



أولاً ، دعنا نلقي نظرة فاحصة على مجال الموضوع لتحسين الاستعلام.



لماذا يمكن للمخطط اختيار خطة دون المستوى الأمثل



يمكن تنفيذ استعلام SQL بطرق مختلفة. على سبيل المثال ، عندما يكون هناك ارتباط بين جدولين ، يمكن القيام بذلك بعدة طرق مختلفة - باستخدام الحلقات المتداخلة ، والدمج ، والتجزئة. كلما زادت الجداول المشاركة في الاستعلام ، زادت الاختلافات في صلاتها. مهمة المخطط هي اختيار خطة تنفيذ الاستعلام بأقل تكلفة من العديد من الاختلافات.



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



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



فمثلا،يؤدي نقص المعلومات الإحصائية ذات الصلة إلى حقيقة أن المخطط يركز على (على الأرجح) البيانات غير الصحيحة حول عدد الصفوف في الجداول المرتبطة. يؤدي التقليل المفرط (أو المبالغة في تقدير) الأصالة إلى اختيار الطرق غير المثلى للوصول إلى البيانات في الجداول.



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



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



يمكن أن يؤثر استخدام الوظائف في الظروف أيضًا على المجدول . وظيفة المجدول هي "الصندوق الأسود" ، ولا يعرف عدد الأسطر التي ستعيدها الوظيفة ، والتي يمكن أن تؤدي أيضًا إلى تكلفة خاطئة للخطة.



طرق التأثير على عمل المجدول



الإحصائيات الحديثة هي شرط لا غنى عنه للعمل الكافي للجدولة. بادئ ذي بدء ، تأكد من تكوين النظام لجمع المعلومات الإحصائية بانتظام.


هناك العديد من الطرق لمعالجة المواقف الموضحة أعلاه ومساعدة المخطط في اختيار خطط تنفيذ الاستعلام الأمثل.



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



عند استخدام شروط الصلة المرتبطة في الاستعلامات ، يمكنك إنشاء إحصائيات موسعة- "أخبر" المُحسِّن بوضوح أن الظروف مرتبطة ببعضها البعض. للقيام بذلك ، يحتاج DBA (أو المطور) إلى معرفة بياناتهم جيدًا وتتبع الظروف التابعة في الاستعلامات ، حيث يصعب التنبؤ مسبقًا بعدد مجموعات مجموعات الأعمدة التابعة. يجب إنشاء إحصاءات موسعة يدويًا لكل خيار من هذا القبيل.



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



إذا فشل كل شيء آخر ، يمكنك إعادة كتابة الطلب يدويًا، على سبيل المثال ، باستخدام تمثيلات ملموسة ، تعبيرات الجدول الشائعة (CTE). أو لتوضيح متطلبات مجال الموضوع ، وربما إعادة كتابة منطق الاستعلام بشكل جذري.



وهناك طريقة أخرى لل"تلميحات" لجدولة - التكيف الأمثل الاستعلام ( على daptive ف uery س ptimization). فكرة هذه الطريقة هي أنه بعد تنفيذ الاستعلام ، يتم حفظ المعلومات الإحصائية الحقيقية ، وعندما يتكرر الاستعلام المحدد (أو ما شابه) ، يمكن للمحسن الاعتماد عليه.



DBMS Postgres Pro Enterprise هو امتداد لتحسين الاستعلام التكيفي يسمى AQO... هذا الملحق منشور على github: github.com/postgrespro/aqo ، يمكن أيضًا تجربته مع vanilla PostgreSQL ، المزيد عن ذلك أدناه.



كيف تعمل الوحدة



تستخدم وحدة AQO التعلم الآلي في عملها. يمكنك قراءة المزيد حول مبدأ التشغيل في مقالة كتبها Oleg Ivanov باستخدام التعلم الآلي لزيادة إنتاجية PostgreSQL وبمزيد من التفاصيل في العرض التقديمي تحسين الاستعلام التكيفي (تقرير على YouTube ).



يتم وصف جوهر هذه الطريقة بإيجاز أدناه:



لتقدير التكلفة ، يحتاج المخطط إلى تقدير الكاردينالايت ، وبالتالي ، يلزم تقدير انتقائية الظروف.



بالنسبة إلى الشروط البسيطة (مثل "سمة = ثابت" أو "سمة> ثابت") ، يمتلك المخطط نموذجًا يُقدِّر من خلاله الانتقائية. للقيام بذلك ، يستخدم المعلومات الإحصائية: عدد قيم السمات الفريدة ، الرسوم البيانية ، إلخ.

بالنسبة للظروف التي تتكون من عناصر بسيطة باستخدام الروابط المنطقية ، يستخدم المخطط الصيغ المحسوبة بسهولة:



  • sel (not A) = 1 - sel (A)
  • sel (not A) = 1 - sel (A)
  • sel (A و B) = sel (A) * sel (B)
  • sel (A or B) = sel (not (not A and not B)) = 1 - (1 - sel (A)) * (1 - sel (B))


تفترض هذه الصيغ استقلالية (عدم الارتباط) بالشروط A و B ، ولهذا السبب نحصل على تقديرات غير صحيحة في حالة انتهاك هذا الافتراض.

تعقيد AQO الصيغة: يقدم معامله الخاص لكل حالة بسيطة. باستخدام التعلم الآلي (باستخدام الانحدار الأقرب) ، يختار AQO هذه المعاملات بحيث الانتقائية المحسوبة بالصيغة تتطابق بشكل أفضل مع الانتقائية الحقيقية التي لاحظها AQO سابقًا.



لهذا ، تحفظ الوحدة:



  • , ;
  • .


في عملها ، يميز AQO بين الشروط حتى الثوابت. وهذا يجعل من الممكن تقليل تعقيد المشكلة التي يتم حلها ، بالإضافة إلى أنه في معظم الحالات ، لا تزال المعلومات مفقودة: AQO لا "ترى" قيمة الثابت ، ولكنها "ترى" انتقائية الحالة.

الحالة التي لا تزال تحدث فيها الخسارة هي الظروف التي يتم تقييمها على أنها ثابتة بغض النظر عن القيم المحددة. على سبيل المثال ، في بعض الحالات ، لا يمكن للمخطط إجراء أي تقديرات معقولة واختيار ثابت افتراضي (على سبيل المثال ، يتم تقييم انتقائية الشرط "التعبير 1 = التعبير 2" دائمًا إلى 0.005 ، ويتم دائمًا تقييم "التعبير 1> التعبير 2" على أنه 1/3).



وهكذا ، يحسن AQO تقدير الانتقائية للظروف المعقدة (ونتيجة لذلك ، تقدير التكلفة ، والذي قد يؤدي إلى اختيار خطة تنفيذ أكثر ملاءمة).



تركيب الوحدة



لتجربة وظائف الوحدة على الفانيليا PostgreSQL ، تحتاج إلى استخدام رقعة خاصة ثم بناء النظام من المصدر. اقرأ المزيد في ملف README على github.



إذا تم استخدام Postgres Pro Enterprise ، فسيتم تثبيت الوحدة النمطية AQO في الوضع القياسي:



shared_preload_libraries = 'aqo'



بعد ذلك ، يمكنك إنشاء ملحق في قاعدة البيانات المطلوبة.



تحضير قاعدة البيانات



دعونا نلقي نظرة على مثال محدد لكيفية عمل وحدة AQO في قاعدة بيانات تجريبية . سنستخدم قاعدة بيانات كبيرة تحتوي على معلومات حول الرحلات الجوية للسنة ، من سبتمبر 2016 إلى سبتمبر 2017.



أولاً ، أنشئ امتدادًا:



CREATE EXTENSION aqo;




بعد ذلك ، قم بإيقاف تشغيل معالجة الاستعلام المتوازي - حتى لا يصرف عرض الخطط المتوازية عن المهمة الرئيسية: من



max_parallel_workers_per_gather = 0;



أجل أن يكون لجدولة PostgreSQL المزيد من الخيارات لربط الجداول ، سنقوم بإنشاء فهرسين:



CREATE INDEX ON flights (scheduled_departure );
CREATE INDEX ON ticket_flights (fare_conditions );


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



قم بزيادة التخزين المؤقت و work_mem بحيث يتم تنفيذ جميع الأعمال في ذاكرة الوصول العشوائي:



shared_buffers = '256MB';

work_mem = '256MB';




باستخدام وحدة AQO



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

دعنا ننفذ الطلب دون استخدام AQO (فيما يلي ، تمت إزالة جزء من المعلومات التي لا تؤثر على فهم عملية الوحدة من الخطط):



EXPLAIN (ANALYZE, BUFFERS, TIMING OFF) SELECT t.ticket_no
  FROM flights f
   	JOIN ticket_flights tf ON f.flight_id = tf.flight_id
   	JOIN tickets t ON tf.ticket_no = t.ticket_no
 WHERE f.scheduled_departure > '2017-08-01'::timestamptz
   AND f.actual_arrival < f.scheduled_arrival + interval '1 hour'
   AND tf.fare_conditions = 'Business';




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



Nested Loop (rows=33210) (actual rows=31677)

  Buffers: shared hit=116830 read=1

  ->  Hash Join (rows=33210) (actual rows=31677)

        Hash Cond: (tf.flight_id = f.flight_id)

        ->  Index Scan ... on ticket_flights tf  

              Index Cond: fare_conditions = 'Business'

        ->  Hash

              ->  Bitmap Heap Scan on flights f (rows=8331) (actual rows=7673)

                    Recheck Cond: scheduled_departure > '2017-08-01'

                    Filter: actual_arrival < scheduled_arrival + '01:00:00'::interval

                    ->  Bitmap Index Scan on ... [flights]

                          Index Cond: scheduled_departure > '2017-08-01'

                          Buffers: shared hit=44 read=1

  ->   Index Only Scan  ... on tickets t (rows=1 width=14) (actual rows=1 loops=31677)

        Index Cond: (ticket_no = tf.ticket_no)

        Buffers: shared hit=106205

 Planning Time: 9.326 ms

 Execution Time: 675.836 ms




Bitmap Heap Scan on flightsHash JoinIndex Scan ... on ticket_flightsNested LoopIndex Only Scan ... on tickets





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



الآن دعنا نجري تجربة ونرى كيف ستتغير الخطة المقترحة (أو لن تتغير) اعتمادًا على التغيير في التواريخ في الطلب. يتم تحديد التواريخ بطريقة تزيد بشكل تسلسلي نطاق صفوف جدول الرحلات الجوية التي تلبي الشرط ، مما يؤدي إلى خطأ في المخطط في تقييم أصل الوصول إلى هذا الجدول. توضح الخطة أعلاه أنه مع التاريخ الأول ، لا يخطئ المُحسِّن تقريبًا في الكاردينال ( ). دعونا نستبدل التواريخ التالية في الطلب واحدًا تلو الآخر:Bitmap Heap Scan on flights f (rows=8331) (actual rows=7673)







  • 2017-04-01
  • 2017-01-01
  • 2016-08-01


ودعنا نرى النتيجة:



خطط الاستعلام بدون AQO
2017-04-01



Nested Loop (rows=31677) (actual rows=292392)

  Buffers: shared hit=991756

  ->  Hash Join (rows=31677) (actual rows=292392)

        Hash Cond: (tf.flight_id = f.flight_id)

        ->  Index Scan … on ticket_flights tf

              Index Cond: fare_conditions = 'Business')

        ->  Hash

              ->  Bitmap Heap Scan on flights f (rows=7673) (actual rows=70553)

                    Recheck Cond: scheduled_departure > '2017-04-01'

                    Filter: actual_arrival < (scheduled_arrival + '01:00:00'::interval)

                    ->  Bitmap Index Scan on ... [flights]

                          Index Cond: scheduled_departure > '2017-04-01'

                          Buffers: shared hit=160

  ->  Index Only Scan ... on tickets t ( rows=1 width=14) (actual rows=1 loops=292392)

        Index Cond: (ticket_no = tf.ticket_no)

        Buffers: shared hit=980995

 Planning Time: 5.980 ms

 Execution Time: 2771.563 ms



, . . , (Bitmap Heap Scan on flights f (rows=7673) (actual rows=70553)), , Nested Loop, , .



() — Flights , ( , ).



2017-01-01



Nested Loop (rows=187710) (actual rows=484569)

  Buffers: shared hit=1640723 read=49

  ->  Hash Join (rows=187738) (actual rows=484569)

        Hash Cond: (tf.flight_id = f.flight_id)

        ->  Index Scan ... on ticket_flights tf

              Index Cond: fare_conditions = 'Business'

        ->  Hash

              ->  Seq Scan on flights f (rows=45352) (actual rows=116985)

                    Filter: scheduled_departure > '2017-01-01'::date 

                              AND actual_arrival < scheduled_arrival + '01:00:00'::interval

  ->  Index Only Scan ... on tickets t (rows=1) (actual rows=1 loops=484569)

        Index Cond: (ticket_no = tf.ticket_no)

        Buffers: shared hit=1630118 read=49

 Planning Time: 6.225 ms

 Execution Time: 4498.741 ms



, . flights, ( ) .

tickets — (1 630 118).



2016-08-01



Hash Join (rows=302200) (actual rows=771441)

   Hash Cond: (t.ticket_no = tf.ticket_no)

   Buffers: shared hit=25499 read=34643

   ->  Seq Scan on tickets t (rows=2949857) (actual rows=2949857)

   ->  Hash

         ->  Hash Join (rows=302236) (actual rows=771441)

               Hash Cond: (tf.flight_id = f.flight_id)

               ->  Index Scan on ticket_flights tf

                     Index Cond: fare_conditions = 'Business'

               ->  Hash

                     ->  Seq Scan on flights f (rows=73005) (actual rows=188563)

                           Filter: scheduled_departure > '2016-08-01'::date) 

                                     AND actual_arrival < scheduled_arrival + '01:00:00'::interval

 Planning Time: 9.990 ms

 Execution Time: 3014.577 ms



((rows=302236) (actual rows=771441)). , , : Hash Join Nested Loop.



إذا قمت بجمع ملخص موجز ، دون استخدام وحدة AQO ، فإن المجدول يعمل على النحو التالي:

تاريخ             مخازن الوقت ، مللي ثانية تعليق
2017-08-01   11631       675.836 يتم استخدام الحلقة المتداخلة ووصلة التجزئة ، ويتم فحص جداول الرحلات الجوية والتذاكر عن طريق الفهرس
2017-04-01   991 756      2771.563 نفس الخطة ، ولكنها ليست الأمثل. عند اختيار الوصول حسب الفهرس لجداول الرحلات الجوية والتذاكر ، يمكنك أن ترى أن المخطط يرتكب خطأً فادحًا عند حساب الأصالة
2017-01-01 1،640،772      4498.741 نفس الخطة دون المستوى الأمثل. لكن المخطط يقرر التبديل إلى مسح تسلسلي لجدول الرحلات الجوية.
2016-08-01       60142      3014.577 لقد تغيرت الخطة أخيرًا - يدرك المُحسِّن أنه سيتعين عليه اختيار الكثير من الصفوف من الجداول ، لذلك يستمر في مسح جداول الرحلات الجوية والتذاكر بشكل تسلسلي. يتم استبدال حلقة متداخلة غير فعالة (في هذه الحالة) بعلاقة تجزئة.
خطط الاستعلام مع AQO
AQO. :



SET aqo.mode = 'learn';



, :



2017-08-01



, , . AQO .



2017-04-01



Hash Join (rows=293891) (actual rows=292392)

  Hash Cond: (t.ticket_no = tf.ticket_no)

  Buffers: shared hit=25658 read=34640

  ->  Seq Scan on tickets t  (rows=2949857) (actual rows=2949857)

  ->  Hash

        ->  Hash Join  (rows=293734) (actual rows=292392)

              Hash Cond: (tf.flight_id = f.flight_id)

              ->  Index Scan ... on ticket_flights tf

                    Index Cond: (fare_conditions)::text = 'Business'::text

              ->  Hash

                    ->  Bitmap Heap Scan on flights f

                          Recheck Cond: scheduled_departure > '2017-04-01'::date

                          Filter: actual_arrival < scheduled_arrival + '01:00:00'::interval

                          ->  Bitmap Index Scan on ... [flights]

                                Index Cond: scheduled_departure > '2017-04-01'::date

                                Buffers: shared hit=160

 Planning Time: 9.652 ms

 Execution Time: 2218.074 ms



“”, AQO — . Tickets . . , AQO.



2017-01-01



Hash Join  (rows=484452) (actual rows=484569)

  Hash Cond: (t.ticket_no = tf.ticket_no)

  Buffers: shared hit=25534 read=34608

  ->  Seq Scan on tickets t (rows=2949857) (actual rows=2949857)

  ->  Hash (rows=484464) (actual rows=484569)

        ->  Hash Join (rows=484464) (actual rows=484569)

              Hash Cond: (tf.flight_id = f.flight_id)

              ->  Index Scan ... on ticket_flights tf

                    Index Cond: fare_conditions::text = 'Business'::text

              ->  Hash

                    ->  Seq Scan on flights f (rows=116971) (actual rows=116985)

                          Filter: scheduled_departure > '2017-01-01'::date

                                    AND actual_arrival < scheduled_arrival + '01:00:00'::interval

 Planning Time: 6.264 ms

 Execution Time: 2746.485 ms



— Flights .



2016-08-01



.



دعونا نلقي نظرة على النتيجة مرة أخرى:

تاريخ             مخازن الوقت ، مللي ثانية تعليق
2017-08-01   11631      662.966 الخطة هي نفسها كما هي بدون استخدام الوحدة
2017-04-01     60298    2218.074 باستخدام تلميحات الوحدة النمطية ، يدرك المُحسِّن أن هناك عددًا كبيرًا من السلاسل مُخططًا للانضمام إليها ، وفي هذه الخطوة يعمل بالفعل على تحسين الخطة عن طريق استبدال الحلقة المتداخلة بربط التجزئة
2017-01-01     60142    2746.485 لقد تحسنت الخطة قليلاً - بدلاً من الوصول إلى جدول الرحلات من خلال الصورة النقطية ، فإنها تستخدم مسحًا تسلسليًا
2016-08-01     60142    3253.861 ظلت الخطة دون تغيير - أفضل خطة في هذه الحالة
عندما يتم تمكين AQO ، يدرك المجدول بسرعة أنك بحاجة إلى التبديل من اتصال حلقة متداخلة واستخدام فهرس إلى صلة تجزئة ومسح تسلسلي.



لخص



هناك مزايا وعيوب لاستخدام وحدة AQO لتحسين الاستعلام التكيفي.



تتمثل إحدى مزايا استخدام الوحدة النمطية في أنه ليس من الضروري مراقبة الظروف التابعة في الاستعلامات. في بعض الحالات ، يمكن أن تزيد سرعة تنفيذ الاستعلام. وهناك طرق مختلفة لاستخدام الوحدة. على سبيل المثال ، يمكنك استخدام AQO لتحسين أنواع معينة فقط من الاستعلامات.



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



وحدة AQO ليست "رصاصة فضية" من جميع المشاكل المحتملة للجدولة. على سبيل المثال ، في بعض الحالات ، يمكن أن تحل الوحدة محل الإحصاءات المتقدمة (إذا لم تقم بإنشائها يدويًا) ، أو لن تنتبه للإحصاءات غير ذات الصلة. لكن الوحدة لن تنشئ الفهارس الضرورية ، علاوة على ذلك ، لن تعيد كتابة نص الاستعلام.



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



All Articles