原文連接: Redis在Web項目中的應用與實踐
Redis做爲一個開源的(BSD)基於內存的高性能存儲系統,已經被各大互聯網公司普遍使用,而且有着諸多的應用場景。本篇文章將基於PHP來詳細講解Redis在Web項目中的主要應用與實踐。git
這裏所介紹的緩存是指能夠丟失或過時的數據。經常使用的命令有 set
, hset
, get
, hget
,使用redis做爲緩存時須要注意一下幾個問題:github
maxmemory
最大內存。在web項目中,redis可存儲讀寫很是頻繁的數據來緩解MySQL等數據庫的壓力。redis若是做爲存儲系統的話,爲了防止數據丟失,持久化必須開啓。web
典型場景redis
計數器的需求很是廣泛,例如微博點贊數、帖子收藏數、文章分享數、用戶關注數等。算法
好比使用Sets結構存儲關注列表、收藏列表、點贊列表等。數據庫
藉助redis高性能的key-value存儲,可將用戶登陸狀態保存到redis中。緩存
簡單隊列安全
通常使用redis的list結構做爲隊列,rpush
生產消息,lpop
消費消息,當 lpop
沒有消息的時候,要進行適當的sleep操做。服務器
$queueKey = "queue"; // 生產者 $redis->rpush($queueKey, $data) // 消費者 while (true) { $data = $redis->lpop($queueKey); if (null === $data) { usleep(100000); continue; } // 業務邏輯 ... }
因爲沒有消息時使用的sleep事件很差控制,生產環境儘可能不要使用sleep來休眠,可以使用 blpop
來消費消息,在沒有新消息的時候它會阻塞到消息到來。微信
延時隊列
延時隊列可以使用redis的 sorted set
數據結構,使用時間戳做爲 score
,消息內容做爲 member
,使用 zadd
命令來生產消息,消費者使用 zrangebyscore
命令獲取指定時間以前的消息數據輪詢進行處理。
$queueKey = "queue"; // 生產消息 // 消費時間, 這裏設置爲1小時候 $consumeTimestamp = time() + 3600; // $data須要添加隨機串前綴(or後綴),防止出現重複member被丟棄 $data = $data . md5(uniqid(rand(), true)); $redis->zadd($queueKey, $consumeTimestamp, $data); // 消費消息 while (tue) { $arrData = $redis->zrangebyscore($queueKey, 0, time()); if (!$arrData) { usleep(100000); continue; } // 業務邏輯 foreach ($arrData as $data) { $data = substr($data, 0, strlen($data) - 32); // 消費$data } }
多消費者
使用pub/sub主題訂閱者模式,能夠實現1:N的消息隊列。這種模式中在消費者下線的狀況下,生產的消息會丟失,在這裏不推薦使用。
須要強調的是不推薦使用redis做爲消息隊列服務,這不是redis的設計目標。若是必定要用可考慮 disque,是由redis的做者開發。
分佈式鎖主要解決的幾個問題:
方案1
咱們可能會考慮使用 setnx
和 expire
命令來實現加鎖,即當沒有key存在時纔會成功寫入value:
$lockStatus = $redis->setnx($lockKey, 1); if (1 === $lockStatus) { // 加鎖成功,爲鎖設置超時時間 $redis->expire($lockKey, 300); // 進行後續操做 } elseif (0 === $lockStatus) { // 加鎖失敗 } else { // 其餘異常 }
但這種操做不是原子性的,若是在進行setnx時服務崩潰,沒有來得及對Key進行超時設置,該鎖將一直沒法釋放。
方案2
咱們推薦 set key value [EX seconds] [PX milliseconds] [NX|XX]
命令來進行加鎖
$lockStatus = $this->redis->set($lockKey, 1, "EX", 30, "NX"); if ("OK" === $lockStatus) { // 加鎖成功,可進行後續操做 //業務邏輯執行完畢,釋放鎖 $this->redis->del($lockKey); } elseif (null === $lockStatus) { // 加鎖失敗 }
如上代碼所示,若是 set
命令返回OK,那麼客戶端就能夠得到鎖(若是返回null,那麼應用服務能夠在一段時間以後從新嘗試獲取鎖),而且能夠經過 del
命令來釋放鎖。
此方法須要注意的問題:
del
就會釋放了b服務加好的鎖。能夠經過以下優化使得上面的鎖系統變得更加健壯:
del
命令。優化後的代碼可參考以下:
$lockToken = md5(uniqid(rand(), true)); // 此處超時時間根據具體業務邏輯配置 $expire = rand(280, 320); $lockStatus = $this->redis->set($lockKey, $lockToken, "EX", $expire, "NX"); if ("OK" === $lockStatus) { // 加鎖成功,可進行後續操做 // 業務邏輯執行完畢,釋放鎖 // 刪除鎖以前須要判斷是不是本身上的鎖 $currentToken = $this->redis->get($lockKey); if ($currentToken === $lockToken) { $this->redis->del($lockKey); } } elseif (null === $lockStatus) { // 加鎖失敗 }
redis提供的原子自增減方法以及有序集合結構等能夠承擔一些計算任務,例如瀏覽量統計等。
瀏覽計數
文章瀏覽量+1
$redis->incr($postsKey);
批量獲取文章瀏覽量
$arrPostsKey = [ //... ]; $arrPostsViewNum = $redis->mget($arrPostsKey);
排行榜
可使用redis的有序集合來實現排行榜的功能,score做爲權重排序並取前n條記錄。
// 存儲數據 $sortKey = "sort_key"; $redis->zadd($sortKey, 100, "tom"); $redis->zadd($sortKey, 80, "Jon"); $redis->zadd($sortKey, 59, "Lilei"); $redis->zadd($sortKey, 87, "Hanmeimei"); // 獲取排行 // 由大到小排序 $arrRet = $redis->zrevrange($sortKey, 0, -1, true); // 由小到大排序 $arrRet = $redis->zrange($sortKey, 0, -1, true);
redis涉及的應用實踐很是繁多的,因爲篇幅所限沒法所有顧及,本文只針對web應用中最經常使用的幾個場景進行了展開介紹,渴望進一步拓展redis知識的同窗可參考如下連接進一步學習。
原文連接: Redis在Web項目中的應用與實踐
掃碼關注微信公衆號: Learn2Code