最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

php并發(fā)加鎖問題分析與設(shè)計代碼實例講解

 更新時間:2021年02月26日 15:38:02   作者:PHP開發(fā)社區(qū)  
這篇文章主要介紹了php并發(fā)加鎖問題分析與設(shè)計代碼實例講解,有感興趣的同學(xué)可以跟著研究學(xué)習(xí)下

在工作項目中,會遇到一些php并發(fā)訪問去修改一個數(shù)據(jù)問題,如果這個數(shù)據(jù)不加鎖,就會造成數(shù)據(jù)的錯誤。下面我將分析一個財務(wù)支付鎖的問題。希望對大家有所幫助。

1 沒有應(yīng)用鎖機(jī)制

1.1 財務(wù)支付簡化版本代碼

<!--?php 
/** 
 * pay.php 
 * 
 * 支付沒有應(yīng)用鎖
 * 
 * Copy right (c) 2016 
 * 
 * modification history: 
 * -------------------- 
 * 2018/9/10, by CleverCode, Create 
 * 
 */
//用戶支付
function pay($userId,$money)
{
 if(false == is_int($userId) || false == is_int($money))
 {
 return false;
 } 
 //取出總額
 $total = getUserLeftMoney($userId);
 //花費(fèi)大于剩余
 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數(shù)據(jù)庫
 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數(shù)據(jù)庫
 return $mysql->execute($sql);
}
?>

1.2 問題分析

如果有兩個操作人(p和m),都用用戶編號100賬戶,分別在pc和手機(jī)端同時登陸,100賬戶總余額有1000,p操作人花200,m操作人花300。并發(fā)過程如下。

p操作人:

取出用戶的余額1000。
支付后剩余 800 = 1000 - 200。
更新后賬戶余額800。

m操作人:

取出用戶余額1000。
支付后剩余700 = 1000 - 300。
支付后賬戶余額700。
兩次支付后,賬戶的余額居然還有700,應(yīng)該的情況是花費(fèi)了500,賬戶余額500才對。造成這個現(xiàn)象的根本原因,是并發(fā)的時候,p和m同時操作取到的余額數(shù)據(jù)都是1000。

2 加鎖設(shè)計

鎖的操作一般只有兩步,一 獲取鎖(getLock);二是釋放鎖(releaseLock)。但現(xiàn)實鎖的方式有很多種,可以是文件方式實現(xiàn);sql實現(xiàn);Memcache實現(xiàn);根據(jù)這種場景我們考慮使用策略模式。

2.1 類圖設(shè)計如下


在這里插入圖片描述

2.2 php源碼設(shè)計如下

LockSystem.php

<!--?php 
/** 
 * LockSystem.php 
 * 
 * php鎖機(jī)制
 * 
 * Copy right (c) 2018
 * 
 * modification history: 
 * -------------------- 
 * 2018/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 應(yīng)用鎖機(jī)制

3.1 支付系統(tǒng)應(yīng)用鎖

<!--?php
/** 
 * pay.php 
 * 
 * 支付應(yīng)用鎖
 * 
 * Copy right (c) 2018 
 * 
 * modification history: 
 * -------------------- 
 * 2018/9/10, by CleverCode, Create 
 * 
 */
