您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關怎么在mysql中實現一個分布式鎖,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
那么什么是分布式鎖呢,在說分布式鎖之前我們看到單體應用鎖的特點就是在一個jvm進行有效,但是無法跨越jvm以及進程。所以我們就可以下一個不那么官方的定義,分布式鎖就是可以跨越多個jvm,跨越多個進程的鎖,像這樣的鎖就是分布式鎖。
由于tomcat是java啟動的,所以每個tomcat可以看成一個jvm,jvm內部的鎖無法跨越多個進程。所以我們實現分布式鎖,只能在這些jvm外去尋找,通過其他的組件來實現分布式鎖。
上圖兩個tomcat通過第三方的組件實現跨jvm,跨進程的分布式鎖。這就是分布式鎖的解決思路。
那么目前有哪些第三方組件來實現呢?目前比較流行的有以下幾種:
數據庫,通過數據庫可以實現分布式鎖,但是高并發的情況下對數據庫的壓力比較大,所以很少使用。
Redis,借助redis可以實現分布式鎖,而且redis的java客戶端種類很多,所以使用方法也不盡相同。
Zookeeper,也可以實現分布式鎖,同樣zk也有很多java客戶端,使用方法也不同。
針對上述實現方式,老貓還是通過具體的代碼例子來一一演示。
思路:基于數據庫悲觀鎖去實現分布式鎖,用的主要是select ... for update。select ... for update是為了在查詢的時候就對查詢到的數據進行了加鎖處理。當用戶進行這種行為操作的時候,其他線程是禁止對這些數據進行修改或者刪除操作,必須等待上個線程操作完畢釋放之后才能進行操作,從而達到了鎖的效果。
實現:我們還是基于電商中超賣的例子和大家分享代碼。
咱們還是利用上次單體架構中的超賣的例子和大家分享,針對上次的代碼進行改造,我們新鍵一張表,叫做distribute_lock,這張表的目的主要是為了提供數據庫鎖,我們來看一下這張表的情況。
由于我們這邊模擬的是訂單超賣的場景,所以在上圖中我們有一條訂單的鎖數據。
我們將上一篇中的代碼改造一下抽取出一個controller然后通過postman去請求調用,當然后臺是啟動兩個jvm進行操作,分別是8080端口以及8081端口。完成之后的代碼如下:
/** * @author kdaddy@163.com * @date 2021/1/3 10:48 * @desc 公眾號“程序員老貓” */ @Service @Slf4j public class MySQLOrderService { @Resource private KdOrderMapper orderMapper; @Resource private KdOrderItemMapper orderItemMapper; @Resource private KdProductMapper productMapper; @Resource private DistributeLockMapper distributeLockMapper; //購買商品id private int purchaseProductId = 100100; //購買商品數量 private int purchaseProductNum = 1; @Transactional(propagation = Propagation.REQUIRED) public Integer createOrder() throws Exception{ log.info("進入了方法"); DistributeLock lock = distributeLockMapper.selectDistributeLock("order"); if(lock == null) throw new Exception("該業務分布式鎖未配置"); log.info("拿到了鎖"); //此處為了手動演示并發,所以我們暫時在這里休眠1分鐘 Thread.sleep(60000); KdProduct product = productMapper.selectByPrimaryKey(purchaseProductId); if (product==null){ throw new Exception("購買商品:"+purchaseProductId+"不存在"); } //商品當前庫存 Integer currentCount = product.getCount(); log.info(Thread.currentThread().getName()+"庫存數"+currentCount); //校驗庫存 if (purchaseProductNum > currentCount){ throw new Exception("商品"+purchaseProductId+"僅剩"+currentCount+"件,無法購買"); } //在數據庫中完成減量操作 productMapper.updateProductCount(purchaseProductNum,"kd",new Date(),product.getId()); //生成訂單 ...次數省略,源代碼可以到老貓的github下載:https://github.com/maoba/kd-distribute return order.getId(); } }
SQL的寫法如下:
select * from distribute_lock where business_code = #{business_code,jdbcType=VARCHAR} for update
以上為主要實現邏輯,關于代碼中的注意點:
createOrder方法必須要有事務,因為只有在事務存在的情況下才能觸發select for update的鎖。
代碼中必須要對當前鎖的存在性進行判斷,如果為空的情況下,會報異常
我們來看一下最終運行的效果,先看一下console日志,
8080的console日志情況:
11:49:41 INFO 16360 --- [nio-8080-exec-2] c.k.d.service.MySQLOrderService : 進入了方法
11:49:41 INFO 16360 --- [nio-8080-exec-2] c.k.d.service.MySQLOrderService : 拿到了鎖
8081的console日志情況:
11:49:48 INFO 17640 --- [nio-8081-exec-2] c.k.d.service.MySQLOrderService : 進入了方法
通過日志情況,兩個不同的jvm,由于第一個到8080的請求優先拿到了鎖,所以8081的請求就處于等待鎖釋放才會去執行,這說明我們的分布式鎖生效了。
再看一下完整執行之后的日志情況:
8080的請求:
11:58:01 INFO 15380 --- [nio-8080-exec-1] c.k.d.service.MySQLOrderService : 進入了方法
11:58:01 INFO 15380 --- [nio-8080-exec-1] c.k.d.service.MySQLOrderService : 拿到了鎖
11:58:07 INFO 15380 --- [nio-8080-exec-1] c.k.d.service.MySQLOrderService : http-nio-8080-exec-1庫存數1
8081的請求:
11:58:03 INFO 16276 --- [nio-8081-exec-1] c.k.d.service.MySQLOrderService : 進入了方法
11:58:08 INFO 16276 --- [nio-8081-exec-1] c.k.d.service.MySQLOrderService : 拿到了鎖
11:58:14 INFO 16276 --- [nio-8081-exec-1] c.k.d.service.MySQLOrderService : http-nio-8081-exec-1庫存數0
11:58:14 ERROR 16276 --- [nio-8081-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.Exception: 商品100100僅剩0件,無法購買] with root causejava.lang.Exception: 商品100100僅剩0件,無法購買
at com.kd.distribute.service.MySQLOrderService.createOrder(MySQLOrderService.java:61) ~[classes/:na]
很明顯第二個請求由于沒有庫存,導致最終購買失敗的情況,當然這個場景也是符合我們正常的業務場景的。最終我們數據庫的情況是這樣的:
很明顯,我們到此數據庫的庫存和訂單數量也都正確了。到此我們基于數據庫的分布式鎖實戰演示完成,下面我們來歸納一下如果使用這種鎖,有哪些優點以及缺點。
優點:簡單方便、易于理解、易于操作。
缺點:并發量大的時候對數據庫的壓力會比較大。
建議:作為鎖的數據庫和業務數據庫分開。
以上就是怎么在mysql中實現一個分布式鎖,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。