企業面對高併發場景採用的方案.html
好比 產品搶購高併發時的超發現象.數據庫
1 悲觀鎖
悲觀鎖 須要數據庫自己提供支持(Oracle和MySQL都是支持的).
實現細節:
當前 數據庫事務 讀取到產品後, 就將目標數據直接鎖定(select ... for update), 不容許別的線程進行讀寫操做, 知道 當前數據庫事務完成自動釋放鎖.
悲觀鎖中, 資源只能被一個事務鎖持有, 因此也被稱爲 獨佔鎖 or 排它鎖.服務器
悲觀鎖的優勢是實現簡單好理解, 缺點是過多的等待和響應的事務切換致使性能問題.
爲提升運行效率, 能夠採用 樂觀鎖.併發
2 樂觀鎖
樂觀鎖 是一種 不使用數據庫鎖 和 不阻塞線程併發 的方案.
著名的 CAS(Compare and Swap): 線程在修改目標數據時, 會將目標數據的當前狀態和舊狀態進行比較(Compare), 若是狀態一直, 就能夠認爲 數據木有被修改過, 不然就認爲數據已經不一樣步了, 當前計算做廢, 不作任何修改操做(回滾).
CAS方案卻會引起 ABA問題, 就是共享值回退致使數據的不一致問題, 有點像髒讀.
採用 帶版本號(version)的樂觀鎖方案, 再配合CAS, 就能夠解決 ABA問題了.
實現細節:
給產品表添加 version字段, 只要操做過程當中有修改產品狀態(好比減小庫存), 不管是業務正常 回退 仍是異常, 版本號只增不減. 即
update t_product set stock=stock-#{quantity}, version=version+1
where id=#{id} and version=#{version}
樂觀鎖沒有獨佔資源和阻塞任何線程, 因此樂觀鎖也稱爲 非獨佔鎖 or 無阻塞鎖.
在實際使用樂觀鎖時, 會發現線程的業務失敗率會很高, 針對這點能夠對 樂觀鎖 引入重入機制. 也就是一旦業務失敗, 不是當即結束請求, 而是從新作一次樂觀鎖流程, 可限制重入時間or重入次數.高併發
樂觀鎖優勢是不獨佔不阻塞, 缺點是實現相對複雜.性能
3 Redis
有些企業已經開始使用 NoSQL 來處理高併發問題, 表明就是 Redis(內存數據庫).
首先, Redis 是內存數據庫, 因此性能是沒的說的. 其次, Redis Lua 在 Redis 的執行中是具有原子性的, 因此不會發送超發現象.
兩步設計:
1) 使用 Redis 響應高併發用戶請求
即用 Redis 代替原來的磁盤數據庫 讀寫, 保證性能和數據一致性.
2) 定時持久化 Redis 數據
內存數據存儲是不穩定的, 咱們須要及時將保存在內存中的數據持久化到磁盤數據庫中.spa
使用 Redis 效率要比悲觀鎖和樂觀鎖機制快上數倍, 可是千萬記住 Redis 的存儲基於內存, 若是操做不當容易引起數據的丟失, 因此使用 Redis 時建議使用獨立的 Redis 服務器, 並且要作好備份和容災等手段.線程
參考:
https://www.cnblogs.com/zhiqian-ali/p/6200874.html
<<深刻淺出 Spring Boot 2.x>> 楊開振設計