使用場景:
某大型**部署是分布式的,訂單系統有三颱伺服器響應使用者請求,生成訂單後統一存放到order_info表;order_info表要求訂單id(order_id)必須是唯一的,那麼三颱伺服器怎麼協同工作來確認order_id的唯一性呢?這時候就要用到分布式鎖了。
分布式鎖的要求:
在了解了使用場景之後,再看一下我們需要的分布式鎖應該是怎樣的(以方法鎖為例)
這把鎖要可重入(防止死鎖)
這把鎖最好是乙個阻塞鎖(根據業務考慮是否需要這條)
有高可用的獲取鎖跟釋放鎖的功能
獲取鎖跟釋放鎖的效能要好
實現方式:
分布式鎖的實現分為3種,基於資料庫的,基於快取的跟基於zookeeper的。接下來我們對這三種方式進行實現。
基於資料庫的分布式鎖:
大概原理:直接建立一張鎖表,當要鎖住某個方法或者資源時,就在該表中增加一條記錄,想要釋放的時候就刪除這條記錄。
create table `methodlock` (`id` int(11) not null auto_increment comment '主鍵',
`method_name` varchar(64) not null default '' comment '鎖定的方法名',
`mydesc` varchar(1024) not null comment '備註資訊',
`update_time` timestamp not null default current_timestamp on update current_timestamp,
primary key (`id`),
unique key `uidx_method_name` (`method_name`) using btree
) engine=innodb default charset=utf8;
當我們想要鎖住某個方法時,執行以下sql:
insert into methodlock(method_name,desc) values ('具體方法名','描述');
以上的簡單實現有幾個問題:
1、這把鎖依賴資料庫的可用性,如果資料庫是乙個單點,一旦掛掉,會導致業務系統不可用;
2、這把鎖沒有失效時間,一旦解鎖操作失敗,會導致鎖一直存留在資料庫中,其它執行緒無法獲得鎖;
3、這把鎖只能是非阻塞的,因為資料的insert操作一旦插入失敗就直接報錯,沒有獲得鎖的執行緒不會進入排隊佇列,想要再次獲得鎖就要再次觸發獲得鎖的操作;
4、這把鎖是非重入的,同一執行緒在沒有釋放鎖之前無法再次獲得該鎖,因為表中資料已經存在了。
當然上面的問題也是可以解決的:
1、單點問題,兩個資料庫,雙向同步,一旦掛掉切換到另乙個上;
2、失效時間,做乙個定時任務,每隔多長時間清理超時資料;
3、非阻塞問題,程式寫for迴圈多次嘗試,直至獲取到鎖為止;
4、非重入,增加乙個字段,記錄獲取所的ip跟執行緒資訊,下次查詢的時候如果有,則直接給鎖;
示例**:
獲取鎖:
public boolean lock());}catch (exception e)
if(result == 1)
return false;
}
釋放鎖:
public boolean unlock());if(rows == 1)else
}
兩個執行緒,模擬兩個客戶端進行測試:
public void testlock() catch (interruptedexception e)system.out.println("t1釋放鎖了");
mysqllock.unlock();}}
});thread t2 = new thread(new runnable() catch (interruptedexception e)
trylock();
}//多次嘗試獲取鎖
private boolean trylock()else catch (interruptedexception e)
trylock();
mysqllock.unlock();
}return flag;
}});
t1.start();
t2.start();
try catch (interruptedexception e)
}
輸出結果:
t1拿到鎖,獲取的訂單id為:1
t2獲取鎖失敗,再次嘗試
t2獲取鎖失敗,再次嘗試
t2獲取鎖失敗,再次嘗試
t2獲取鎖失敗,再次嘗試
t2獲取鎖失敗,再次嘗試
t1釋放鎖了
t2拿到鎖,獲取訂單id為:2
可以看到,的確是t2等t1釋放鎖後才拿到了鎖進行了業務操作。
基於資料庫實現分布式鎖
多個程序 多個執行緒訪問共同元件資料庫.通過selec.for update訪問同一條資料 for update鎖定資料,其他執行緒只能等待 此時只有乙個操作可以對資料進行修改,而其他人不能夠對該資料進行修改操作,但可以檢視 select from distribute lock where bus...
基於資料庫悲觀鎖的分布式鎖
基於synchronized和reentrantlock鎖都只限於在單體架構中使用,當出現分布式跨jvm的情況,需要使用分布式鎖來解決跨jvm的問題。通過select for update 訪問同一條資料 for update 鎖定資料,其他執行緒只能等待 1.select autocommit 查...
基於資料庫的分布式鎖實現
一 基於資料庫表 要實現分布式鎖,最簡單的方式可能就是直接建立一張鎖表,然後通過操作該表中的資料來實現了。當我們要鎖住某個方法或資源的時候,我們就在該表中增加一條記錄,想要釋放鎖的時候就刪除這條記錄。建立這樣一張資料庫表 create table methodlock id int 11 not n...