1. 與MySQL等關係數據庫相同,Redis中也有事務機制,Redis的事務實質上是命令的集合,但Redis中的事務機制不保證事務的原子性,這與關係型數據庫中的事務不一樣,在一個事務中要麼全部命令都被執行,要麼全部事物都不執行。 一個事務從開始到執行會經歷如下三個階段:java
在MySQL中使用START TRANSACTION 或 BEGIN開啓一個事務,使用COMMIT提交一個事務;而在Redis中使用MULTI 開始一個事務,由 EXEC 命令觸發事務, 一併執行事務中的全部命令。和關係型數據庫中的事物相比,在redis事務中若是有某一條命令執行失敗,其它的命令仍然會被繼續執行,也就是Redis中不支持事務的回滾,也就不具有事務的原子性redis
2. Redis事務機制的相關指令:sql
3. 命令使用示例:數據庫
//正常執行 127.0.0.1:6379> redis-cli -h 127.0.0.1 -p 6379 //命令拼接redis服務器 ok 127.0.0.1:6379> get test //獲取test的鍵值 "hello world" 127.0.0.1:6379> multi //生成事務 ok 127.0.0.1:6379> set test "hello mygod" //修改指令 QUEUED 127.0.0.1:6379>exec //提交事務 1) OK 127.0.0.1:6379>
1. 產生背景:分佈式的CAP理論告訴咱們「任何一個分佈式系統都沒法同時知足一致性(Consistency)、可用性(Availability)和分區容錯性(Partition tolerance),最多隻能同時知足兩項。」因此,不少系統在設計之初就要對這三者作出取捨。在互聯網領域的絕大多數的場景中,都須要犧牲強一致性來換取系統的高可用性,系統每每只須要保證「最終一致性」,只要這個最終時間是在用戶能夠接受的範圍內便可。在不少場景中,咱們爲了保證數據的最終一致性,須要不少的技術方案來支持,好比分佈式事務、分佈式鎖等。有的時候,咱們須要保證一個方法在同一時間內只能被同一個線程執行。在單機環境中,Java中其實提供了不少併發處理相關的API,可是這些API在分佈式場景中就無能爲力了。也就是說單純的Java Api並不能提供分佈式鎖的能力。因此針對分佈式鎖的實現目前有多種方案。緩存
2. 實現分佈式鎖的方案:典型的方案有如下幾種bash
3. 分佈式鎖的要求:服務器
1. 基於數據庫表的實現:最簡單的方式可能就是直接建立一張鎖表,而後經過操做該表中的數據來實現,當咱們要鎖住某個方法或資源時,咱們就在該表中增長一條記錄,想要釋放鎖的時候就刪除這條記錄。併發
CREATE TABLE `methodLock` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵', `method_name` varchar(64) NOT NULL DEFAULT '' COMMENT '鎖定的方法名', `desc` varchar(1024) NOT NULL DEFAULT '備註信息', `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '保存數據時間,自動生成', PRIMARY KEY (`id`), UNIQUE KEY `uidx_method_name` (`method_name `) USING BTREE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='鎖定中的方法';
insert into methodLock(method_name,desc) values (‘method_name’,‘desc’)
由於咱們對method_name作了惟一性約束,這裏若是有多個插入請求同時提交到數據庫的話,數據庫會保證只有一個操做能夠成功,那麼咱們就能夠認爲操做成功的那個線程得到了該方法的鎖,能夠執行方法體內容。分佈式
delete from methodLock where method_name ='method_name'
2. 產生的問題:memcached
3. 解決辦法:
1. 相比較於基於數據庫實現分佈式鎖的方案來講,基於緩存來實如今性能方面會表現的更好一點(鏈接數據庫進行讀寫操做性能耗費比緩存大)。並且不少緩存是能夠集羣部署的,能夠解決單點問題。
2. Redis中有直接的命令支持,並且Redis的自己命令執行是一個單線程的,這就爲分佈式鎖提供了很好的實現,實現命令以下
3. Jedis客戶端也提供了相應的方法,主要就是setnx(String key,String value)方法,簡單實現思想僞代碼以下:
String get(String key) { //首先嚐試從redis(或redis集羣)中獲取key對應的數據 String value = redis.get(key); //若是爲null,則使用redis中的分佈式鎖 if (value == null) { //經過setnx方法建立分佈式鎖 if (redis.setnx(key_mutex, "1")) { // 設置分佈式鎖的過時時間,能夠避免死鎖 redis.expire(key_mutex, 3 * 60) value = db.get(key); //從數據庫中取得數據 redis.set(key, value);//回寫到緩存中 redis.delete(key_mutex);//釋放鎖 } else { //其餘線程休息50毫秒後重試 Thread.sleep(50); get(key); } } }
1. ZooKeeper是一個爲分佈式應用提供一致性服務的開源組件,它內部是一個分層的文件系統目錄樹結構,規定同一個目錄下只能有一個惟一文件名。基於ZooKeeper實現分佈式鎖的步驟以下:
(1)建立一個目錄mylock;
(2)線程A想獲取鎖就在mylock目錄下建立臨時順序節點;
(3)獲取mylock目錄下全部的子節點,而後獲取比本身小的兄弟節點,若是不存在,則說明當前線程順序號最小,得到鎖;
(4)線程B獲取全部節點,判斷本身不是最小節點,設置監聽比本身次小的節點;
(5)線程A處理完,刪除本身的節點,線程B監聽到變動事件,判斷本身是否是最小的節點,若是是則得到鎖。
這裏推薦一個Apache的開源庫Curator,它是一個ZooKeeper客戶端,Curator提供的InterProcessMutex是分佈式鎖的實現,acquire方法用於獲取鎖,release方法用於釋放鎖。
優勢:具有高可用、可重入、阻塞鎖特性,可解決失效死鎖問題。
缺點:由於須要頻繁的建立和刪除節點,性能上不如Redis方式