分布式鎖之redis實(shí)現(xiàn)

前言

傳統(tǒng)的鎖(比如編程語言里的lock)厕氨,都是對(duì)多線程進(jìn)行控制进每,但是對(duì)多進(jìn)程、或者多客戶端就無能為力了命斧,為此田晚,就誕生了分布式鎖。

通常国葬,redis會(huì)被用作緩存功能贤徒,但是芹壕,它也可以有其他的一些用途。本文接奈,就是利用redis實(shí)現(xiàn)分布式鎖功能踢涌。當(dāng)然,實(shí)現(xiàn)分布式鎖還有很多種其他方式鲫趁,比如:

  • 基于數(shù)據(jù)庫樂觀鎖
  • 基于ZooKeeper的分布式鎖

不管哪種方式斯嚎,他的基本原理是不變的:用一個(gè)狀態(tài)值表示鎖,對(duì)鎖的占用和釋放通過狀態(tài)值來標(biāo)識(shí)挨厚。

基本原理

redis為單進(jìn)程單線程模式堡僻,采用隊(duì)列模式將并發(fā)訪問變成串行訪問,且多客戶端對(duì)redis的連接并不存在競爭關(guān)系疫剃。

關(guān)鍵命令

> SETNX key value
# SETNX: SET if Not eXists
# 將key的值設(shè)置為value
# 當(dāng)key存在時(shí)钉疫,不做任何操作,返回   0
# 當(dāng)key不存在時(shí)巢价,進(jìn)行設(shè)置操作牲阁,返回 1

> GET key
# 返回key的值

> GETSET key value
# 返回key的舊值,同時(shí)將key的值設(shè)置成value

實(shí)現(xiàn)的基本思想

key為任意值壤躲,value代表過期時(shí)間(unix time = now + expire)

實(shí)現(xiàn)Lock函數(shù)城菊,此函數(shù)進(jìn)行一次加鎖嘗試。

首先調(diào)用SETNX設(shè)置碉克,如果成功凌唬,則成功獲得鎖。否則漏麦,用GET獲取key值客税,和當(dāng)前時(shí)間比較,如果對(duì)比發(fā)現(xiàn)過期撕贞,放棄加鎖更耻。否則進(jìn)行下一步。用GETSET獲取key值捏膨,如果發(fā)現(xiàn)未過期秧均,則加鎖成功。 如果過期号涯,則表示有其他客戶端已經(jīng)先于本客戶端設(shè)置熬北,放棄加鎖。

Talk is cheap, show me the code.

下面诚隙,就用一個(gè)redis單實(shí)例實(shí)現(xiàn)分布式鎖。如果是多客戶端使用起胰,確本糜郑客戶端時(shí)間一致巫延。

/*
 expire: N秒后鎖失效,允許其他客戶端競爭
*/
func Lock(conn redis.Conn, key string, expire int) bool {
    var now int64 = time.Now().Unix()

    r1, err := conn.Do("SETNX", key, now+int64(expire))
    if err != nil {
        return false
    }

    v1, err := redis.Int(r1, err)
    if err != nil {
        return false
    }

    if v1 == 1 {
        return true
    }

    /*此時(shí)key存在地消,查看對(duì)應(yīng)的值*/
    r, err := conn.Do("GET", key)
    if err != nil {
        return false
    }

    v2, err := redis.Int64(r, err)
    if err != nil {
        return false
    }

    if now < v2 {
        /*值未過期炉峰,表示其他客戶端占用資源,放棄鎖*/
        return false
    } else {
        /*獲取舊值脉执,設(shè)置新值*/
        r, err := conn.Do("GETSET", key, now+int64(expire))
        if err != nil {
            return false
        }

        v3, err := redis.Int64(r, err)
        if err != nil {
            return false
        }

        if now >= v3 {
            return true
        }
        /*
          else情況:表示其他redis客戶端搶先一步設(shè)置成功疼阔,此時(shí)放棄鎖
          return false
        */
    }

    return false
}

/*
 釋放鎖
*/
func Unlock(conn redis.Conn, key string) bool {
    var _, err = conn.Do("DEL", key)
    if err != nil {
        return false
    }

    return true
}

/*
 嘗試加鎖
*/
func TryLock(conn redis.Conn, key string, expire int, timeout int) bool {
    var b = Lock(conn, key, expire)
    if b {
        return b
    }

    if timeout == 0 {
        return false
    }
    var ticker = time.NewTicker(time.Duration(timeout) * time.Second)
    defer ticker.Stop()
    for {
        select {
        case <-time.After(100 * time.Millisecond):
            if Lock(conn, key, expire) {
                /*成功lock后返回,否則一直持續(xù)到超時(shí)*/
                return true
            }
        }

        select {
        case <-ticker.C:
            return false
        default:
            //DO NOTHING
        }
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末半夷,一起剝皮案震驚了整個(gè)濱河市婆廊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌巫橄,老刑警劉巖淘邻,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異湘换,居然都是意外死亡宾舅,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門彩倚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來筹我,“玉大人,你說我怎么就攤上這事帆离∈呷铮” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵盯质,是天一觀的道長袁串。 經(jīng)常有香客問我,道長呼巷,這世上最難降的妖魔是什么囱修? 我笑而不...
    開封第一講書人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮王悍,結(jié)果婚禮上破镰,老公的妹妹穿的比我還像新娘。我一直安慰自己压储,他們只是感情好鲜漩,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著集惋,像睡著了一般孕似。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上刮刑,一...
    開封第一講書人閱讀 51,165評(píng)論 1 299
  • 那天喉祭,我揣著相機(jī)與錄音养渴,去河邊找鬼。 笑死泛烙,一個(gè)胖子當(dāng)著我的面吹牛理卑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蔽氨,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼藐唠,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了鹉究?” 一聲冷哼從身側(cè)響起宇立,我...
    開封第一講書人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎坊饶,沒想到半個(gè)月后泄伪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡匿级,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年蟋滴,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片痘绎。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡津函,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出孤页,到底是詐尸還是另有隱情尔苦,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布行施,位于F島的核電站允坚,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蛾号。R本人自食惡果不足惜稠项,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望鲜结。 院中可真熱鬧展运,春花似錦、人聲如沸精刷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽怒允。三九已至埂软,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間纫事,已是汗流浹背仰美。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來泰國打工迷殿, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人咖杂。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像蚊夫,于是被迫代替她去往敵國和親诉字。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容