redis實戰-高併發搶購案例

先普及一下,什麼叫超賣,訂單商品數據量大於商品庫存數量,就叫做超賣;

那麼問題來了,爲什麼會超賣呢?

在商城搶購中,假如庫存爲100個,這時有100 000個併發請求過來了,最後庫存只剩1個時,假如還有1000個併發請求,如果這1000個請求都成功了,那最後庫存是不是變成了-999,這就是超賣。(以上僅爲理論上理解超賣)

先介紹一個很有用的工具,併發測試工具ab.exe,在Apache的bin目錄下打開cmd窗口,輸入命令ab -n10000 -c1000 http://localhost/code/mysql.php,表示測試10000個請求,併發量爲1000,測試訪問地址爲http://localhost/code/mysql.php

 

先建一個商品表,store爲庫存,以下爲一些用到SQL語句

// 建表
create table product(
	id int(8) not null auto_increment comment '產品id',
	name varchar(25) not null default '' comment '產品名',
	price decimal(10,3) not null default 0 comment '價格',
	store int(8) not null default 0 comment '庫存',
	primary key(id)
)engine=innodb default charset=utf8;

// 添加數據
insert into product value(1, '小米手環', 198, 10);

// 查看錶數據
select * from product;

// 設置庫存
update product set store=10 where id=1;

 

接下來,用代碼解釋下高併發搶購的超賣,直接在MySQL上測試,以下就是mysql.php文件

<?php
    header("content-type:text/html; charset=utf-8");
    
    // 連接數據庫
    $pdo = new PDO('mysql:host=localhost;dbname=test', 'root', 'root') or die("數據庫連接失敗");
    $pdo->exec("set names utf8");

    $sql = "select * from product where id=1";
    $res = $pdo->query($sql);
    $data = $res->fetch(PDO::FETCH_ASSOC);
    // print_r($data);

    if($data['store']>0){

        // 開啓事務
        $pdo->beginTransaction();
        $sql = "update product set store=store-1 where id=1";
        $res = $pdo->query($sql);
        print_r($res);
        if($res){
            echo "搶到啦!";
        }else{
            echo "搶購失敗...";
            $pdo->rollBack();
        }
        $pdo->commit();

    }else{
        echo '商品已被搶購一空,感謝參與!';
    }
?>

給它10000個請求,併發爲1000

來看下商品表中的庫存store

 

再來看下用redis實現的,用到了redis的事務、樂觀鎖,以下爲redis.php

<?PHP
    header("content-type:text/html; charset=utf-8");
    
    // 連接數據庫
    $pdo = new PDO('mysql:host=localhost;dbname=test', 'root', 'root') or die("數據庫連接失敗");
    $pdo->exec("set names utf8");

    $redis = new Redis();
    $redis->connect("localhost");

    $redis->watch('salec'); // 虛擬銷量
    $salec = $redis->get('salec');

    $store = 10; // 設置虛擬庫存與數據庫的一致

    if($salec >= $store){
        exit("商品已被搶購一空,感謝參與!");
    }

    // 開啓事務
    $redis->multi();
    $redis->incr('salec'); // 增加銷量 
    $res = $redis->exec(); // 執行事務 如果發現自己拿到的key被修改了,事務則被打斷

    if($res){
        // 以下代碼只是爲了測試,實際中是交給 MQ 去處理

        // 連接數據庫
        $pdo = new PDO('mysql:host=localhost;dbname=test', 'root', 'root') or die("數據庫連接失敗");
        $pdo->exec("set names utf8");
        $sql = "update product set store=store-1 where id=1";
        if($pdo->query($sql)){
            echo "搶到啦!";
        }

    }else{
        echo "搶購失敗...";
    }
?>

也給它10000個請求,併發爲1000

看下商品表中的庫存store

redis完美解決了高併發搶購的超賣現象。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章