PHP Concurrent Lock Example _php instance

Source: Internet
Author: User
Tags flock

In the work item, will encounter some PHP concurrent access to modify a data problem, if this data does not lock, can cause the data error. Below I will analyze a financial payment lock problem. Hope to help you.

1 NO lock mechanism applied

1.1 Financial Payment Simplified version code

<!--? php/** * pay.php * * * Payment does not apply lock * * Copy Right (c) 2016 * * Modification history: *---------------  -----* 2016/9/10, by Clevercode, Create * *///user pays function Pay ($userId, $money) {if (false = = Is_int ($userId) ||
 false = = Is_int ($money)) {return false;
  
 Total $total = Getuserleftmoney ($userId); 
 The cost is greater than the remaining if ($money--> $total) {return false;
  
 }//Balance $left = $total-$money;
 
Update balance return Setuserleftmoney ($userId, $left);
 ///Remove the user's balance function Getuserleftmoney ($userId) {if (false = = Is_int ($userId)) {return 0;
  
 $sql = "Select Account form user_account where UserID = ${userid}";
$mysql = new MySQL ();//mysql database return $mysql->query ($sql); //Update user balance function Setuserleftmoney ($userId, $money) {if (false = Is_int ($userId) | | false = = Is_int ($money)) {RET
 Urn false;
  
 $sql = "Update user_account Set account = ${money} where UserID = ${userid}"; $mysql = new MySQL ();//mysql Database return $Mysql->execute ($sql); }?>

1.2 Problem Analysis

If there are two operators (P and M), with the user number 100 account, respectively, on the PC and mobile phone at the same time landing, 100 account total Balance has 1000,p operator flower 200,m operator Flowers 300. The concurrency process is as follows.

P Operator:

    1. Remove the user's balance of 1000.
    2. The remaining 800 = 1000-200 after payment.
    3. Updated account balance 800.

M operator:

    1. Remove the user balance 1000.
    2. The remaining 700 = 1000-300 after payment.
    3. Account balance 700 after payment.

Two times after payment, the balance of the account is still 700, should be the case is spent 500, account balance 500 is right. The root cause of this phenomenon is concurrency, when both p and M operate at the same time, the balance data is 1000.

2 Lock Design

The operation of the lock is generally only two steps, one acquires the lock (Getlock) and the other is releasing the lock (ReleaseLock). However, there are many ways of the reality lock, which can be realized by the file way, SQL implementation, memcache implementation, and according to this scenario, we consider using the policy pattern.

2.1 Class diagram design is as follows

2.2 PHP Source design is as follows

locksystem.php

<!--? php/** * locksystem.php * * PHP lock mechanism * * 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 '); The Public function __construct ($type, $options = Array ()) {if (false = = Empty ($type)) {$this--->createlock ($type
  , $options);
   The 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) {T   
  Hrow new Exception (' false = = $this->_lock instanceof ilock '); } $this->_lock->geTlock ($key, $timeout); The Public Function ReleaseLock ($key) {if (false = = $this->_lock instanceof ilock) {throw new Exception (' F   
  Alse = = $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-& Gt;_lockpath = $options [' Path '].
  /';
  else {$this->_lockpath = '/tmp/';
 $this->_single = isset ($options [' single '])? $options [' Single ']:false;
 
  The Public Function Getlock ($key, $timeout =self::expire) {$startTime = Timer::gettimestamp ();
  $file = MD5 (__file__. $key); $this-&GT;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;
  The Public Function ReleaseLock ($key) {flock ($this->fp, Lock_un);
 Fclose ($this-&GT;FP); 
 } class Sqllock implements Ilock {public function __construct ($options) {$this->_db = new MySQL ();
  The Public Function Getlock ($key, $timeout =self::expire) {$sql = ' select Get_lock ('. $key. ', '. $timeout. ') ';
  $res = $this->_db->query ($sql);
 return $res;
  The 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 MEMCA
 Che ();
  The 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;
 
 The IF ($totalWaitime >= $time) throw new Exception (' Can not get lock for waiting '. $timeout. ');
 The Public Function ReleaseLock ($key) {$this->memcache->delete ($key);
 }
}

3 Application lock mechanism

3.1 Payment System Application lock

<!--? php/** * pay.php * * * Payment Application Lock * * Copy Right (c) 2016 * * Modification history: *---------------- ----* 2016/9/10, by Clevercode, Create * *///user pays function Pay ($userId, $money) {if (false = = Is_int ($userId) ||
 false = = Is_int ($money)) {return false;    
   
  try {//Create lock (Recommended use memcachelock) $lockSystem = new Locksystem (Locksystem::lock_type_memcache);
  Get lock $lockKey = ' pay '. $userId;
   
  $lockSystem--->getlock ($lockKey, 8);
   
  Total $total = Getuserleftmoney ($userId); 
  The cost is greater than the remaining if ($money > $total) {$ret = false;
    
   else {//Balance $left = $total-$money;
  Update Balance $ret = Setuserleftmoney ($userId, $left); 
 }//Release lock $lockSystem->releaselock ($lockKey);  
 The catch (Exception $e) {//Release lock $lockSystem->releaselock ($lockKey);
 }///Remove the user's balance function Getuserleftmoney ($userId) {if (false = = Is_int ($userId)) {return 0; } $sql = "Select Account form user_account where UserID =${userid} ";
$mysql = new MySQL ();//mysql database return $mysql->query ($sql); //Update user balance function Setuserleftmoney ($userId, $money) {if (false = Is_int ($userId) | | false = = Is_int ($money)) {RET
 Urn false;
  
 $sql = "Update user_account Set account = ${money} where UserID = ${userid}";
$mysql = new MySQL ();//mysql database return $mysql->execute ($sql);
 }?>

3.2 Lock Analysis

P Operator:

    1. Get Lock: pay100
    2. Remove the user's balance of 1000.
    3. The remaining 800 = 1000-200 after payment.
    4. Updated account balance 800.
    5. Release Lock: pay100

M operator:

    1. Wait for Lock: pay100
    2. Get Lock: pay100
    3. Get Balance: 800
    4. The remaining 500 = 800-300 after payment.
    5. Account balance 500 after payment.
    6. Release Lock: pay100

Two times after payment, the balance is 500. It's perfect. It solves the problem of access to the critical area resources caused by concurrency.

Thank you for reading, I hope to help you, thank you for your support for this site!

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.