لماذا يعتبر استخدام المؤشرات في SQL Server ممارسة سيئة؟

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

سؤال

كنت أعرف بعض أسباب الأداء في أيام SQL السبعة، ولكن هل لا تزال نفس المشكلات موجودة في SQL Server 2005؟إذا كان لدي مجموعة نتائج في إجراء مخزن وأريد التصرف بناءً عليها بشكل فردي، فهل لا تزال المؤشرات خيارًا سيئًا؟إذا كان الأمر كذلك لماذا؟

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

المحلول

لأن المؤشرات تشغل الذاكرة وتنشئ أقفالًا.

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

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

كل ذلك لديه القدرة على التسبب في مشكلات في الأداء للمستخدمين الآخرين.

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

نصائح أخرى

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

كمثال بسيط للغاية، لنفترض أن لدي أكثر من 100 سجل في جدول يحدد أسماء الجداول التي أريد نسخها/اقتطاعها/أيًا كان.ما هو أفضل؟تشفير SQL للقيام بما أحتاج إليه؟أو قم بالتكرار من خلال مجموعة النتائج هذه واستخدم SQL الديناميكي (sp_executesql) لتنفيذ العمليات؟

لا توجد طريقة لتحقيق الهدف أعلاه باستخدام لغة SQL المستندة إلى المجموعة.

إذن، هل تستخدم المؤشرات أم حلقة زمنية (مؤشرات زائفة)؟

مؤشرات SQL جيدة طالما أنك تستخدم الخيارات الصحيحة:

سيقوم INSENSITIVE بإنشاء نسخة مؤقتة من مجموعة النتائج الخاصة بك (مما يوفر عليك القيام بذلك بنفسك للمؤشر الزائف).

سوف يتأكد READ_ONLY من عدم وجود أقفال في مجموعة النتائج الأساسية.ستنعكس التغييرات في مجموعة النتائج الأساسية في عمليات الجلب اللاحقة (كما لو كنت تحصل على TOP 1 من المؤشر الزائف).

سيقوم FAST_FORWARD بإنشاء مؤشر محسّن لإعادة التوجيه فقط، للقراءة فقط.

اقرأ عن الخيارات المتاحة قبل الحكم على كل المؤشرات بأنها شريرة.

هناك طريقة عمل حول المؤشرات التي أستخدمها في كل مرة أحتاج إليها.

أقوم بإنشاء متغير جدول يحتوي على عمود هوية فيه.

أدخل جميع البيانات التي أحتاجها للعمل بها.

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

بهذه الطريقة لا أقفل أي شيء وأستخدم ذاكرة أقل بكثير، كما أنها آمنة، ولن أفقد أي شيء بسبب تلف الذاكرة أو شيء من هذا القبيل.

ومن السهل رؤية رمز الكتلة والتعامل معه.

هذا مثال بسيط:

DECLARE @TAB TABLE(ID INT IDENTITY, COLUMN1 VARCHAR(10), COLUMN2 VARCHAR(10))

DECLARE @COUNT INT,
        @MAX INT, 
        @CONCAT VARCHAR(MAX), 
        @COLUMN1 VARCHAR(10), 
        @COLUMN2 VARCHAR(10)

SET @COUNT = 1

INSERT INTO @TAB VALUES('TE1S', 'TE21')
INSERT INTO @TAB VALUES('TE1S', 'TE22')
INSERT INTO @TAB VALUES('TE1S', 'TE23')
INSERT INTO @TAB VALUES('TE1S', 'TE24')
INSERT INTO @TAB VALUES('TE1S', 'TE25')

SELECT @MAX = @@IDENTITY

WHILE @COUNT <= @MAX BEGIN
    SELECT @COLUMN1 = COLUMN1, @COLUMN2 = COLUMN2 FROM @TAB WHERE ID = @COUNT

    IF @CONCAT IS NULL BEGIN
        SET @CONCAT = '' 
    END ELSE BEGIN 
        SET @CONCAT = @CONCAT + ',' 
    END

    SET @CONCAT = @CONCAT + @COLUMN1 + @COLUMN2

    SET @COUNT = @COUNT + 1
END

SELECT @CONCAT

أعتقد أن المؤشرات تحصل على اسم سيئ لأن مبتدئي SQL يكتشفونها ويفكرون "مرحبًا بالحلقة!أعرف كيفية استخدامها!" ثم يستمرون في استخدامها في كل شيء.

