Redis應(yīng)用場景

本文主要介紹Redis的常用場景,這都是由Redis的特性決定的。并盡量從源碼角度說明Redis為什么適合于這種場景。

I、積分排行榜

1、積分排行榜是Redis的經(jīng)典應(yīng)用,這是得益于Redis提供了zset的有序集合數(shù)據(jù)結(jié)構(gòu)。

2、針對zset,Redis提供了一系列的命令:

ZADD: 添加新元素;
ZRANGE: 按分數(shù)從低到高返回給定排名區(qū)間的元素;
ZREVRANGE: 按分數(shù)從高到底返回給定區(qū)間的元素;
ZRANK: 返回某個元素的排名。

3、實現(xiàn):下面的例子為用python完成的一個leaderboard

# -*- coding: utf-8 -*-
#!/usr/bin/python
import redis
class Leaderboard:
    def __init__(self,host,port,key,db):
        self.host = host
        self.port = port
        self.key = key
        self.db = db
        self.r = redis.StrictRedis(host=self.host,port=self.port,db=self.db)
    def isRedisValid(self):
        return self.r is None
    def addMember(self,member,score):
        if self.isRedisValid():
            return None
        return self.r.zadd(self.key,score,member)
    def delMember(self,member):
        if self.isRedisValid():
            return None
        return self.r.zrem(self.key,member)
    def incrScore(self,member,increment):
        """increase score on specified member"""
        if self.isRedisValid():
            return None
        return self.r.zincrby(self.key,member,increment)
    def getRankByMember(self,member):
        """Get ranking by specified member."""
        if self.isRedisValid():
            return None
        return self.r.zrank(self.key,member)
    def getLeaderboard(self,start,stop,reverse,with_score):
        """Return the whole leaderboard."""
        if self.isRedisValid():
            return None
        return self.r.zrange(self.key,start,stop,reverse,with_score)
    def getLeaderboardByPage(self,item_per_page,page_num,reverse=False,with_score=False):
        """Return part of leaderboard configurably."""
        # fix parameters
        if item_per_page <= 0:
            item_per_page = 5
        if page_num <= 0:
            page_num = 1
        # note: it is possible that return value is empty list.
        return self.getLeaderboard((page_num-1)*item_per_page,
                                    page_num*item_per_page-1,
                                            reverse,with_score)
    def getWholeLeaderboard(self,reverse=False,with_score=False):
        """Return the whole leaderboard."""
        return self.getLeaderboard(0,-1,reverse,with_score)

II、分布式鎖

1、我們通常所提到的鎖,一般都是在本地環(huán)境下對多線程完成互斥操作,而如果共享資源存在于網(wǎng)絡(luò)上,本地的鎖就不起作用了。
互斥訪問某個網(wǎng)絡(luò)資源的時候,需要有一個在網(wǎng)絡(luò)上的鎖服務(wù)器,負責(zé)鎖的申請與回收,Redis就可以作為這種分布式鎖的服務(wù)器。

2、因為Redis是單進程單線程的工作模式,所以前來申請鎖資源的請求都被排隊處理,能保證鎖資源的同步訪問。

3、實現(xiàn):可以在Redis服務(wù)器設(shè)置一個鍵值對,用以表示一把分布式互斥鎖,當(dāng)申請鎖的時候,申請方SET這個鍵值對,當(dāng)釋放鎖的時候,釋放方DEL這個鍵值對:

lock = redis.get("mutex_lock");
if(!lock)
    error("apply the lock error.");
else
    -- 確定可以申請鎖
    redis.set("mutex_lock","locking");
    do_something();

4、上述的申請方法,涉及到客戶端與Redis服務(wù)器的多次交互,當(dāng)客戶端確定可以加鎖的時候,可能這時候鎖已經(jīng)被其他客戶端申請了,最終導(dǎo)致兩個客戶端同時持有鎖。解決這個問題的方法就是將申請/釋放鎖的邏輯都放在服務(wù)器上。Redis Lua腳本可以完成這個問題:

-- apply for lock
local key = KEYS[1]
local res = redis.call('get', key)

-- 鎖被占用,申請失敗
if res == '0' then
return -1

-- 鎖可以被申請
else
local setres = redis.call('set', key, 0)
if setres['ok'] == 'OK' then
return 0
end
end
return -1

get 命令不成功返回(nil).
實驗命令:保存lua 腳本redis-cli script load ”$(cat mutex_lock.lua)”
-- releae lock
local key = KEYS[1]
local setres = redis.call('set', key, 1)
if setres['ok'] == 'OK' then
return 0
return -1

5、死鎖問題

分布式鎖由于客戶端的崩潰很容易造成鎖無法釋放,從而出現(xiàn)死鎖。所以需要使用Redis的TTL功能,設(shè)置超時釋放:

-- apply for lock
local key = KEYS[1]
local timeout = KEYS[2]

local res = redis.call('get', key)

-- 鎖被占用,申請失敗
if res == '0' then
return -1
-- 鎖可以被申請
else
local setres = redis.call('set', key, 0)
local exp_res = redis.call('pexpire', key, timeout)
if exp_res == 1 then
return 0
end
end
return -1

6、此外如出現(xiàn)服務(wù)器宕機,也會出現(xiàn)死鎖問題,這就需要slave服務(wù)器。

III、消息中間件

1、消息中間件可以理解為分布式的消息隊列。
消息中間件負責(zé)接收生產(chǎn)者的消息,并存儲轉(zhuǎn)發(fā)給對應(yīng)的消費者,生產(chǎn)者按照topic發(fā)布消息,消費者按topic訂閱各種消息。生產(chǎn)者只需要向中間件中推送消息,不用等待消費者回應(yīng)。

2、也就是說,這種分布式的消息中間件就是網(wǎng)絡(luò)上的一個服務(wù)器,起到一個數(shù)據(jù)中轉(zhuǎn)的功能。

IV、Web服務(wù)器存儲session

1、以Redis作為session的存儲,可以加速后臺的處理速度,如購物車數(shù)據(jù),就不必直接存儲到磁盤數(shù)據(jù)庫中,在這里Redis的作用就是一個緩存。

【參考】
[1] 《Redis源碼日志》

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Redis在很多方面與其他數(shù)據(jù)庫解決方案不同:它使用內(nèi)存提供主存儲支持,而僅使用硬盤做持久性的存儲;它的數(shù)據(jù)模型非...
    全能程序猿閱讀 24,792評論 0 8
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,680評論 19 139
  • string(字符串) 常用命令: set,get,decr,incr,mget 等。 使用SETBIT、GETB...
    陳小陌丿閱讀 1,017評論 0 3
  • 1. 取最新N個數(shù)據(jù)的操作 比如典型的取你網(wǎng)站的最新文章,通過下面方式,我們可以將最新的5000條評論的ID放在R...
    六月星空2011閱讀 526評論 0 1
  • 在英語的時態(tài)中有一個進行時還有一個完成時,其實想來應(yīng)該還有一個正在適應(yīng)時,當(dāng)你進入一個全新的領(lǐng)域,必然需要一個適應(yīng)...
    怡兒話書影閱讀 435評論 0 0

友情鏈接更多精彩內(nèi)容