//用戶支付
function pay($userId,$money)
{
 if(false == is_int($userId) || false == is_int($money))
 {
 return false;
 } 
 try
 {
 //創(chuàng)建鎖(推薦使用MemcacheLock)
 $lockSystem = new LockSystem(LockSystem::LOCK_TYPE_MEMCACHE); 
 //獲取鎖
 $lockKey = 'pay'.$userId;
 $lockSystem--->getLock($lockKey,8);
 //取出總額
 $total = getUserLeftMoney($userId);
 //花費(fèi)大于剩余
 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數(shù)據(jù)庫
 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數(shù)據(jù)庫
 return $mysql->execute($sql);
}
?>

3.2 鎖分析

p操作人:

獲取鎖:pay100
取出用戶的余額1000。
支付后剩余 800 = 1000 - 200。
更新后賬戶余額800。
釋放鎖:pay100

m操作人:

1、等待鎖:pay100
2、獲取鎖:pay100
3、獲取余額:800
3、支付后剩余500 = 800 - 300。
5、支付后賬戶余額500。
6、釋放鎖:pay100

兩次支付后,余額500。非常完美了解決了并發(fā)造成的臨界區(qū)資源的訪問問題。

到此這篇關(guān)于php并發(fā)加鎖問題分析與設(shè)計代碼實例講解的文章就介紹到這了,更多相關(guān)php并發(fā)加鎖問題分析與設(shè)計內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 基于PHP中的常用函數(shù)回顧

    基于PHP中的常用函數(shù)回顧

    以下是對PHP中的常用函數(shù)進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以參考下
    2013-07-07
  • Laravel Reponse響應(yīng)客戶端示例詳解

    Laravel Reponse響應(yīng)客戶端示例詳解

    這篇文章主要給大家介紹了關(guān)于Laravel Reponse響應(yīng)客戶端的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • ThinkPHP框架獲取最后一次執(zhí)行SQL語句及變量調(diào)試簡單操作示例

    ThinkPHP框架獲取最后一次執(zhí)行SQL語句及變量調(diào)試簡單操作示例

    這篇文章主要介紹了ThinkPHP框架獲取最后一次執(zhí)行SQL語句及變量調(diào)試簡單操作,結(jié)合實例形式分析了thinkPHP獲取最后一次執(zhí)行SQL語句的getLastSql()及fetchSql()函數(shù)以及使用dump方法變量調(diào)試、E方法輸出錯誤信息等操作技巧,需要的朋友可以參考下
    2018-06-06
  • PHP生成唯一訂單號的方法匯總

    PHP生成唯一訂單號的方法匯總

    前陣子有個電子商務(wù)項目,需要生成訂單號。當(dāng)時的考慮很簡單,取系統(tǒng)時間加上隨機(jī)數(shù),或者使用 uniqid() 方法。仔細(xì)考慮下上述方法,在顧客購買量少的情況下,訂單重復(fù)的可能性為零,但是在購買高蜂期生成的訂單號重復(fù)是很有可能發(fā)生的。
    2015-04-04
  • 一組PHP加密解密函數(shù)分享

    一組PHP加密解密函數(shù)分享

    這篇文章主要介紹了一組PHP加密解密函數(shù)分享,需要的朋友可以參考下
    2014-06-06
  • 基于PHP 面向?qū)ο笾蓡T方法詳解

    基于PHP 面向?qū)ο笾蓡T方法詳解

    本篇文章是基于PHP 面向?qū)ο笾蓡T方法詳解。需要的朋友參考下
    2013-05-05
  • php求正負(fù)數(shù)數(shù)組中連續(xù)元素最大值示例

    php求正負(fù)數(shù)數(shù)組中連續(xù)元素最大值示例

    問題是給出數(shù)組,該數(shù)組由正負(fù)數(shù)字組成,找出該數(shù)組中連續(xù)元素組成的子數(shù)組的最大值。下面是PHP實現(xiàn)的示例,需要的朋友可以參考下
    2014-04-04
  • 簡單的PHP留言本實例代碼

    簡單的PHP留言本實例代碼

    對于學(xué)習(xí)php的朋友,開始做個留言板對于php+mysql的操作有個簡單的過程。學(xué)會了這個基本上php開始入門了。
    2010-05-05
  • PHP如何通過傳引用的思想實現(xiàn)無限分類(代碼簡單)

    PHP如何通過傳引用的思想實現(xiàn)無限分類(代碼簡單)

    本文給大家介紹php引用傳遞,如何通過引用出的思想實現(xiàn)無限分類,代碼超簡單,需要的朋友可以借鑒下
    2015-10-10
  • PHP無限循環(huán)獲取MySQL中的數(shù)據(jù)實例代碼

    PHP無限循環(huán)獲取MySQL中的數(shù)據(jù)實例代碼

    最近公司有個需求需要從MySQL獲取數(shù)據(jù),然后在頁面上無線循環(huán)的翻頁展示.其實這個功能可以通過jq實現(xiàn),也可以通過php+mysql實現(xiàn),下面小編給大家分享基于PHP無限循環(huán)獲取MySQL中的數(shù)據(jù)實現(xiàn)方法,感興趣的朋友一起看看吧
    2017-08-08

最新評論

神农架林区| 石阡县| 称多县| 邵阳市| 高碑店市| 金湖县| 鹤山市| 陆丰市| 永年县| 沛县| 武平县| 来宾市| 阳信县| 尖扎县| 宜兰市| 龙胜| 平凉市| 攀枝花市| 剑阁县| 宾阳县| 定陶县| 秦安县| 衡山县| 华亭县| 普兰店市| 峨眉山市| 图们市| 阿瓦提县| 桑植县| 乐都县| 龙江县| 清远市| 兴文县| 辽宁省| 德钦县| 甘南县| 郑州市| 重庆市| 门头沟区| 法库县| 土默特左旗|