Maison développement back-end tutoriel php 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

May 17, 2018 am 10:17 AM
php 写入 répéter

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, ['nx', 'ex'=>$intExpireTime]);
  //加锁成功返回锁ID,加锁失败返回false
  return $bolRes ? $intUniqueLockId : $bolRes;
 }
Copier après la connexion

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;
 }
Copier après la connexion

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

<?php
/**
 * Class Lock_Service 单据锁服务
 */
class Lock_Service
{
 /**
  * 单据锁redis key模板
  */
 const REDIS_LOCK_KEY_TEMPLATE = 'order_lock_%s';
 /**
  * 单据锁默认超时时间(秒)
  */
 const REDIS_LOCK_DEFAULT_EXPIRE_TIME = 86400;
 /**
  * 加单据锁
  * @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 &lt;= 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-&gt;set($strKey, $intUniqueLockId, [&#39;nx&#39;, &#39;ex&#39;=&gt;$intExpireTime]);
  //加锁成功返回锁ID,加锁失败返回false
  return $bolRes ? $intUniqueLockId : $bolRes;
 }
 /**
  * 解单据锁
  * @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-&gt;watch($strKey);
  if ($intLockId == $objRedisConn-&gt;get($strKey)) {
   $objRedisConn-&gt;multi()-&gt;del($strKey)-&gt;exec();
   return true;
  }
  $objRedisConn-&gt;unwatch();
  return false;
 }
 /**
  * Redis配置:IP
  */
 const REDIS_CONFIG_HOST = '127.0.0.1';
 /**
  * Redis配置:端口
  */
 const REDIS_CONFIG_PORT = 6379;
 /**
  * 获取Redis连接(简易版本,可用单例实现)
  * @param string $strIp IP
  * @param int $intPort 端口
  * @return object Redis连接
  */
 public static function getRedisConn($strIp = self::REDIS_CONFIG_HOST, $intPort = self::REDIS_CONFIG_PORT)
 {
  $objRedis = new Redis();
  $objRedis->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,解锁失败
Copier après la connexion
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 de ce site Web
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

Article chaud

Combien de temps faut-il pour battre Split Fiction?
3 Il y a quelques semaines By DDD
Repo: Comment relancer ses coéquipiers
3 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island Adventure: Comment obtenir des graines géantes
3 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Crystals d'énergie expliqués et ce qu'ils font (cristal jaune)
1 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌

Article chaud

Combien de temps faut-il pour battre Split Fiction?
3 Il y a quelques semaines By DDD
Repo: Comment relancer ses coéquipiers
3 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island Adventure: Comment obtenir des graines géantes
3 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Crystals d'énergie expliqués et ce qu'ils font (cristal jaune)
1 Il y a quelques semaines By 尊渡假赌尊渡假赌尊渡假赌

Tags d'article chaud

Bloc-notes++7.3.1

Bloc-notes++7.3.1

Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise

SublimeText3 version chinoise

Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1

Envoyer Studio 13.0.1

Puissant environnement de développement intégré PHP

Dreamweaver CS6

Dreamweaver CS6

Outils de développement Web visuel

SublimeText3 version Mac

SublimeText3 version Mac

Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Guide d'installation et de mise à niveau de PHP 8.4 pour Ubuntu et Debian Guide d'installation et de mise à niveau de PHP 8.4 pour Ubuntu et Debian Dec 24, 2024 pm 04:42 PM

Guide d'installation et de mise à niveau de PHP 8.4 pour Ubuntu et Debian

Date et heure de CakePHP Date et heure de CakePHP Sep 10, 2024 pm 05:27 PM

Date et heure de CakePHP

Téléchargement de fichiers CakePHP Téléchargement de fichiers CakePHP Sep 10, 2024 pm 05:27 PM

Téléchargement de fichiers CakePHP

Configuration du projet CakePHP Configuration du projet CakePHP Sep 10, 2024 pm 05:25 PM

Configuration du projet CakePHP

Routage CakePHP Routage CakePHP Sep 10, 2024 pm 05:25 PM

Routage CakePHP

Discuter de CakePHP Discuter de CakePHP Sep 10, 2024 pm 05:28 PM

Discuter de CakePHP

Guide rapide CakePHP Guide rapide CakePHP Sep 10, 2024 pm 05:27 PM

Guide rapide CakePHP

Comment configurer Visual Studio Code (VS Code) pour le développement PHP Comment configurer Visual Studio Code (VS Code) pour le développement PHP Dec 20, 2024 am 11:31 AM

Comment configurer Visual Studio Code (VS Code) pour le développement PHP

See all articles