Cet article présente un exemple de verrouillage simultané en PHP. Les données sont verrouillées et un seul utilisateur est autorisé à opérer à la fois. À l'heure actuelle, le verrouillage est nécessaire.
Dans le projet de travail, vous rencontrerez quelques problèmes avec l'accès simultané de PHP pour modifier une donnée. Si les données ne sont pas verrouillées, cela provoquera des erreurs de données. Ci-dessous, j'analyserai un problème de blocage des paiements financiers. J'espère que cela aide tout le monde.
1 Aucun mécanisme de verrouillage de l'application
1.1 Code de version simplifié du paiement financier
<!--?php /** * pay.php * * 支付没有应用锁 * * Copy right (c) 2016 * * modification history: * -------------------- * 2016/9/10, by CleverCode, Create * */ //用户支付 function pay($userId,$money) { if(false == is_int($userId) || false == is_int($money)) { return false; } //取出总额 $total = getUserLeftMoney($userId); //花费大于剩余 if($money --> $total) { return false; } //余额 $left = $total - $money; //更新余额 return setUserLeftMoney($userId,$left); } //取出用户的余额 function getUserLeftMoney($userId) { if(false == is_int($userId)) { return 0; } $sql = "select account form user_account where userid = ${userId}"; //$mysql = new mysql();//mysql数据库 return $mysql->query($sql); } //更新用户余额 function setUserLeftMoney($userId,$money) { if(false == is_int($userId) || false == is_int($money)) { return false; } $sql = "update user_account set account = ${money} where userid = ${userId}"; //$mysql = new mysql();//mysql数据库 return $mysql->execute($sql); } ?>
1.2 Analyse du problème
S'il y a deux opérateurs (p et m), tous deux utilisent le numéro d'utilisateur 100. Le compte est connecté simultanément sur PC et téléphone mobile respectivement. Le solde total de 100 comptes est de 1000. L'opérateur p dépense 200 et l'opérateur m en dépense 300. Le processus simultané est le suivant.
p Opérateur :
Retirer le solde de l'utilisateur 1000.
800 restants = 1000 - 200 après paiement.
Le solde du compte mis à jour est de 800.
m Opérateur :
Retirer le solde utilisateur 1000.
700 restant après paiement = 1000 - 300.
Le solde du compte après paiement est de 700.
Après deux paiements, le solde du compte est toujours de 700. Il faudrait qu'après avoir dépensé 500, le solde du compte soit de 500. La cause fondamentale de ce phénomène est que pendant la concurrence, les données de solde obtenues par p et m en même temps sont toutes deux de 1 000.
2 Conception du verrou
L'opération de verrouillage ne comporte généralement que deux étapes, l'une consiste à obtenir le verrou (getLock) ; libérer le verrou (releaseLock) . Cependant, il existe de nombreuses façons d'implémenter les verrous dans la réalité, notamment l'implémentation de fichiers, l'implémentation de SQL et l'implémentation de Memcache. Selon ce scénario, nous envisageons d'utiliser le mode stratégie.
2.1 Le diagramme de classes est conçu comme suit
2.2 Le code source php est conçu comme suit
LockSystem.php
<!--?php /** * LockSystem.php * * php锁机制 * * Copy right (c) 2016 * * modification history: * -------------------- * 2016/9/10, by CleverCode, Create * */ class LockSystem { const LOCK_TYPE_DB = 'SQLLock'; const LOCK_TYPE_FILE = 'FileLock'; const LOCK_TYPE_MEMCACHE = 'MemcacheLock'; private $_lock = null; private static $_supportLocks = array('FileLock', 'SQLLock', 'MemcacheLock'); public function __construct($type, $options = array()) { if(false == empty($type)) { $this--->createLock($type, $options); } } public function createLock($type, $options=array()) { if (false == in_array($type, self::$_supportLocks)) { throw new Exception("not support lock of ${type}"); } $this->_lock = new $type($options); } public function getLock($key, $timeout = ILock::EXPIRE) { if (false == $this->_lock instanceof ILock) { throw new Exception('false == $this->_lock instanceof ILock'); } $this->_lock->getLock($key, $timeout); } public function releaseLock($key) { if (false == $this->_lock instanceof ILock) { throw new Exception('false == $this->_lock instanceof ILock'); } $this->_lock->releaseLock($key); } } interface ILock { const EXPIRE = 5; public function getLock($key, $timeout=self::EXPIRE); public function releaseLock($key); } class FileLock implements ILock { private $_fp; private $_single; public function __construct($options) { if (isset($options['path']) && is_dir($options['path'])) { $this->_lockPath = $options['path'].'/'; } else { $this->_lockPath = '/tmp/'; } $this->_single = isset($options['single'])?$options['single']:false; } public function getLock($key, $timeout=self::EXPIRE) { $startTime = Timer::getTimeStamp(); $file = md5(__FILE__.$key); $this->fp = fopen($this->_lockPath.$file.'.lock', "w+"); if (true || $this->_single) { $op = LOCK_EX + LOCK_NB; } else { $op = LOCK_EX; } if (false == flock($this->fp, $op, $a)) { throw new Exception('failed'); } return true; } public function releaseLock($key) { flock($this->fp, LOCK_UN); fclose($this->fp); } } class SQLLock implements ILock { public function __construct($options) { $this->_db = new mysql(); } public function getLock($key, $timeout=self::EXPIRE) { $sql = "SELECT GET_LOCK('".$key."', '".$timeout."')"; $res = $this->_db->query($sql); return $res; } public function releaseLock($key) { $sql = "SELECT RELEASE_LOCK('".$key."')"; return $this->_db->query($sql); } } class MemcacheLock implements ILock { public function __construct($options) { $this->memcache = new Memcache(); } public function getLock($key, $timeout=self::EXPIRE) { $waitime = 20000; $totalWaitime = 0; $time = $timeout*1000000; while ($totalWaitime < $time && false == $this->memcache->add($key, 1, $timeout)) { usleep($waitime); $totalWaitime += $waitime; } if ($totalWaitime >= $time) throw new Exception('can not get lock for waiting '.$timeout.'s.'); } public function releaseLock($key) { $this->memcache->delete($key); } }
3 Mécanisme de verrouillage des applications
3.1 Verrouillage de l'application du système de paiement
<!--?php /** * pay.php * * 支付应用锁 * * Copy right (c) 2016 * * modification history: * -------------------- * 2016/9/10, by CleverCode, Create * */ //用户支付 function pay($userId,$money) { if(false == is_int($userId) || false == is_int($money)) { return false; } try { //创建锁(推荐使用MemcacheLock) $lockSystem = new LockSystem(LockSystem::LOCK_TYPE_MEMCACHE); //获取锁 $lockKey = 'pay'.$userId; $lockSystem--->getLock($lockKey,8); //取出总额 $total = getUserLeftMoney($userId); //花费大于剩余 if($money > $total) { $ret = false; } else { //余额 $left = $total - $money; //更新余额 $ret = setUserLeftMoney($userId,$left); } //释放锁 $lockSystem->releaseLock($lockKey); } catch (Exception $e) { //释放锁 $lockSystem->releaseLock($lockKey); } } //取出用户的余额 function getUserLeftMoney($userId) { if(false == is_int($userId)) { return 0; } $sql = "select account form user_account where userid = ${userId}"; //$mysql = new mysql();//mysql数据库 return $mysql->query($sql); } //更新用户余额 function setUserLeftMoney($userId,$money) { if(false == is_int($userId) || false == is_int($money)) { return false; } $sql = "update user_account set account = ${money} where userid = ${userId}"; //$mysql = new mysql();//mysql数据库 return $mysql->execute($sql); } ?>
3.2 Analyse du verrouillage
p opération Personne :
Obtenir le verrou : payer 100
Retirer le solde de 1000 de l'utilisateur.
800 restants = 1000 - 200 après paiement.
Le solde du compte mis à jour est de 800.
Libérer le verrou : payer 100
m Opérateur :
En attente du verrou : payer100
Obtenir le verrouillage : payer100
Obtenir le solde : 800
500 restant après le paiement = 800-300.
Le solde du compte après paiement est de 500.
Lâchez le verrou : payez 100
Après deux paiements, le solde est de 500. Il résout parfaitement le problème d’accès aux ressources des sections critiques causé par la concurrence.
Résumé : Ce qui précède représente l'intégralité du contenu de cet article, j'espère qu'il sera utile à l'étude de chacun.
Recommandations associées :
PHP utilise file_get_contents pour envoyer des requêtes http avec une fonction simple
Verrouillage à haute concurrence PHP+MySQL traitement des transactions Problème Solution
Algorithme Monkey King (Monkey Chooses the King) implémenté 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!