如下內容爲目前本身理解的總結,若有錯誤請你們指正。java
在單進程的系統中,當存在多個線程能夠同時改變某個變量(可變共享變量)時,就須要對變量或代碼塊作同步,使其在修改這種變量時可以線性執行消除併發修改變量。node
而同步的本質是經過鎖來實現的。爲了實現多個線程在一個時刻同一個代碼塊只能有一個線程可執行,那麼須要在某個地方作個標記,這個標記必須每一個線程都能看到,當標記不存在時能夠設置該標記,其他後續線程發現已經有標記了則等待擁有標記的線程結束同步代碼塊取消標記後再去嘗試設置標記。這個標記能夠理解爲鎖。linux
除了利用內存數據作鎖其實任何互斥的都能作鎖(只考慮互斥狀況),如流水錶中流水號與時間結合作冪等校驗能夠看做是一個不會釋放的鎖,或者使用某個文件是否存在做爲鎖等。只須要知足在對標記進行修改能保證原子性和內存可見性便可。redis
此處主要指集羣模式下,多個相同服務同時開啓.數據庫
與單機模式下的鎖不只須要保證進程可見,還須要考慮進程與鎖之間的網絡問題。(我以爲分佈式狀況下之因此問題變得複雜,主要就是須要考慮到網絡的延時和不可靠。。。一個大坑)安全
分佈式鎖仍是能夠將標記存在內存,只是該內存不是某個進程分配的內存而是公共內存如Redis、Memcache。至於利用數據庫、文件等作鎖與單機的實現是同樣的,只要保證標記能互斥就行。服務器
1. 直接調用Lua腳本原子setnx同時expire,設置一個隨機值。 2. 獲取到鎖則執行同步代碼塊,沒獲取則根據業務場景能夠選擇自旋、休眠、或作一個等待隊列等擁有鎖進程來喚醒(相似Synchronize的同步隊列)。 3. 當同步代碼塊執行完成,先判斷鎖的key是不是本身設置的,若是是則刪除key(可利用Lua作成原子操做),不是則代表本身的鎖已通過期,不須要刪除。(這時候就出現了多進程同時有鎖的問題了)
通常狀況下直接用setnx加expire就夠了,但從安全性的角度看仍是存在一下幾個問題:網絡
Redlock是Redis的做者antirez給出的集羣模式的Redis分佈式鎖,它基於N個徹底獨立的Redis節點(一般狀況下N能夠設置成5)。多線程
1. 獲取當前時間(毫秒數)。 2. 按順序依次向N個Redis節點執行獲取鎖的操做。獲取鎖的操做與單機鎖同樣。 3. 若是獲取鎖成功的節點數>=N/2+1,則再計算獲取鎖的時間有沒有超過鎖過時時間(可考慮設置一個必須留多長的時間給代碼塊執行),若是超過了則認爲取鎖失敗。 4. 若是取鎖失敗則應該對全部節點進行釋放鎖的操做。
原理:利用臨時節點與watch機制。每一個鎖佔用一個普通節點/lock,當須要獲取鎖時在/lock下建立一個臨時節點,建立成功則表示獲取鎖成功,失敗則watch/lock節點,有刪除操做後再去爭鎖。臨時節點好處在於當進程掛掉後能自動上鎖的節點自動刪除即取消鎖。併發
缺點:全部取鎖失敗的進程都監聽父節點,很容易發生羊羣效應,即當釋放鎖後全部等待進程一塊兒來建立節點,併發量很大。
原理:上鎖改成建立臨時有序節點,每一個上鎖的節點均能建立節點成功,知識其序號不一樣。只有序號最小的能夠擁有鎖,當須要不是最小的則watch序號排在前面的一個節點(公平鎖)。
步驟:
1. 在/lock節點下建立一個有序臨時節點(EPHEMERAL_SEQUENTIAL)。 2. 判斷建立的節點序號是否最小,若是是最小則獲取鎖成功。不是則取鎖失敗,而後watch序號比自己小的前一個節點。 3. 當取鎖失敗,設置watch後則等待watch事件到來後,再次判斷是否序號最小。 4. 取鎖成功則執行代碼,最後刪除自己節點,釋放了鎖。
目前就這些了。。。。後面想到再補充吧。
引用:基於Redis的分佈式鎖真的安全嗎?
基於Redis的分佈式鎖真的安全嗎?上
基於Redis的分佈式鎖真的安全嗎?下