سؤال

كيف يمكنك إيقاف ظروف السباق في MySQL؟المشكلة المطروحة ناتجة عن خوارزمية بسيطة:

  1. حدد صفًا من الجدول
  2. إذا لم يكن موجودا، أدخله

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

الآن أعتقد عادةً أن المعاملات تساعد هنا، ولكن نظرًا لعدم وجود الصف، فإن المعاملة لا تساعد فعليًا (أو هل أفتقد شيئًا ما؟).

يبدو LOCK TABLE وكأنه مبالغة، خاصة إذا تم تحديث الجدول عدة مرات في الثانية.

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

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

المحلول

ما تريده هو قفل الجداول

أو إذا كان هذا يبدو مفرطا ماذا عنه إدراج تجاهل مع التحقق من إدراج الصف بالفعل.

إذا كنت تستخدم الكلمة الرئيسية للتجاهل ، فسيتم التعامل مع الأخطاء التي تحدث أثناء تنفيذ بيان الإدراج كتحذيرات بدلاً من ذلك.

نصائح أخرى

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

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

أعتقد أن السؤال الأول الذي يجب عليك طرحه هو لماذا لديك العديد من سلاسل الرسائل التي تقوم بنفس العمل بالضبط؟لماذا يتعين عليهم إدراج نفس الصف بالضبط؟

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

لا توجد طريقة أخرى أعرفها.لماذا تريد تجنب الأخطاء؟لا يزال يتعين عليك ترميز الحالة عند حدوث نوع آخر من الأخطاء.

كما تقول staticsan، فإن المعاملات تساعد، ولكن، كما هو مضمن عادةً، إذا تم تشغيل إدراجين بواسطة سلاسل رسائل مختلفة، فسيكون كلاهما داخل معاملات ضمنية ويشاهدان طرق عرض متسقة لقاعدة البيانات.

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

تسمح لك بعض الأنظمة التي لا تعتمد على SQL بالقيام بالأمرين (1) و(2) في عبارة واحدة، مما يعني بشكل أو بآخر أن حالات السباق المحتملة الناشئة عن تعليق نظام التشغيل الخاص بك لسلسلة التنفيذ الخاصة بك مباشرة بين (1) و(2)، هي بالكامل مستبعد.

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

(و في الختام:بعض أنظمة إدارة قواعد البيانات - خاصة تلك التي لا يتعين عليك دفع ثمنها - لا تقدم بالفعل أي تفاصيل قفل أكثر دقة من "الجدول بأكمله".)

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

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

واجهت نفس المشكلة وبحثت في النت للحظة :)

أخيرًا توصلت إلى حل مشابه لطريقة إنشاء كائنات نظام الملفات في الدلائل المشتركة (المؤقتة) لفتح الملفات المؤقتة بشكل آمن:

$exists = $success = false;
do{
 $exists = check();// select a row in the table 
 if (!$exists)
  $success = create_record();
  if ($success){
   $exists = true;
  }else if ($success != ERROR_DUP_ROW){
    log_error("failed to create row not 'coz DUP_ROW!");
    break;
  }else{
    //probably other process has already created the record,
    //so try check again if exists
  }
}while(!$exists)

لا تخافوا من حلقة مشغولة - عادة سيتم تنفيذه مرة أو مرتين.

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

هل تهتم إذا فشل الإدخال لأنه مكرر؟هل تحتاج إلى أن يتم إعلامك إذا فشلت؟أم أن كل ما يهم هو أنه تم إدراج الصف، ولا يهم من أو عدد التكرارات التي فشلت في الإدراج؟

إذا كنت لا تهتم، فكل ما تحتاجه هو INSERT IGNORE.ليست هناك حاجة للتفكير في المعاملات أو أقفال الجدول على الإطلاق.

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

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

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

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

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

  1. يتحقق المستخدم "أ" لمعرفة ما إذا كانت التذكرة محجوزة أم لا
  2. يتحقق المستخدم "ب" لمعرفة ما إذا كانت التذكرة محجوزة أم لا
  3. يقوم المستخدم "ب" بإدراج سجل "محجوز" في الجدول الخاص بهذه التذكرة
  4. يقوم المستخدم "أ" بإدراج سجل "محجوز" في الجدول الخاص بتلك التذكرة
  5. هل يتحقق المستخدم "ب" من التكرار؟نعم، هل سجلي أحدث؟نعم، اتركه
  6. المستخدم والتحقق من التكرار؟نعم، هل سجلي أحدث؟لا، احذفه

قام المستخدم "ب" بحجز التذكرة، وأبلغ المستخدم "أ" أن شخصًا آخر قد أخذ التذكرة.

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

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