PS:只有共享鎖與共享鎖相互兼容,共享鎖與排它鎖、排它鎖之間都互不兼容
這兩種鎖機制的區別在於MySQL的查詢與更新操作互相不阻塞;而SQL SERVER的更新鎖轉化成排它鎖之前,其查詢與更新操作互相不阻塞,當更新鎖要轉化為排它鎖時,需要等待共享鎖的釋放,當更新鎖轉化為排它鎖後,查詢數據需要等待排它鎖的釋放。
參考:
[數據庫鎖機制](http://blog.csdn.net/samjustin1/article/details/52210125)
[InnoDB鎖機制](http://blog.chinaunix.net/uid-24111901-id-2627857.html)
[SQL SERVER鎖機制](http://blog.itpub.net/13651903/viewspace-1091664/)
update table set date=1,version=version+1 where id=#{id} and version=#{version};
參考:
[樂觀鎖與悲觀鎖](http://www.open-open.com/lib/view/open1452046967245.html)
CAS是項樂觀鎖技術,當多個線程嘗試使用CAS同時更新同一個變量時,只有其中一個線程能更新變量的值,而其它線程都失敗,失敗的線程並不會被掛起,而是被告知這次競爭中失敗,並可以再次嘗試。
CAS 操作包含三個操作數 —— 內存位置(V)、預期原值(A)和新值(B)。如果內存位置的值與預期原值相匹配,那麼處理器會自動將該位置值更新為新值。否則,處理器不做任何操作。無論哪種情況,它都會在 CAS 指令之前返回該位置的值。CAS 有效地說明了“我認為位置 V 應該包含值 A;如果包含該值,則將 B 放到這個位置;否則,不要更改該位置,只告訴我這個位置現在的值即可。”這其實和樂觀鎖的沖突檢查+數據更新的原理是一樣的。
java.util.concurrent(J.U.C)就是建立在CAS之上的。相對於對於synchronized這種阻塞算法,CAS是非阻塞算法的一種常見實現。所以J.U.C在性能上有了很大的提升。
public class AtomicInteger extends Number implements java.io.Serializable { private volatile int value; public final int get() { return value; } public final int getAndIncrement() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return current; } } public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); } }
參考:
[樂觀鎖的一種實現方式—CAS](http://www.importnew.com/20472.html)
step1:
select id,passCount,rejectCount,hideCount,warnCount,waitCount from book.TradeItemAuditCount where type = #{type} and date = #{date} and editor = #{editor} and isDeleted = 0 limit 1
step 2:【失敗重試】
update book.TradeItemAuditCount set passCount = #{passCount} , rejectCount = #{rejectCount} , hideCount = #{hideCount} , warnCount = #{warnCount} , waitCount = #{waitCount} , updated = #{updated} where id = #{id} and passCount = #{oldPassCount} and rejectCount = #{oldRejectCount} and hideCount = #{oldHideCount} and warnCount = #{oldWarnCount} and waitCount = #{oldWaitCount} and isDeleted = 0 limit 1
update 庫存表 set stock=stock-1 where id=#{id}
{"tags":"16,32,233,22","itemState":1,"hd":"ai:4|nd:18","au":"baoming"}
1.樂觀鎖:采用CAS
update TradeItem set extra=#{extra} where tradeItemId=#{tradeItemId} and extra=#{oldExtra}
這裡使用長字符串做更新條件,會影響到SQL性能
2.樂觀鎖:采用數據版本 表中新增version字段標識數據版本,作為數據更新的檢查方式
update TradeItem set extra=#{extra} , version=version+1 where tradeItemId=#{tradeItemId} and version=#{version}
此方案改造較大,還需要為表新增字段,而且采用樂觀鎖擁有這一律的弊端:重試帶來的時間代價,一旦並發量上漲,某次更新操作的重試次數也會隨之上漲,直接影響到暴露服務的響應時間。【限制重試次數能夠一定程度上控制更新操作的響應時間,但是仍然會出現更新丟失的現象(讓調用方進行重試操作,分攤單次請求的響應時間?)】
3. 悲觀鎖:更新丟失的根本原因是執行查詢、修改兩個操作之間數據被另一事務修改了,單純的UPDATE操作其實也是進行著先查詢後修改的操作,沒有產生更新丟失是因為數據上存在排它鎖(sql server則是更新鎖),在執行期間並不允許其他修改。同理我們將要打標的商品記錄加上排它鎖或者更新鎖就能解決問題。
MySQL:
start transaction; SELECT extra FROM TradeItem WHERE tradeItemId=#{tradeItemId} FOR UPDATE; UPDATE TradeItem SET extra = bdo.AddTag(tag,extra) WHERE tradeItemId=#{tradeItemId}; commit;
SQL SERVER:
BEGIN TRANSACTION --開始一個事務 SELECT extra FROM TradeItem WITH (UPDLOCK) WHERE tradeItemId=#{tradeItemId} UPDATE TradeItem SET extra = bdo.AddTag(tag,extra) WHERE tradeItemId=#{tradeItemId} COMMIT TRANSACTION --提交事務
該方案避免了重試帶來的開銷,同時使用排它鎖(更新鎖)也沒有額外增加鎖的開銷