這里我們主要利用Redis的setnx的命令來(lái)處理高并發(fā)。
setnx 有兩個(gè)參數(shù)。第一個(gè)參數(shù)表示鍵。第二個(gè)參數(shù)表示值。如果當(dāng)前鍵不存在,那么會(huì)插入當(dāng)前鍵,將第二個(gè)參數(shù)做為值。返回 1。如果當(dāng)前鍵存在,那么會(huì)返回0。
創(chuàng)建庫(kù)存表
CREATE TABLE `storage` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `number` int(11) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1
設(shè)置初始庫(kù)存為10
創(chuàng)建訂單表
CREATE TABLE `order` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `number` int(11) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1
測(cè)試不用鎖的時(shí)候
$pdo = new PDO( mysql:host=127.0.0.1;dbname=test , root , root $sql= select `number` from storage where id=1 limit 1 $res = $pdo- query($sql)- fetch();$number = $res[ number if($number 0) $sql = insert into `order` VALUES (null,$number) $order_id = $pdo- query($sql); if($order_id) $sql= update storage set `number`=`number`-1 WHERE id=1 $pdo- query($sql);}
ab測(cè)試模擬并發(fā),發(fā)現(xiàn)庫(kù)存是正確的。
mysql select * from storage;+----+--------+| id | number |+----+--------+| 1 | 0 |+----+--------+1 row in set (0.00 sec)
在來(lái)看訂單表
mysql select * from `order`;+----+--------+| id | number |+----+--------+| 1 | 10 || 2 | 10 || 3 | 9 || 4 | 7 || 5 | 6 || 6 | 5 || 7 | 5 || 8 | 5 || 9 | 4 || 10 | 1 |+----+--------+10 rows in set (0.00 sec)
發(fā)現(xiàn)存在幾個(gè)訂單都是操作的同一個(gè)庫(kù)存數(shù)據(jù),這樣就可能引起超賣(mài)的情況。
修改代碼加入redis鎖進(jìn)行數(shù)據(jù)控制
?php * Created by PhpStorm. * User: daisc * Date: 2018/7/23 * Time: 14:45html' target='_blank'>class Lock private static $_instance ; private $_redis; private function __construct() $this- _redis = new Redis(); $this- _redis - connect( 127.0.0.1 public static function getInstance() if(self::$_instance instanceof self) return self::$_instance; return self::$_instance = new self(); * @function 加鎖 * @param $key 鎖名稱(chēng) * @param $expTime 過(guò)期時(shí)間 public function set($key,$expTime) //初步加鎖 $isLock = $this- _redis- setnx($key,time()+$expTime); if($isLock) return true; else //加鎖失敗的情況下。判斷鎖是否已經(jīng)存在,如果鎖存在切已經(jīng)過(guò)期,那么刪除鎖。進(jìn)行重新加鎖 $val = $this- _redis- get($key); if($val $val time()) $this- del($key); return $this- _redis- setnx($key,time()+$expTime);
$pdo = new PDO( mysql:host=127.0.0.1;dbname=test , root , root $lockObj = Lock::getInstance();//判斷是能加鎖成功if($lock = $lockObj- set( storage ,10)) $sql= select `number` from storage where id=1 limit 1 $res = $pdo- query($sql)- fetch(); $number = $res[ number if($number 0) $sql = insert into `order` VALUES (null,$number) $order_id = $pdo- query($sql); if($order_id) $sql= update storage set `number`=`number`-1 WHERE id=1 $pdo- query($sql); //解鎖 $lockObj- del( storage //加鎖不成功執(zhí)行其他操作。}
再次進(jìn)行ab測(cè)試,查看測(cè)試結(jié)果
mysql select * from `order`;+----+--------+| id | number |+----+--------+| 1 | 10 || 2 | 9 || 3 | 8 || 4 | 7 || 5 | 6 || 6 | 5 || 7 | 4 || 8 | 3 || 9 | 2 || 10 | 1 |+----+--------+10 rows in set (0.00 sec)
發(fā)現(xiàn)訂單表沒(méi)有操作同一個(gè)庫(kù)存數(shù)據(jù)的情況。所以利用redis鎖是可以有效的處理高并發(fā)的。
這里在加鎖的時(shí)候其實(shí)是可以不需要判斷過(guò)期時(shí)間的,這里我們?yōu)榱吮苊庠斐伤梨i,所以加一個(gè)過(guò)期時(shí)間的判斷。當(dāng)過(guò)期的時(shí)候主動(dòng)刪除該鎖。
相關(guān)推薦:
php利用文件鎖來(lái)解決高并發(fā)問(wèn)題
使用Redis來(lái)存儲(chǔ)PHP的session如何解決并發(fā)一致性的問(wèn)題
以上就是如何利用Redis鎖解決高并發(fā)問(wèn)題的詳細(xì)內(nèi)容,PHP教程
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注