1. 程式人生 > 其它 >redis簡單實現高併發秒殺功能

redis簡單實現高併發秒殺功能

技術標籤:其他redis秒殺併發樂觀鎖分散式鎖

前言:

秒殺功能不外乎就是解決下面兩個問題,

  1. 第一個是高併發對資料庫產生的壓力,
  2. 第二個是競爭狀態下如何解決庫存的正確減少,則超賣問題。

使用redis是最優方式,檔案鎖和資料庫鎖都不太好,因為redis可以方便實現分散式鎖,而且redis支援的併發量遠遠大於檔案鎖和資料庫鎖。redis使用樂觀鎖(共享鎖),悲觀鎖(排它鎖)都可以,不過悲觀鎖有個問題就是鎖等待的時間會佔用大量記憶體,秒殺一般是少量的資料,所以是讀多寫少場景,使用樂觀鎖更加合適。另外redis實現悲觀鎖不太友好,會產生一些問題,這些問題需要結合lua指令碼才能解決。使用佇列也可以,但是併發量會使佇列的記憶體瞬間佔慢。

redis使用樂觀鎖非常簡單,就是事務結合watch()方法實現監控,因為悲觀鎖是程序阻塞的,為了使用者體驗更好,可以手動模擬把阻塞改成非阻塞。以下是實現的程式碼:


 	 //初始化redis
        $redis = Cache::factory('Redis');

        $stock = 3; //搶購貨存數量
        $i = 0;
        $rebeat = 5; //重複執行次數(如果鎖衝突了,允許重複執行,直到沒有出現鎖衝突為止,把阻塞模擬成非阻塞)
        while($i < $rebeat) {
            $watchkey =
$redis->handle()->get("watchkey"); if(!$watchkey) $watchkey = 0; if ($watchkey < $stock) { //1.監控watchlist,因為雖然redis有原子性,但是事務沒有原子性,所以watch這裡起到一個事務鎖(樂觀鎖)的功能. //2.舉個例子,假如有兩個併發程序去競爭購買權,redis在監控一個key,第一個先到,第二個後到,先到的搶先一步更新了key,後到的雖然也進來的,但是得知key已被更新,不得已只能中斷自己當前的執行事務。
//3.這裡監控watchlist和watchkey都可以。 $redis->handle()->watch("watchlist"); //$redis->watch("watchkey"); //開啟事務 $redis->handle()->multi(); //插入搶購資料(先放入redis,等搶購完成之後再非同步入庫) $redis->handle()->hSet("watchlist", "user_id_" . mt_rand(1, 9999), time()); $redis->handle()->set("watchkey", $watchkey + 1); //執行事務 $result = $redis->handle()->exec(); if ($result) { $watchlist = $redis->handle()->hGetAll("watchlist"); return array( 'status'=> 1, 'msg'=>'搶購成功,剩餘數量:'.($stock - $watchkey - 1), 'data'=>$watchlist ); break; } else { //設定一個迴圈時間防止效能損耗過大 usleep(5000); $i++; } } else { return array( 'status'=> 0, 'msg'=>'搶購失敗,商品已經搶購完畢!', 'data'=>[] ); break; } } return array( 'status'=> 0, 'msg'=>'搶購失敗,鎖衝突了!', 'data'=>[] );

結果分析:

執行結果如下,使用ab工具模擬多個程序執行也沒有出現超賣情況

在這裡插入圖片描述