سؤال

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

لقد فكرت في عدة حلول:

تحديد القيمة القديمة في النموذج ومقارنتها بالقيمة الحالية عند الإرسال

   <input name="money"><input type="hidden" name="old_money" value="10">

ثم قبل التحديث:

   $currentmoney=value_from_query("select money from mytable","money");
   if($currentmoney!=$_REQUEST["old_money"]){
      return "value changed to $currentmoney while you were editing it, are you sure you still want to change it?!??!?!?!?";
   }
   else{
     mysql_query("update everyonesmoney set money='".intval($_REQUEST["money"])."' where user='$user_id'");
     return true;
   }

ولكن يمكن أن يكون هناك الوضع التالي:

  1. يحتاج المستخدم إلى تغيير قيمة الأموال من 9 دولارات إلى 10 دولارات

  2. admin1 يغير أمواله إلى 10 دولارات

  3. ينفق المستخدم بذكاء دولارًا واحدًا، وبالتالي تصبح أمواله الحالية 9 دولارات مرة أخرى!

  4. يقوم admin2 بتغيير أمواله إلى 10 دولارات دون سابق إنذار.

إنشاء إعداد الطابع الزمني (عمود updated_at) في الصف

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

   <input type="hidden" name="formtimestamp" value="<? echo time();?>">

ثم أثناء التحديث:

   $query_add = ($overriden ? "" : " and updated_at>'".securevalue($_REQUEST["formtimestamp"])."'");
   if(mysql_affected_rows(mysql_query("update everyonesmoney set money='".intval($_REQUEST["money"])."', updated_at=NOW() where user='$user_id' ".$query_add))==0){
      return "some values were changed by someone else while you were editing it, are you sure you still want to change it?!??!?!?!?";
   }
   else{
     return true;
   }

إنشاء ملف مؤقت بطول 0 باسم كائن/إجراء محدد

إنشاء/قفله أثناء التحديث ، والتحقق من وجوده/datestamp قبل التحديث.

قبل التحديث:

   $myfname="/tmp/user{$user_id}EDITMONEY.tmp";
   $timedifference=((time()-filectime($myfname)); //in seconds
   if(file_exists($myfname) and ($timedifference<60) and (!$overriden)){ // a minute difference
      $currentmoney=value_from_query("select money from mytable","money");
      return "money were edited by someone else $timedifference seconds ago and set to {$currentmoney}, are you sure you still want to change it?!??!?!?!?";
   }else{
      $fp = fopen("/tmp/user".intval($_REQUEST["user_id"])."EDITMONEY.tmp", "r+");         
      if (flock($fp, LOCK_EX)) { // do an exclusive lock
         mysql_query("update everyonesmoney set money='".intval($_REQUEST["money"])."' where user='$user_id'")
        flock($fp, LOCK_UN); // release the lock
        return true;
     } else {
        return "Couldn't get the lock, it's possible that someone tried to execute query simultaneously!";
     }

   fclose($fp);

   }

في الوقت الحالي، يعد إنشاء الملف هو الأسلوب المفضل لدي للأسباب التالية:

  1. أعتقد أن إنشاء ملف محلي أسرع من الوصول إلى قاعدة البيانات.

  2. لست بحاجة إلى إضافة عمود آخر (الطابع الزمني) إلى الجدول

  3. يمكنني بسهولة تعديل اسم الملف للتحقق من تعديل عمود معين، أي إنشاء ملف "money_user{$userid}_modified" عند الانتهاء من mysqlupdate.

هل هذا صحيح أم أن هناك شيء أسيء فهمه؟

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

المحلول

يمكنك تحديد القيمة القديمة في UPDATE عمليات WHERE جملة، ثم انظر إلى عدد الصفوف المتأثرة:

منح

id  name          amount
--- ------------- ---------
1   Joe User      10

يتم تنفيذ الموضوع 1

UPDATE accounts SET amount=9 WHERE id=1 AND amount=10;
=> Query Okay, 1 row(s) affected

يتم تنفيذ الموضوع 2

UPDATE accounts SET amount=9 WHERE id=1 AND amount=10;
=> Query Okay, 0 row(s) affected

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

نصائح أخرى

في حالتك أفترض أن القفل هو أفضل طريقة.يمكنك استخدام أقفال MySQL: GET_LOCK و RELEASE_LOCK و IS_FREE_LOCK.لا تضمن المعاملات في رأيي أن الصف لن يتغير أثناء تنفيذ عملية أخرى مهمتها على البيانات التي تم جلبها.

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

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

لذلك، أقترح نهجا مماثلا مع جدول التسجيل.

  • كل جدول له مفتاحه الأساسي (مثل pid)
  • قم بتسجيل اسم الجدول ورقم التعريف الشخصي في جدول السجل مع الطابع الزمني عندما يحاول شخص ما التلاعب في صف ما.
  • تحقق من جدول السجل قبل تشغيل الاستعلام.

إلقاء نظرة على InnoDB والمعاملات.هم أكثر ملاءمة للتغييرات الحساسة (أي التوازن).

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

اسمح لي أن أذكر حلين محتملين ، ربما تكون قد ذكرت أعلاه أيضًا.

يمكنك إضافة "معرف_عنوان" مع معرف حساب المسؤول الخاص بك مع طابع زمني بحيث يعرض تطبيقك تحذيرًا إذا قام شخص آخر بتحريره.

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

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