Php concurrent lock examples and php concurrent examples
In the work project, you may encounter some php concurrent access to modify a data problem. If the data is not locked, it will cause a data error. Next I will analyze a financial payment lock problem. Hope to help you.
1. No application lock mechanism
1.1 simplified financial payment version code
<! --? Php/*** pay. php ** payment does not apply the lock ** Copy right (c) 2016 ** modification history: * ------------------ * 2016/9/10, by CleverCode, create ** // user payment function pay ($ userId, $ money) {if (false = is_int ($ userId) | false = is_int ($ money )) {return false;} // total amount retrieved $ total = getUserLeftMoney ($ userId); // if ($ money --> $ total) {return false ;} // balance $ left = $ total-$ money; // update balance return setUserLeftM Oney ($ userId, $ left) ;}// retrieve 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 the user balance function setUserLeftMoney ($ userId, $ money) {if (false = is_int ($ userId) | false = is_int ($ money) {return false;} $ SQL = "update user_account set Ccount =$ {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), log on to the pc and mobile phone respectively with the user ID 100. The total account balance is 100, and the p operator spends 1000, m operator costs 300. The concurrency process is as follows.
P OPERATOR:
- Obtain the user's balance of 1000.
- Remaining 800 after payment = 1000-200.
- The updated account balance is 800.
M OPERATOR:
- Take out the user balance of 1000.
- Remaining 700 after payment = 1000-300.
- After payment, the account balance is 700.
After the two payments, the account balance is still 700. It should be because 500 is spent and the account balance is 500. The root cause of this phenomenon is that when concurrency occurs, the balance data obtained by both p and m operations is 1000.
2 Lock Design
The lock operation generally takes only two steps: getLock and releaseLock ). There are many methods to implement real locks, such as file implementation, SQL implementation, and Memcache implementation. In this scenario, we consider using the Policy mode.
2.1 class diagram design:
2.2 php source code design:
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'); public function _ const Ruct ($ 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 I Lock) {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 this-> _ lock instanceof ilock');} $ this-> _ lock-> releaseLock ($ key );}} interface ILock {const EXPIRE = 5; public function getLock ($ key, $ timeout = self: EXPIRE); public functio N 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 apply the 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 payment function pay ($ userId, $ money) {if (false = is_int ($ userId) | false = is_int ($ money )) {return false;} try {// create a lock (MemcacheLock is recommended) $ lockSystem = new LockSystem (LockSystem: LOCK_TYPE_MEMCACHE); // obtain the lock $ lockKey = 'pay '. $ userId; $ lockSystem ---> getLock ($ lo CkKey, 8); // get the total $ total = getUserLeftMoney ($ userId); // if ($ money >$ total) {$ ret = false ;} else {// balance $ left = $ total-$ money; // update balance $ ret = setUserLeftMoney ($ userId, $ left );} // release the lock $ lockSystem-> releaseLock ($ lockKey);} catch (Exception $ e) {// release the lock $ lockSystem-> releaseLock ($ lockKey );}} // retrieve 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 the user balance 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 database return $ mysql-> execute ($ SQL) ;}?>
3.2 lock analysis
P OPERATOR:
- Get lock: pay100
- Obtain the user's balance of 1000.
- Remaining 800 after payment = 1000-200.
- The updated account balance is 800.
- Release lock: pay100
M OPERATOR:
- Lock waiting: pay100
- Get lock: pay100
- Obtain balance: 800
- Remaining 500 after payment = 800-300.
- After payment, the account balance is 500.
- Release lock: pay100
After two payments, the balance is 500. It perfectly solves the problem of resource access in the critical section caused by concurrency.
Thank you for reading this article. I hope it will help you. Thank you for your support for this site!