當(dāng)我們與數(shù)據(jù)庫(kù)打交道時(shí),并發(fā)是經(jīng)常要進(jìn)行處理的,同時(shí)對(duì)一條記錄進(jìn)行讀取并更新時(shí),雖有先后順序,但就如同我們審核任務(wù)一樣,幾個(gè)人拿到同一個(gè)任務(wù),同時(shí)評(píng)分1分2分3分,最終這個(gè)任務(wù)的分?jǐn)?shù)是由最后提交的給更新了。當(dāng)更新完畢,再次拿到打分時(shí),會(huì)校驗(yàn)已經(jīng)打過(guò)分了,不允許評(píng)分。問(wèn)題就出現(xiàn)在同時(shí)更新時(shí)。
數(shù)據(jù)庫(kù)事務(wù)的隔離級(jí)別有4個(gè),由低到高依次為Read uncommitted、Read committed、Repeatable read、Serializable,這四個(gè)級(jí)別可以逐個(gè)解決臟讀、不可重復(fù)讀、幻讀這幾類問(wèn)題。
Read uncommitted:讀未提交的數(shù)據(jù)
Read committed: 讀提交
Repeatable read:不可重復(fù)讀
Serializable: 序列化
大多數(shù)據(jù)庫(kù)使用Read committed,這是性能和安全的結(jié)合點(diǎn),并使用加鎖的方式來(lái)解決安全問(wèn)題。
解決此類問(wèn)題有悲觀鎖和樂(lè)觀鎖兩種方式,
悲觀鎖:A查詢出這條任務(wù)時(shí)就會(huì)拿到一把鎖,這把鎖是更新鎖,其他人依然可以查出這條任務(wù),但是在A更新之前的過(guò)程中,后查詢出任務(wù)的BCD等不可以進(jìn)行更新,更新就會(huì)報(bào)錯(cuò)。
樂(lè)觀鎖:A查詢出這條任務(wù)時(shí),拿到這條任務(wù)的一個(gè)版本號(hào)version,沒(méi)更新一次version++,當(dāng)A進(jìn)行更新時(shí),一旦發(fā)現(xiàn)數(shù)據(jù)庫(kù)版本和自己拿到的版本不對(duì),就報(bào)錯(cuò),否則正常更新。
例子:
悲觀鎖:LockMode.UPGRADE實(shí)現(xiàn)加鎖
Task a = (Task)session.load(Task.class, 1, LockMode.UPGRADE);
樂(lè)觀鎖:在需要加鎖的類里加一個(gè)version字段,注解@Version
private int version;
@Version
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
常用的是樂(lè)觀鎖,悲觀鎖效率太低,一旦有人拿到任務(wù)不進(jìn)行更新,那就意味著所有人都會(huì)受影響。