Maison  >  Article  >  développement back-end  >  Comment empêcher l'écriture répétée simultanée en PHP

Comment empêcher l'écriture répétée simultanée en PHP

php中世界最好的语言
php中世界最好的语言original
2018-05-17 10:17:156688parcourir

Cette fois, je vais vous montrer comment PHP empêche l'écriture répétée simultanée. Quelles sont les précautions pour PHP pour empêcher l'écriture répétée simultanée. Voici des cas pratiques, jetons un coup d'œil.

1. Écrivez devant :

Dans l'ensemble du système de chaîne d'approvisionnement, il y aura de nombreux types de documents (bon de commande, bon de commande, bon d'arrivée, lettre de transport, etc.), en ce qui concerne l'interface d'écriture des données du document (opérations d'ajout, de suppression et de modification), même si le front-end a fait des restrictions pertinentes, il est toujours possible que des appels répétés simultanés se produisent en raison du réseau ou des opérations anormales, entraînant le même traitement du même document

Afin d'éviter que cette situation n'ait un impact anormal sur le système, nous avons mis en place un simple verrouillage de document via Redis. Chaque requête doit obtenir le verrou. avant d'exécuter la logique métier, et le verrou sera libéré une fois l'exécution terminée ; assurer le même document Une seule demande d'opérations répétées simultanées peut obtenir le verrou (en s'appuyant sur le thread unique de Redis), ce qui est une conception de verrouillage pessimiste ; 🎜>

Remarque : les verrous Redis ne sont généralement utilisés que pour résoudre les répétitions simultanées dans notre système. Dans le cas de requêtes, pour les requêtes répétées non simultanées, l'état des données sera généralement vérifié dans la base de données ou dans le journal. des deux mécanismes peut assurer la fiabilité de l’ensemble du lien.

2. Mécanisme de verrouillage :

S'appuie principalement sur l'implémentation de la commande Redis setnx :

Mais l'utilisation de setnx a Un problème est que l'instruction setnx ne prend pas en charge la définition du délai d'expiration. Vous devez utiliser l'instruction expire pour définir un délai d'attente pour la clé. De cette manière, l'ensemble de l'opération de verrouillage n'est pas une opération atomique. le verrouillage réussit, mais le programme se termine anormalement, ce qui entraîne un échec. La définition réussie du délai d'attente peut entraîner un blocage s'il n'est pas déverrouillé à temps (même si un blocage ne se produit pas dans le scénario commercial, ce n'est pas une bonne conception pour inutile). clés pour rester en mémoire);

Cette situation peut être résolue à l'aide de transactions Redis, et les deux instructions setnx et expire sont exécutées comme une opération atomique, mais cela sera relativement gênant, heureusement, dans les versions postérieures à Redis 2.6. .12, l'instruction Redis set prend en charge le mode nx, ex et prend en charge la définition atomique du délai d'expiration :

3. Implémentation du verrouillage (le code de test complet sera affiché à la fin) :

 /**
  * 加单据锁
  * @param int $intOrderId 单据ID
  * @param int $intExpireTime 锁过期时间(秒)
  * @return bool|int 加锁成功返回唯一锁ID,加锁失败返回false
  */
 public static function addLock($intOrderId, $intExpireTime = self::REDIS_LOCK_DEFAULT_EXPIRE_TIME)
 {
  //参数校验
  if (empty($intOrderId) || $intExpireTime <= 0) {
   return false;
  }
  //获取Redis连接
  $objRedisConn = self::getRedisConn();
  //生成唯一锁ID,解锁需持有此ID
  $intUniqueLockId = self::generateUniqueLockId();
  //根据模板,结合单据ID,生成唯一Redis key(一般来说,单据ID在业务中系统中唯一的)
  $strKey = sprintf(self::REDIS_LOCK_KEY_TEMPLATE, $intOrderId);
  //加锁(通过Redis setnx指令实现,从Redis 2.6.12开始,通过set指令可选参数也可以实现setnx,同时可原子化地设置超时时间)
  $bolRes = $objRedisConn->set($strKey, $intUniqueLockId, [&#39;nx&#39;, &#39;ex&#39;=>$intExpireTime]);
  //加锁成功返回锁ID,加锁失败返回false
  return $bolRes ? $intUniqueLockId : $bolRes;
 }

4. Mécanisme de déverrouillage :

Le déverrouillage signifie comparer l'identifiant de verrouillage unique lors du verrouillage. , la clé sera supprimée ; il convient de noter que l'ensemble du processus de déverrouillage doit également garantir l'atomicité, qui repose sur la mise en œuvre de la surveillance et des transactions de redis

La commande WATCH peut surveiller une ou plusieurs clés ; . Une fois l'une des clés modifiée (ou supprimée), les transactions suivantes ne seront pas exécutées. La surveillance continue jusqu'à la commande EXEC (les commandes de la transaction sont exécutées après EXEC, de sorte que la valeur clé de la surveillance WATCH peut être modifiée après la commande MULTI)

5. Implémentation de déverrouillage (terminer la le code du test sera publié à la fin) :

/**
  * 解单据锁
  * @param int $intOrderId 单据ID
  * @param int $intLockId 锁唯一ID
  * @return bool
  */
 public static function releaseLock($intOrderId, $intLockId)
 {
  //参数校验
  if (empty($intOrderId) || empty($intLockId)) {
   return false;
  }
  //获取Redis连接
  $objRedisConn = self::getRedisConn();
  //生成Redis key
  $strKey = sprintf(self::REDIS_LOCK_KEY_TEMPLATE, $intOrderId);
  //监听Redis key防止在【比对lock id】与【解锁事务执行过程中】被修改或删除,提交事务后会自动取消监控,其他情况需手动解除监控
  $objRedisConn->watch($strKey);
  if ($intLockId == $objRedisConn->get($strKey)) {
   $objRedisConn->multi()->del($strKey)->exec();
   return true;
  }
  $objRedisConn->unwatch();
  return false;
 }

6. Ci-joint le code global du test (ce code n'est qu'une version simple)

connect($strIp, $intPort);
  return $objRedis;
 }
 /**
  * 用于生成唯一的锁ID的redis key
  */
 const REDIS_LOCK_UNIQUE_ID_KEY = 'lock_unique_id';
 /**
  * 生成锁唯一ID(通过Redis incr指令实现简易版本,可结合日期、时间戳、取余、字符串填充、随机数等函数,生成指定位数唯一ID)
  * @return mixed
  */
 public static function generateUniqueLockId()
 {
  return self::getRedisConn()->incr(self::REDIS_LOCK_UNIQUE_ID_KEY);
 }
}
//test
$res1 = Lock_Service::addLock('666666');
var_dump($res1);//返回lock id,加锁成功
$res2 = Lock_Service::addLock('666666');
var_dump($res2);//false,加锁失败
$res3 = Lock_Service::releaseLock('666666', $res1);
var_dump($res3);//true,解锁成功
$res4 = Lock_Service::releaseLock('666666', $res1);
var_dump($res4);//false,解锁失败
Croyez-le Après avoir lu le cas dans cet article, vous maîtrisez la méthode. Pour des informations plus intéressantes, veuillez prêter attention aux autres articles connexes sur le site Web PHP chinois !

Lecture recommandée :

Explication détaillée des étapes pour configurer la synchronisation de session de cluster Web en PHP

Explication détaillée de les étapes pour couper et fusionner des fichiers volumineux en PHP

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn