亚洲综合原千岁中文字幕_国产精品99久久久久久久vr_无码人妻aⅴ一区二区三区浪潮_成人h动漫精品一区二区三

主頁(yè) > 知識(shí)庫(kù) > PHP商品秒殺問(wèn)題解決方案實(shí)例詳解【mysql與redis】

PHP商品秒殺問(wèn)題解決方案實(shí)例詳解【mysql與redis】

熱門(mén)標(biāo)簽:廣東地市地圖標(biāo)注 外呼系統(tǒng)撥打暫時(shí)無(wú)法接通 長(zhǎng)春人工外呼系統(tǒng)服務(wù)商 江西手機(jī)自動(dòng)外呼防封系統(tǒng)是什么 廣州防封卡外呼系統(tǒng)多少錢(qián)一個(gè)月 仁和怎么申請(qǐng)400開(kāi)頭的電話 高德地圖標(biāo)注家 怎么向銷售公司推銷外呼系統(tǒng) 哪里辦理400電話

本文實(shí)例講述了PHP商品秒殺問(wèn)題解決方案。分享給大家供大家參考,具體如下:

引言

假設(shè)num是存儲(chǔ)在數(shù)據(jù)庫(kù)中的字段,保存了被秒殺產(chǎn)品的剩余數(shù)量。

if($num > 0){
  //用戶搶購(gòu)成功,記錄用戶信息
  $num--;
}

假設(shè)在一個(gè)并發(fā)量較高的場(chǎng)景,數(shù)據(jù)庫(kù)中num的值為1時(shí),可能同時(shí)會(huì)有多個(gè)進(jìn)程讀取到num為1,程序判斷符合條件,搶購(gòu)成功,num減一。這樣會(huì)導(dǎo)致商品超發(fā)的情況,本來(lái)只有10件可以搶購(gòu)的商品,可能會(huì)有超過(guò)10個(gè)人搶到,此時(shí)num在搶購(gòu)?fù)瓿芍鬄樨?fù)值。

解決該問(wèn)題的方案由很多,可以簡(jiǎn)單分為基于mysql和redis的解決方案,redis的性能要由于mysql,因此可以承載更高的并發(fā)量,不過(guò)下面介紹的方案都是基于單臺(tái)mysql和redis的,更高的并發(fā)量需要分布式的解決方案,本文沒(méi)有涉及。

基于mysql的解決方案

商品表 goods

CREATE TABLE `goods` (
 `id` int(11) NOT NULL,
 `num` int(11) DEFAULT NULL,
 `version` int(11) DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

搶購(gòu)結(jié)果表 log

CREATE TABLE `log` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `good_id` int(11) DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

悲觀鎖

悲觀鎖的方案采用的是排他讀,也就是同時(shí)只能有一個(gè)進(jìn)程讀取到num的值。事務(wù)在提交或回滾之后,鎖會(huì)釋放,其他的進(jìn)程才能讀取。該方案最簡(jiǎn)單易懂,在對(duì)性能要求不高時(shí),可以直接采用該方案。要注意的是,SELECT … FOR UPDATE要盡可能的使用索引,以便鎖定盡可能少的行數(shù);排他鎖是在事務(wù)執(zhí)行結(jié)束之后才釋放的,不是讀取完成之后就釋放,因此使用的事務(wù)應(yīng)該盡可能的早些提交或回滾,以便早些釋放排它鎖。

$this->mysqli->begin_transaction();
$result = $this->mysqli->query("SELECT num FROM goods WHERE id=1 LIMIT 1 FOR UPDATE");
$row = $result->fetch_assoc();
$num = intval($row['num']);
if($num > 0){
  usleep(100);
  $this->mysqli->query("UPDATE goods SET num=num-1");
  $affected_rows = $this->mysqli->affected_rows;
  if($affected_rows == 1){
    $this->mysqli->query("INSERT INTO log(good_id) VALUES({$num})");
    $affected_rows = $this->mysqli->affected_rows;
    if($affected_rows == 1){
      $this->mysqli->commit();
      echo "success:".$num;
    }else{
      $this->mysqli->rollback();
      echo "fail1:".$num;
    }
  }else{
    $this->mysqli->rollback();
    echo "fail2:".$num;
  }
}else{
  $this->mysqli->commit();
  echo "fail3:".$num;
}

樂(lè)觀鎖

樂(lè)觀鎖的方案在讀取數(shù)據(jù)是并沒(méi)有加排他鎖,而是通過(guò)一個(gè)每次更新都會(huì)自增的version字段來(lái)解決,多個(gè)進(jìn)程讀取到相同num,然后都能更新成功的問(wèn)題。在每個(gè)進(jìn)程讀取num的同時(shí),也讀取version的值,并且在更新num的同時(shí)也更新version,并在更新時(shí)加上對(duì)version的等值判斷。假設(shè)有10個(gè)進(jìn)程都讀取到了num的值為1,version值為9,則這10個(gè)進(jìn)程執(zhí)行的更新語(yǔ)句都是UPDATE goods SET num=num-1,version=version+1 WHERE version=9,然而當(dāng)其中一個(gè)進(jìn)程執(zhí)行成功之后,數(shù)據(jù)庫(kù)中version的值就會(huì)變?yōu)?0,剩余的9個(gè)進(jìn)程都不會(huì)執(zhí)行成功,這樣保證了商品不會(huì)超發(fā),num的值不會(huì)小于0,但這也導(dǎo)致了一個(gè)問(wèn)題,那就是發(fā)出搶購(gòu)請(qǐng)求較早的用戶可能搶不到,反而被后來(lái)的請(qǐng)求搶到了。

$result = $this->mysqli->query("SELECT num,version FROM goods WHERE id=1 LIMIT 1");
$row = $result->fetch_assoc();
$num = intval($row['num']);
$version = intval($row['version']);
if($num > 0){
  usleep(100);
  $this->mysqli->begin_transaction();
  $this->mysqli->query("UPDATE goods SET num=num-1,version=version+1 WHERE version={$version}");
  $affected_rows = $this->mysqli->affected_rows;
  if($affected_rows == 1){
    $this->mysqli->query("INSERT INTO log(good_id) VALUES({$num})");
    $affected_rows = $this->mysqli->affected_rows;
    if($affected_rows == 1){
      $this->mysqli->commit();
      echo "success:".$num;
    }else{
      $this->mysqli->rollback();
      echo "fail1:".$num;
    }
  }else{
    $this->mysqli->rollback();
    echo "fail2:".$num;
  }
}else{
  echo "fail3:".$num;
}

where條件(原子操作)

悲觀鎖的方案保證了數(shù)據(jù)庫(kù)中num的值在同一時(shí)間只能被一個(gè)進(jìn)程讀取并處理,也就是并發(fā)的讀取進(jìn)程到這里要排隊(duì)依次執(zhí)行。樂(lè)觀鎖的方案雖然num的值可以被多個(gè)進(jìn)程同時(shí)讀取到,但是更新操作中version的等值判斷可以保證并發(fā)的更新操作在同一時(shí)間只能有一個(gè)更新成功。

還有一種更簡(jiǎn)單的方案,只在更新操作時(shí)加上num>0的條件限制即可。通過(guò)where條件限制的方案雖然看似和樂(lè)觀鎖方案類似,都能夠防止超發(fā)問(wèn)題的出現(xiàn),但在num較大時(shí)的表現(xiàn)還是有很大區(qū)別的。假如此時(shí)num為10,同時(shí)有5個(gè)進(jìn)程讀取到了num=10,對(duì)于樂(lè)觀鎖的方案由于version字段的等值判斷,這5個(gè)進(jìn)程只會(huì)有一個(gè)更新成功,這5個(gè)進(jìn)程執(zhí)行完成之后num為9;對(duì)于where條件判斷的方案,只要num>0都能夠更新成功,這5個(gè)進(jìn)程執(zhí)行完成之后num為5。

$result = $this->mysqli->query("SELECT num FROM goods WHERE id=1 LIMIT 1");
$row = $result->fetch_assoc();
$num = intval($row['num']);
if($num > 0){
  usleep(100);
  $this->mysqli->begin_transaction();
  $this->mysqli->query("UPDATE goods SET num=num-1 WHERE num>0");
  $affected_rows = $this->mysqli->affected_rows;
  if($affected_rows == 1){
    $this->mysqli->query("INSERT INTO log(good_id) VALUES({$num})");
    $affected_rows = $this->mysqli->affected_rows;
    if($affected_rows == 1){
      $this->mysqli->commit();
      echo "success:".$num;
    }else{
      $this->mysqli->rollback();
      echo "fail1:".$num;
    }
  }else{
    $this->mysqli->rollback();
    echo "fail2:".$num;
  }
}else{
  echo "fail3:".$num;
}

基于redis的解決方案

基于watch的樂(lè)觀鎖方案

watch用于監(jiān)視一個(gè)(或多個(gè)) key ,如果在事務(wù)執(zhí)行之前這個(gè)(或這些) key 被其他命令所改動(dòng),那么事務(wù)將被打斷。這種方案跟mysql中的樂(lè)觀鎖方案類似,具體表現(xiàn)也是一樣的。

$num = $this->redis->get('num');
if($num > 0) {
  $this->redis->watch('num');
  usleep(100);
  $res = $this->redis->multi()->decr('num')->lPush('result',$num)->exec();
  if($res == false){
    echo "fail1";
  }else{
    echo "success:".$num;
  }
}else{
  echo "fail2";
}

基于list的隊(duì)列方案

基于隊(duì)列的方案利用了redis出隊(duì)操作的原子性,搶購(gòu)開(kāi)始之前首先將商品編號(hào)放入響應(yīng)的隊(duì)列中,在搶購(gòu)時(shí)依次從隊(duì)列中彈出操作,這樣可以保證每個(gè)商品只能被一個(gè)進(jìn)程獲取并操作,不存在超發(fā)的情況。該方案的優(yōu)點(diǎn)是理解和實(shí)現(xiàn)起來(lái)都比較簡(jiǎn)單,缺點(diǎn)是當(dāng)商品數(shù)量較多是,需要將大量的數(shù)據(jù)存入到隊(duì)列中,并且不同的商品需要存入到不同的消息隊(duì)列中。

public function init(){
  $this->redis->del('goods');
  for($i=1;$i=10;$i++){
    $this->redis->lPush('goods',$i);
  }
  $this->redis->del('result');
  echo 'init done';
}
public function run(){
  $goods_id = $this->redis->rPop('goods');
  usleep(100);
  if($goods_id == false) {
    echo "fail1";
  }else{
    $res = $this->redis->lPush('result',$goods_id);
    if($res == false){
      echo "writelog:".$goods_id;
    }else{
      echo "success".$goods_id;
    }
  }
}

基于decr返回值的方案

如果我們將剩余量num設(shè)置為一個(gè)鍵值類型,每次先get之后判斷,然后再decr是不能解決超發(fā)問(wèn)題的。但是redis中的decr操作會(huì)返回執(zhí)行后的結(jié)果,可以解決超發(fā)問(wèn)題。我們首先get到num的值進(jìn)行第一步判斷,避免每次都去更新num的值,然后再對(duì)num執(zhí)行decr操作,并判斷decr的返回值,如果返回值不小于0,這說(shuō)明decr之前是大于0的,用戶搶購(gòu)成功。

public function run(){
  $num = $this->redis->get('num');
  if($num > 0) {
    usleep(100);
    $retNum = $this->redis->decr('num');
    if($retNum >= 0){
      $res = $this->redis->lPush('result',$retNum);
      if($res == false){
        echo "writeLog:".$retNum;
      }else{
        echo "success:".$retNum;
      }
    }else{
      echo "fail1";
    }
  }else{
    echo "fail2";
  }
}

基于setnx的排它鎖方案

redis沒(méi)有像mysql中的排它鎖,但是可以通過(guò)一些方式實(shí)現(xiàn)排它鎖的功能,就類似php使用文件鎖實(shí)現(xiàn)排它鎖一樣。

setnx實(shí)現(xiàn)了exists和set兩個(gè)指令的功能,若給定的key已存在,則setnx不做任何動(dòng)作,返回0;若key不存在,則執(zhí)行類似set的操作,返回1。我們?cè)O(shè)置一個(gè)超時(shí)時(shí)間timeout,每隔一定時(shí)間嘗試setnx操作,如果設(shè)置成功就是獲得了相應(yīng)的鎖,執(zhí)行num的decr操作,操作完成刪除相應(yīng)的key,模擬釋放鎖的操作。

public function run(){
  do {
    $res = $this->redis->setnx("numKey",1);
    $this->timeout -= 100;
    usleep(100);
  }while($res == 0  $this->timeout>0);
  if($res == 0){
    echo 'fail1';
  }else{
    $num = $this->redis->get('num');
    if($num > 0) {
      $this->redis->decr('num');
      usleep(100);
      $res = $this->redis->lPush('result',$num);
      if($res == false){
        echo "fail2";
      }else{
        echo "success:".$num;
      }
    }else{
      echo "fail3";
    }
    $this->redis->del("numKey");
  }
}

上述代碼都在本地測(cè)試通過(guò),完整代碼地址:https://github.com/qianshou/SeckillSolution

更多關(guān)于PHP相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《php+mysql數(shù)據(jù)庫(kù)操作入門(mén)教程》、《php+redis數(shù)據(jù)庫(kù)程序設(shè)計(jì)技巧總結(jié)》、《php面向?qū)ο蟪绦蛟O(shè)計(jì)入門(mén)教程》、《PHP基本語(yǔ)法入門(mén)教程》、《PHP數(shù)組(Array)操作技巧大全》、《php字符串(string)用法總結(jié)》及《php常見(jiàn)數(shù)據(jù)庫(kù)操作技巧匯總》

希望本文所述對(duì)大家PHP程序設(shè)計(jì)有所幫助。

您可能感興趣的文章:
  • PHP Redis擴(kuò)展無(wú)法加載的問(wèn)題解決方法
  • thinkPHP框架通過(guò)Redis實(shí)現(xiàn)增刪改查操作的方法詳解
  • PHP+redis實(shí)現(xiàn)的限制搶購(gòu)防止商品超發(fā)功能詳解
  • PHP+Redis鏈表解決高并發(fā)下商品超賣問(wèn)題(實(shí)現(xiàn)原理及步驟)

標(biāo)簽:海北 黔東 廈門(mén) 梅河口 文山 濮陽(yáng) 惠州 湘西

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《PHP商品秒殺問(wèn)題解決方案實(shí)例詳解【mysql與redis】》,本文關(guān)鍵詞  PHP,商品,秒殺,問(wèn)題,解決方案,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。
  • 相關(guān)文章
  • 下面列出與本文章《PHP商品秒殺問(wèn)題解決方案實(shí)例詳解【mysql與redis】》相關(guān)的同類信息!
  • 本頁(yè)收集關(guān)于PHP商品秒殺問(wèn)題解決方案實(shí)例詳解【mysql與redis】的相關(guān)信息資訊供網(wǎng)民參考!
  • 推薦文章
    精品久久久久久中文字幕一区| 国产精品1024永久免费视频| 国产不卡在线看| 国产成人精品综合| 欧美夜夜骑 青草视频在线观看完整版 久久精品99无色码中文字幕 欧美日韩一区二区在线观看视频 欧美中文字幕在线视频 www.99精品 香蕉视频久久 | 台湾毛片| 国产91精品露脸国语对白| 国产麻豆精品免费密入口| 夜夜操天天爽| 国产精品1024在线永久免费| 国产成a人片在线观看视频| 麻豆网站在线免费观看| 美女被草网站| 国产亚洲免费观看| 四虎精品在线观看| 国产成人精品综合在线| 一本伊大人香蕉高清在线观看| 国产福利免费观看| 国产a视频精品免费观看| 99久久精品国产免费| 亚洲天堂在线播放| 国产一区国产二区国产三区| 欧美电影免费看大全| 午夜在线影院| 国产成人啪精品| 一级片片| 精品在线观看国产| 成人影院一区二区三区| 日韩专区亚洲综合久久| 日本在线不卡视频| 国产精品1024在线永久免费| 国产亚洲精品成人a在线| 亚洲女初尝黑人巨高清在线观看| 精品久久久久久中文| 香蕉视频久久| 久久久久久久久综合影视网| 九九热国产视频| 精品国产三级a| 沈樵在线观看福利| 国产一区二区精品久久| 99热精品一区| 国产国语对白一级毛片| 日本久久久久久久 97久久精品一区二区三区 狠狠色噜噜狠狠狠狠97 日日干综合 五月天婷婷在线观看高清 九色福利视频 | 日本在线www| 亚洲精品影院一区二区| 亚洲 激情| 亚洲精品中文字幕久久久久久| 欧美激情一区二区三区视频高清| 午夜欧美成人香蕉剧场| 久久国产一区二区| 日本久久久久久久 97久久精品一区二区三区 狠狠色噜噜狠狠狠狠97 日日干综合 五月天婷婷在线观看高清 九色福利视频 | 黄色福利片| 成人免费观看的视频黄页| 精品国产香蕉伊思人在线又爽又黄| 日韩中文字幕在线观看视频| 91麻豆国产福利精品| 精品在线免费播放| 毛片的网站| 免费国产在线视频| 沈樵在线观看福利| 久久国产精品永久免费网站| 国产视频一区在线| 欧美激情一区二区三区在线| 国产不卡高清| 国产视频在线免费观看| 日韩中文字幕一区| 国产一区免费观看| 成人免费福利片在线观看| 成人高清视频在线观看| 国产亚洲精品aaa大片| 91麻豆精品国产自产在线 | 韩国三级一区| 国产国产人免费视频成69堂| 精品国产一区二区三区久久久狼| 欧美激情影院| 精品视频在线看 | 99热视热频这里只有精品| 免费一级片在线| 你懂的日韩| 91麻豆国产| 午夜在线亚洲男人午在线| 九九九国产| 美女免费精品视频在线观看| 亚久久伊人精品青青草原2020| 夜夜操网| 欧美另类videosbestsex| 国产成a人片在线观看视频| 国产韩国精品一区二区三区| 国产成人欧美一区二区三区的| 一级毛片视频在线观看| 精品视频一区二区三区免费| 成人高清视频在线观看| 国产一区二区精品久| 色综合久久久久综合体桃花网| 国产极品精频在线观看| 国产视频网站在线观看| 欧美激情在线精品video| 精品国产一区二区三区国产馆| 精品国产亚洲人成在线| 99色视频在线观看| 青青青草视频在线观看| 国产美女在线一区二区三区| 韩国毛片免费| 国产一区二区精品久久91| 午夜在线亚洲男人午在线| 人人干人人插| 日韩一级黄色大片| 麻豆午夜视频| 日韩中文字幕在线亚洲一区| 精品国产亚一区二区三区| 亚欧成人毛片一区二区三区四区| 日韩专区一区| 国产成人欧美一区二区三区的| 国产伦理精品| 亚洲精品中文一区不卡| 青草国产在线观看| 日本在线不卡视频| 亚洲女人国产香蕉久久精品| 天天做日日爱| 精品视频在线观看视频免费视频 | 天堂网中文在线| 精品久久久久久免费影院| 国产伦理精品| 精品在线观看国产| 毛片高清| 二级特黄绝大片免费视频大片| 久久国产精品自由自在| 亚洲不卡一区二区三区在线| 青青青草影院| 精品国产三级a| 可以免费在线看黄的网站| 青青青草视频在线观看| 成人a级高清视频在线观看| 久久精品店| 欧美激情中文字幕一区二区| 久久久久久久免费视频| 久久99青青久久99久久| 国产一区二区精品久久91| 成人影院久久久久久影院| 欧美国产日韩在线| 久久久成人网| 精品毛片视频| 日韩字幕在线| 精品美女| 欧美激情中文字幕一区二区| 久久福利影视| 国产韩国精品一区二区三区| 国产国语对白一级毛片| 精品视频在线观看免费 | 国产伦久视频免费观看 视频| 可以免费看污视频的网站| 精品视频在线观看免费 | 国产a毛片| 国产一区二区精品久| 国产极品精频在线观看| 亚洲精品中文一区不卡| 亚洲精品中文字幕久久久久久| 国产不卡精品一区二区三区| 国产一区二区精品在线观看| 国产91丝袜高跟系列| 亚飞与亚基在线观看| 色综合久久天天综合绕观看| 999久久久免费精品国产牛牛| 成人影院久久久久久影院| 成人免费观看的视频黄页| 黄视频网站免费看| 欧美18性精品| 国产a毛片| 一级女性全黄久久生活片| 九九热国产视频| 九九久久99| 精品视频在线观看一区二区| 欧美另类videosbestsex视频 | 久久国产精品自由自在| 黄色短视屏| 国产成人女人在线视频观看| 欧美a级v片不卡在线观看| 精品视频在线观看视频免费视频 | 欧美激情在线精品video| 韩国三级一区| 精品在线观看一区| 二级片在线观看| 尤物视频网站在线观看| 久久99青青久久99久久| 国产亚洲精品成人a在线| 二级特黄绝大片免费视频大片| 精品国产一区二区三区久久久蜜臀| 一本高清在线| 亚洲www美色| 欧美国产日韩在线| a级黄色毛片免费播放视频| 你懂的日韩| 成人在免费观看视频国产| 毛片高清| 黄色免费三级| 久久精品成人一区二区三区| 香蕉视频久久| 亚欧成人乱码一区二区| 香蕉视频久久| 99久久精品国产国产毛片|