❶ 使用redis實現的分布式鎖原理是什麼
一、寫在前面
現在面試,一般都會聊聊分布式系統這塊的東西。通常面試官都會從服務框架(Spring Cloud、Dubbo)聊起,一路聊到分布式事務、分布式鎖、ZooKeeper等知識。
所以咱們這篇文章就來聊聊分布式鎖這塊知識,具體的來看看Redis分布式鎖的實現原理。
說實話,如果在公司里落地生產環境用分布式鎖的時候,一定是會用開源類庫的,比如Redis分布式鎖,一般就是用Redisson框架就好了,非常的簡便易用。
大家如果有興趣,可以去看看Redisson的官網,看看如何在項目中引入Redisson的依賴,然後基於Redis實現分布式鎖的加鎖與釋放鎖。
下面給大家看一段簡單的使用代碼片段,先直觀的感受一下:
大家看到了吧,那個myLock的hash數據結構中的那個客戶端ID,就對應著加鎖的次數
(5)釋放鎖機制
如果執行lock.unlock(),就可以釋放分布式鎖,此時的業務邏輯也是非常簡單的。
其實說白了,就是每次都對myLock數據結構中的那個加鎖次數減1。
如果發現加鎖次數是0了,說明這個客戶端已經不再持有鎖了,此時就會用:
「del myLock」命令,從redis里刪除這個key。
然後呢,另外的客戶端2就可以嘗試完成加鎖了。
這就是所謂的分布式鎖的開源Redisson框架的實現機制。
一般我們在生產系統中,可以用Redisson框架提供的這個類庫來基於redis進行分布式鎖的加鎖與釋放鎖。
(6)上述Redis分布式鎖的缺點
其實上面那種方案最大的問題,就是如果你對某個redis master實例,寫入了myLock這種鎖key的value,此時會非同步復制給對應的master slave實例。
但是這個過程中一旦發生redis master宕機,主備切換,redis slave變為了redis master。
接著就會導致,客戶端2來嘗試加鎖的時候,在新的redis master上完成了加鎖,而客戶端1也以為自己成功加了鎖。
此時就會導致多個客戶端對一個分布式鎖完成了加鎖。
這時系統在業務語義上一定會出現問題,導致各種臟數據的產生。
所以這個就是redis cluster,或者是redis master-slave架構的主從非同步復制導致的redis分布式鎖的最大缺陷:在redis master實例宕機的時候,可能導致多個客戶端同時完成加鎖。
❷ 分布式鎖原理
分布式鎖是控制分布式系統之間同步訪問共享資源的一種方式。原理就是,當我們要實現分布式鎖,最簡單的方式可能就是直接創建一張鎖表,然後通過操作該表中的數據來實現了。
就是要鎖住某個方法或資源時,我們就在該表中增加一條記錄,想要釋放鎖的時候就刪除這條記錄。
在分布式系統中,常常需要協調他們的動作。如果不同的系統或是同一個系統的不同主機之間共享了一個或一組資源,那麼訪問這些資源的時候,往往需要互斥來防止彼此干擾來保證一致性,這個時候,便需要使用到分布式鎖。
當在分布式模型下,數據可能只有一份,此時需要利用鎖的技術控制某一時刻修改數據的進程數。與單機模式下的鎖不同,分布式鎖不僅需要保證進程可見,還需要考慮進程與鎖之間的網路問題。
分布式情況下之所以問題變得復雜,主要就是需要考慮到網路的延時和不可靠。分布式鎖還是可以將標記存在內存,只是該內存不是某個進程分配的內存而是公共內存如Redis、Memcache。至於利用資料庫、文件等做鎖與單機的實現是一樣的,只要保證標記能互斥就行。
❸ php 使用redis鎖限制並發訪問類示例
本文介紹了php
使用redis鎖限制並發訪問類,並詳細的介紹了並發訪問限制方法。
1.並發訪問限制問題
對於一些需要限制同一個用戶並發訪問的場景,如果用戶並發請求多次,而伺服器處理沒有加鎖限制,用戶則可以多次請求成功。
例如換領優惠券,如果用戶同一時間並發提交換領碼,在沒有加鎖限制的情況下,用戶則可以使用同一個換領碼同時兌換到多張優惠券。
偽代碼如下:
if
A(可以換領)
B(執行換領)
C(更新為已換領)
D(結束)
如果用戶並發提交換領碼,都能通過可以換領(A)的判斷,因為必須有一個執行換領(B)後,才會更新為已換領(C)。因此如果用戶在有一個更新為已換領之前,有多少次請求,這些請求都可以執行成功。
2.並發訪問限制方法
使用文件鎖可以實現並發訪問限制,但對於分布式架構的環境,使用文件鎖不能保證多台伺服器的並發訪問限制。
Redis是一個開源的使用ANSI
C語言編寫、支持網路、可基於內存亦可持久化的日誌型、Key-Value資料庫,並提供多種語言的API。
本文將使用其setnx方法實現分布式鎖功能。setnx即Set
it
N**ot
eX**ists。
當鍵值不存在時,插入成功(獲取鎖成功),如果鍵值已經存在,則插入失敗(獲取鎖失敗)
RedisLock.class.PHP
<?php
/**
*
Redis鎖操作類
*
Date:
2016-06-30
*
Author:
fdipzone
*
Ver:
1.0
*
*
Func:
*
public
lock
獲取鎖
*
public
unlock
釋放鎖
*
private
connect
連接
*/
class
RedisLock
{
//
class
start
private
$_config;
private
$_redis;
/**
*
初始化
*
@param
Array
$config
redis連接設定
*/
public
function
__construct($config=array()){
$this->_config
=
$config;
$this->_redis
=
$this->connect();
}
/**
*
獲取鎖
*
@param
String
$key
鎖標識
*
@param
Int
$expire
鎖過期時間
*
@return
Boolean
*/
public
function
lock($key,
$expire=5){
$is_lock
=
$this->_redis->setnx($key,
time()+$expire);
//
不能獲取鎖
if(!$is_lock){
//
判斷鎖是否過期
$lock_time
=
$this->_redis->get($key);
//
鎖已過期,刪除鎖,重新獲取
if(time()>$lock_time){
$this->unlock($key);
$is_lock
=
$this->_redis->setnx($key,
time()+$expire);
}
}
return
$is_lock?
true
:
false;
}
/**
*
釋放鎖
*
@param
String
$key
鎖標識
*
@return
Boolean
*/
public
function
unlock($key){
return
$this->_redis->del($key);
}
/**
*
創建redis連接
*
@return
Link
*/
private
function
connect(){
try{
$redis
=
new
Redis();
$redis->connect($this->_config['host'],$this->_config['port'],$this->_config['timeout'],$this->_config['reserved'],$this->_config['retry_interval']);
if(empty($this->_config['auth'])){
$redis->auth($this->_config['auth']);
}
$redis->select($this->_config['index']);
}catch(RedisException
$e){
throw
new
Exception($e->getMessage());
return
false;
}
return
$redis;
}
}
//
class
end
?>
demo.php
<?php
require
'RedisLock.class.php';
$config
=
array(
'host'
=>
'localhost',
'port'
=>
6379,
'index'
=>
0,
'auth'
=>
'',
'timeout'
=>
1,
'reserved'
=>
NULL,
'retry_interval'
=>
100,
);
//
創建redislock對象
$oRedisLock
=
new
RedisLock($config);
//
定義鎖標識
$key
=
'mylock';
//
獲取鎖
$is_lock
=
$oRedisLock->lock($key,
10);
if($is_lock){
echo
'get
lock
success<br>';
echo
'do
sth..<br>';
sleep(5);
echo
'success<br>';
$oRedisLock->unlock($key);
//
獲取鎖失敗
}else{
echo
'request
too
frequently<br>';
}
?>
測試方法:
打開兩個不同的瀏覽器,同時在A,B中訪問demo.php
如果先訪問的會獲取到鎖
輸出
get
lock
success
do
sth..
success
另一個獲取鎖失敗則會輸出request
too
frequently
保證同一時間只有一個訪問有效,有效限制並發訪問。
為了避免系統突然出錯導致死鎖,所以在獲取鎖的時候增加一個過期時間,如果已超過過期時間,即使是鎖定狀態都會釋放鎖,避免死鎖導致的問題。
源碼下載地址:點擊查看
❹ Redis分布式鎖的原理是什麼如何續期
在傳統單體應用單機部署的情況下,並發問題可以通過使用Java並發相關的鎖如synchronized,但是當規模上升到分布式集群的情況下,要控制共享資源訪問,就需要通過分布式鎖來實現。常見的分布式鎖方案如資料庫樂觀鎖,Redis鎖,zk鎖等。
Redis分布式鎖的原理
Redis分布式鎖可以有多種方式實現但是其核心就是通過以下三個Redis命令組合實現。
SETNX SETNX key val 當且僅當key不存在時,set一個key為val的字元串,返回1;若key存在,則什麼都不做,返回0。
Expire expire key timeout 為key設置一個超時時間,單位為second,超過這個時間鎖會自動釋放,避免死鎖。
Delete delete key 刪除key
核心思想
使用setnx獲取鎖。如果成功取到鎖,則使用expire命令為鎖添加一個超時時間,超過該時間則自動釋放鎖。
獲取鎖的時候還設置一個獲取的超時時間,若超過這個時間則放棄獲取鎖。
注意
上面為Redis的一個最簡單的鎖實現原理,實際中還需要考慮更多具體的情況作出相應的調整。如
上面的demo中,當集群系統時間不一致時會有問題
當伺服器異常關閉或是重啟,加鎖後沒來得急設置鎖超時時間,如何避免死鎖
實際開發環境中不確定的因素有很多,需要慢慢地去調整實踐達到理想狀態,可以考慮使用redisson框架來實現。
如何續期?
這個情況比較獨特,出現這個問題的根本原因在於鎖失效的時間小於業務處理的時間導致業務還沒處理完畢鎖就釋放了。那麼解決方案是合理地結合業務去設置鎖失效的時間。
但是也有更好的方案就如前文提到的redisson,其中的可重入鎖概念。
默認情況下,加鎖的時間是30秒.如果加鎖的業務沒有執行完,那麼到 30-10 = 20秒的時候,就會進行一次續期,把鎖重置成30秒。
以上就是redis鎖的原理及續期的方式,希望我的回答能對你有所幫助。
❺ 什麼是分布式鎖
成員變數 A 存在 JVM1、JVM2、JVM3 三個 JVM 內存中
成員變數 A 同時都會在 JVM 分配一塊內存,三個請求發過來同時對這個變數操作,顯然結果是不對的
不是同時發過來,三個請求分別操作三個不同 JVM 內存區域的數據,變數 A 之間不存在共享,也不具有可見性,處理的結果也是不對的
註:該成員變數 A 是一個有狀態的對象
如果我們業務中確實存在這個場景的話,我們就需要一種方法解決這個問題,這就是分布式鎖要解決的問題
分布式鎖應該具備哪些條件
在分布式系統環境下,一個方法在同一時間只能被一個機器的一個線程執行
高可用的獲取鎖與釋放鎖
高性能的獲取鎖與釋放鎖
具備可重入特性(可理解為重新進入,由多於一個任務並發使用,而不必擔心數據錯誤)
具備鎖失效機制,防止死鎖
具備非阻塞鎖特性,即沒有獲取到鎖將直接返回獲取鎖失敗
分布式鎖的實現有哪些
Memcached:利用 Memcached 的 add 命令。此命令是原子性操作,只有在 key 不存在的情況下,才能 add 成功,也就意味著線程得到了鎖。
Redis:和 Memcached 的方式類似,利用 Redis 的 setnx 命令。此命令同樣是原子性操作,只有在 key 不存在的情況下,才能 set 成功。
Zookeeper:利用 Zookeeper 的順序臨時節點,來實現分布式鎖和等待隊列。Zookeeper 設計的初衷,就是為了實現分布式鎖服務的。
Chubby:Google 公司實現的粗粒度分布式鎖服務,底層利用了 Paxos 一致性演算法。
通過 Redis 分布式鎖的實現理解基本概念
分布式鎖實現的三個核心要素:
加鎖
最簡單的方法是使用 setnx 命令。key 是鎖的唯一標識,按業務來決定命名。比如想要給一種商品的秒殺活動加鎖,可以給 key 命名為 「lock_sale_商品ID」 。而 value 設置成什麼呢?我們可以姑且設置成 1。加鎖的偽代碼如下:
setnx(lock_sale_商品ID,1)
當一個線程執行 setnx 返回 1,說明 key 原本不存在,該線程成功得到了鎖;當一個線程執行 setnx 返回 0,說明 key 已經存在,該線程搶鎖失敗。
解鎖
有加鎖就得有解鎖。當得到鎖的線程執行完任務,需要釋放鎖,以便其他線程可以進入。釋放鎖的最簡單方式是執行 del 指令,偽代碼如下:
del(lock_sale_商品ID)
釋放鎖之後,其他線程就可以繼續執行 setnx 命令來獲得鎖。
鎖超時
鎖超時是什麼意思呢?如果一個得到鎖的線程在執行任務的過程中掛掉,來不及顯式地釋放鎖,這塊資源將會永遠被鎖住(死鎖),別的線程再也別想進來。所以,setnx 的 key 必須設置一個超時時間,以保證即使沒有被顯式釋放,這把鎖也要在一定時間後自動釋放。
❻ 分布式鎖是什麼
什麼是分布式鎖?實現分布式鎖的三種方式
在很多場景中,我們為了保證數據的最終一致性,需要很多的技術方案來支持,比如分布式事務、分布式鎖等。那具體什麼是分布式鎖,分布式鎖應用在哪些業務場景、如何來實現分布式鎖呢?
一 為什麼要使用分布式鎖
我們在開發應用的時候,如果需要對某一個共享變數進行多線程同步訪問的時候,可以使用我們學到的鎖進行處理,並且可以完美的運行,毫無Bug!
注意這是單機應用,後來業務發展,需要做集群,一個應用需要部署到幾台機器上然後做負載均衡,大致如下圖:
六、基於ZooKeeper的實現方式
ZooKeeper是一個為分布式應用提供一致性服務的開源組件,它內部是一個分層的文件系統目錄樹結構,規定同一個目錄下只能有一個唯一文件名。基於ZooKeeper實現分布式鎖的步驟如下:
(1)創建一個目錄mylock;
(2)線程A想獲取鎖就在mylock目錄下創建臨時順序節點;
(3)獲取mylock目錄下所有的子節點,然後獲取比自己小的兄弟節點,如果不存在,則說明當前線程順序號最小,獲得鎖;
(4)線程B獲取所有節點,判斷自己不是最小節點,設置監聽比自己次小的節點;
(5)線程A處理完,刪除自己的節點,線程B監聽到變更事件,判斷自己是不是最小的節點,如果是則獲得鎖。
這里推薦一個Apache的開源庫Curator,它是一個ZooKeeper客戶端,Curator提供的InterProcessMutex是分布式鎖的實現,acquire方法用於獲取鎖,release方法用於釋放鎖。
優點:具備高可用、可重入、阻塞鎖特性,可解決失效死鎖問題。
缺點:因為需要頻繁的創建和刪除節點,性能上不如Redis方式。
七、總結
上面的三種實現方式,沒有在所有場合都是完美的,所以,應根據不同的應用場景選擇最適合的實現方式。
在分布式環境中,對資源進行上鎖有時候是很重要的,比如搶購某一資源,這時候使用分布式鎖就可以很好地控制資源。
當然,在具體使用中,還需要考慮很多因素,比如超時時間的選取,獲取鎖時間的選取對並發量都有很大的影響,上述實現的分布式鎖也只是一種簡單的實現,主要是一種思想。
❼ 怎樣實現redis分布式鎖
使用分布式鎖要滿足的幾個條件:系統是一個分布式系統(關鍵是分布式,單機的可以使用ReentrantLock或者synchronized代碼塊來實現)共享資源(各個系統訪問同一個資源,資源的載體可能是傳統關系型資料庫或者NoSQL)。
映射(Map)、多值映射(Multimap)、集(Set)、列表(List)、有序集(SortedSet)、計分排序集(ScoredSortedSet)、字典排序集(LexSortedSet)、列隊(Queue)、雙端隊列(Deque)、阻塞隊列(Blocking Queue)。
有界阻塞列隊(Bounded Blocking Queue)、阻塞雙端列隊(Blocking Deque)、阻塞公平列隊(Blocking Fair Queue)、延遲列隊(Delayed Queue)、優先隊列(Priority Queue)和優先雙端隊列(Priority Deque)。
❽ redis 為什麼需要分布式鎖
比如:秒殺,全局遞增ID,樓層生成等等。
大部分的解決方案是基於DB實現的,Redis為單進程單線程模式,採用隊列模式將並發訪問變成串列訪問,且多客戶端對Redis的連接並不存在競爭關系。
其次Redis提供一些命令SETNX,GETSET,可以方便實現分布式鎖機制。
❾ 什麼是分布式鎖及正確使用redis實現分布式鎖
Redis分布式鎖的安全性問題,在分布式系統專家和Redis的作者 antirez 之間就發生過一場爭論。由於對這個問題一直以來比較關注,所以我前些日子仔細閱讀了與這場爭論相關的資料。這場爭論的大概過程是這樣的:
為了規范各家對基於Redis的分布式鎖的實現,Redis的作者提出了一個更安全的實現,叫做 Redlock 。