إذا كنت تستخدمها فيما صممت من أجله، فلا يمكنني أن أجد خطأً في ذلك.

SQL هي لغة قائمة على مجموعة، وهذا ما تفعله بشكل أفضل.

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

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

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

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

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

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

إحدى الحجج لاستخدام المؤشر هي عندما تحتاج إلى معالجة الصفوف الفردية وتحديثها، خاصة لمجموعة البيانات التي لا تحتوي على مفتاح فريد جيد.في هذه الحالة، يمكنك استخدام جملة FOR UPDATE عند الإعلان عن المؤشر ومعالجة التحديثات باستخدام UPDATE...حيث الحالي.

لاحظ أن المؤشرات "من جانب الخادم" كانت شائعة (من ODBC وOLE DB)، لكن ADO.NET لا يدعمها، ولن يدعمها AFAIK أبدًا.

@ Daniel P -> لا تحتاج إلى استخدام المؤشر للقيام بذلك.يمكنك بسهولة استخدام النظرية القائمة على المجموعة للقيام بذلك.على سبيل المثال:مع اس كيو ال 2008

DECLARE @commandname NVARCHAR(1000) = '';

SELECT @commandname += 'truncate table ' + tablename + '; ';
FROM tableNames;

EXEC sp_executesql @commandname;

سوف تفعل ببساطة ما قلته أعلاه.ويمكنك أن تفعل الشيء نفسه مع Sql 2000 ولكن بناء جملة الاستعلام سيكون مختلفًا.

ومع ذلك، نصيحتي هي تجنب المؤشرات قدر الإمكان.

غايام

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

حتى مؤشر التقديم السريع في Sql Server 2005 لا يمكنه التنافس مع الاستعلامات المستندة إلى المجموعة.غالبًا ما يبدأ الرسم البياني لتدهور الأداء في الظهور وكأنه عملية n^2 مقارنةً بالعملية المستندة إلى المجموعة، والتي تميل إلى أن تكون أكثر خطية مع نمو مجموعة البيانات بشكل كبير جدًا.

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

يسمح تجنب المؤشرات لـ SQL Server بتحسين أداء الاستعلام بشكل كامل، وهو أمر مهم جدًا في الأنظمة الأكبر حجمًا.

عادة لا تكون المؤشرات هي المرض، ولكنها أحد أعراضه:عدم استخدام النهج القائم على المجموعة (كما هو مذكور في الإجابات الأخرى).

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

على سبيل المثال، استبدال تكرار المؤشر برمز تكراري آخر، مثل نقل البيانات إلى جداول مؤقتة أو متغيرات الجدول، للتكرار فوق الصفوف بطريقة مثل:

SELECT * FROM @temptable WHERE Id=@counter 

أو

SELECT TOP 1 * FROM @temptable WHERE Id>@lastId

مثل هذا النهج، كما هو موضح في رمز إجابة أخرى، يجعل الأمور أسوأ بكثير ولا يحل المشكلة الأصلية.إنه مضاد للنمط يسمى برمجة عبادة البضائع:عدم معرفة سبب كون شيء ما سيئًا وبالتالي تنفيذ شيء أسوأ لتجنبه!لقد قمت مؤخرًا بتغيير هذا الرمز (باستخدام #temptable ولا يوجد فهرس على الهوية/PK) مرة أخرى إلى المؤشر، واستغرق تحديث ما يزيد قليلاً عن 10000 صف ثانية واحدة فقط بدلاً من 3 دقائق تقريبًا.ما زلت أفتقر إلى النهج القائم على المجموعة (كوني أهون الشرين)، ولكن أفضل ما يمكنني فعله في تلك اللحظة.

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

var items = new List<Item>();
foreach(int oneId in itemIds)
{
    items.Add(dataAccess.GetItemById(oneId);
}

بدلاً من

var items = dataAccess.GetItemsByIds(itemIds);

الأول عادة ما يغمر قاعدة البيانات بعدد كبير من عمليات التحديد، رحلة واحدة ذهابًا وإيابًا لكل منها، خاصة عندما يتم تشغيل أشجار الكائنات/الرسوم البيانية وتظهر مشكلة SELECT N+1 سيئة السمعة.

هذا هو الجانب التطبيقي المتمثل في عدم فهم قواعد البيانات العلائقية والنهج القائم على التعيين، تمامًا بنفس الطريقة التي تستخدم بها المؤشرات عند استخدام كود قاعدة البيانات الإجرائية، مثل T-SQL أو PL/SQL!

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

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

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

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