سؤال

أحاول تنفيذ (ما أعتقد أنه) نموذج بيانات بسيط جدًا للعداد:

class VisitorDayTypeCounter(models.Model):
    visitType = models.CharField(max_length=60)
    visitDate = models.DateField('Visit Date')
    counter = models.IntegerField()

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

ثم نقوم بزيادة العداد وحفظه.

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

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

كيف أفعل هذا بأمان؟

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

المحلول

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

class VisitorDayTypeCounterManager(models.Manager):
    def get_query_set(self):
        qs = super(VisitorDayTypeCounterManager, self).get_query_set()

        from django.db import connection
        cursor = connection.cursor()

        pk_list = qs.values_list('id', flat=True)
        cursor.execute('UPDATE table_name SET counter = counter + 1 WHERE id IN %s', [pk_list])

        return qs

class VisitorDayTypeCounter(models.Model):
    ...

    objects = VisitorDayTypeCounterManager()

نصائح أخرى

واعتبارا من جانغو 1.1 يمكنك استخدام F () تعبيرات ORM ل.

from django.db.models import F
product = Product.objects.get(name='Venezuelan Beaver Cheese')
product.number_sold = F('number_sold') + 1
product.save()

لمزيد من التفاصيل انظر وثائق:

الشبكي: / /docs.djangoproject.com/en/1.8/ref/models/instances/#updating-attributes-based-on-existing-fields

الشبكي: //docs.djangoproject كوم / EN / 1.8 / المرجع / نماذج / عبارات / # django.db.models.F

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

ويمكنك استخدام قطعة من http://code.djangoproject.com/ticket/2705 لتأمين مستوى قاعدة الدعم.

ومع التصحيح هذا الرمز سيكون الذري:

visitors = VisitorDayTypeCounter.objects.get(day=curday).for_update()
visitors.counter += 1
visitors.save()

واقتراحين:

وإضافة unique_together إلى النموذج الخاص بك، والتفاف الخلق في معالج استثناء للقبض على التكرارات:

class VisitorDayTypeCounter(models.Model):
    visitType = models.CharField(max_length=60)
    visitDate = models.DateField('Visit Date')
    counter = models.IntegerField()
    class Meta:
        unique_together = (('visitType', 'visitDate'))

وبعد هذا، هل يمكن أن يكون stlll حالة تعارض طفيفة على التحديث العداد. اذا كان لديك ما يكفي من المرور للقلق حول ذلك، وأود أن أقترح النظر في معاملات لأدق الحبيبات السيطرة قاعدة البيانات. أنا لا أعتقد أن ORM ديه الدعم المباشر للقفل / التزامن. وثائق الصفقة هنا .

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

بمجرد إضافة القيد/المفتاح إلى الجدول، كل ما عليك فعله هو:

  1. تحقق مما إذا كان الصف موجودًا.إذا كان الأمر كذلك، إحضاره.
  2. أدخل الصف.إذا لم يكن هناك خطأ فأنت بخير ويمكنك المضي قدمًا.
  3. إذا كان هناك خطأ (أيحالة السباق)، أعد جلب الصف.إذا لم يكن هناك صف، فهذا خطأ حقيقي.خلاف ذلك، أنت بخير.

من السيئ القيام بذلك بهذه الطريقة، لكنها تبدو سريعة بما يكفي وستغطي معظم المواقف.

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

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

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