最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

mysql居然還能實(shí)現(xiàn)分布式鎖的方法

 更新時間:2021年01月04日 11:28:16   作者:公眾號_程序員老貓  
這篇文章主要介紹了mysql居然還能實(shí)現(xiàn)分布式鎖的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

前言

之前的文章中通過電商場景中秒殺的例子和大家分享了單體架構(gòu)中鎖的使用方式,但是現(xiàn)在很多應(yīng)用系統(tǒng)都是相當(dāng)龐大的,很多應(yīng)用系統(tǒng)都是微服務(wù)的架構(gòu)體系,那么在這種跨jvm的場景下,我們又該如何去解決并發(fā)。

單體應(yīng)用鎖的局限性

在進(jìn)入實(shí)戰(zhàn)之前簡單和大家粗略聊一下互聯(lián)網(wǎng)系統(tǒng)中的架構(gòu)演進(jìn)。

在互聯(lián)網(wǎng)系統(tǒng)發(fā)展之初,消耗資源比較小,用戶量也比較小,我們只部署一個tomcat應(yīng)用就可以滿足需求。一個tomcat我們可以看做是一個jvm的進(jìn)程,當(dāng)大量的請求并發(fā)到達(dá)系統(tǒng)時,所有的請求都落在這唯一的一個tomcat上,如果某些請求方法是需要加鎖的,比如上篇文章中提及的秒殺扣減庫存的場景,是可以滿足需求的。但是隨著訪問量的增加,一個tomcat難以支撐,這時候我們就需要集群部署tomcat,使用多個tomcat支撐起系統(tǒng)。

在上圖中簡單演化之后,我們部署兩個Tomcat共同支撐系統(tǒng)。當(dāng)一個請求到達(dá)系統(tǒng)的時候,首先會經(jīng)過nginx,由nginx作為負(fù)載均衡,它會根據(jù)自己的負(fù)載均衡配置策略將請求轉(zhuǎn)發(fā)到其中的一個tomcat上。當(dāng)大量的請求并發(fā)訪問的時候,兩個tomcat共同承擔(dān)所有的訪問量。這之后我們同樣進(jìn)行秒殺扣減庫存的時候,使用單體應(yīng)用鎖,還能滿足需求么?

之前我們所加的鎖是JDK提供的鎖,這種鎖在單個jvm下起作用,當(dāng)存在兩個或者多個的時候,大量并發(fā)請求分散到不同tomcat,在每個tomcat中都可以防止并發(fā)的產(chǎn)生,但是多個tomcat之間,每個Tomcat中獲得鎖這個請求,又產(chǎn)生了并發(fā)。從而扣減庫存的問題依舊存在。這就是單體應(yīng)用鎖的局限性。那我們?nèi)绻鉀Q這個問題呢?接下來就要和大家分享分布式鎖了。

分布式鎖

什么是分布式鎖?

那么什么是分布式鎖呢,在說分布式鎖之前我們看到單體應(yīng)用鎖的特點(diǎn)就是在一個jvm進(jìn)行有效,但是無法跨越j(luò)vm以及進(jìn)程。所以我們就可以下一個不那么官方的定義,分布式鎖就是可以跨越多個jvm,跨越多個進(jìn)程的鎖,像這樣的鎖就是分布式鎖。

設(shè)計(jì)思路

由于tomcat是java啟動的,所以每個tomcat可以看成一個jvm,jvm內(nèi)部的鎖無法跨越多個進(jìn)程。所以我們實(shí)現(xiàn)分布式鎖,只能在這些jvm外去尋找,通過其他的組件來實(shí)現(xiàn)分布式鎖。

上圖兩個tomcat通過第三方的組件實(shí)現(xiàn)跨jvm,跨進(jìn)程的分布式鎖。這就是分布式鎖的解決思路。

實(shí)現(xiàn)方式

那么目前有哪些第三方組件來實(shí)現(xiàn)呢?目前比較流行的有以下幾種:

  • 數(shù)據(jù)庫,通過數(shù)據(jù)庫可以實(shí)現(xiàn)分布式鎖,但是高并發(fā)的情況下對數(shù)據(jù)庫的壓力比較大,所以很少使用。
  • Redis,借助redis可以實(shí)現(xiàn)分布式鎖,而且redis的java客戶端種類很多,所以使用方法也不盡相同。
  • Zookeeper,也可以實(shí)現(xiàn)分布式鎖,同樣zk也有很多java客戶端,使用方法也不同。

針對上述實(shí)現(xiàn)方式,老貓還是通過具體的代碼例子來一一演示。

基于數(shù)據(jù)庫的分布式鎖

思路:基于數(shù)據(jù)庫悲觀鎖去實(shí)現(xiàn)分布式鎖,用的主要是select ... for update。select ... for update是為了在查詢的時候就對查詢到的數(shù)據(jù)進(jìn)行了加鎖處理。當(dāng)用戶進(jìn)行這種行為操作的時候,其他線程是禁止對這些數(shù)據(jù)進(jìn)行修改或者刪除操作,必須等待上個線程操作完畢釋放之后才能進(jìn)行操作,從而達(dá)到了鎖的效果。

實(shí)現(xiàn):我們還是基于電商中超賣的例子和大家分享代碼。

咱們還是利用上次單體架構(gòu)中的超賣的例子和大家分享,針對上次的代碼進(jìn)行改造,我們新鍵一張表,叫做distribute_lock,這張表的目的主要是為了提供數(shù)據(jù)庫鎖,我們來看一下這張表的情況。

由于我們這邊模擬的是訂單超賣的場景,所以在上圖中我們有一條訂單的鎖數(shù)據(jù)。

我們將上一篇中的代碼改造一下抽取出一個controller然后通過postman去請求調(diào)用,當(dāng)然后臺是啟動兩個jvm進(jìn)行操作,分別是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;
  //購買商品數(shù)量
  private int purchaseProductNum = 1;
  
  @Transactional(propagation = Propagation.REQUIRED)
  public Integer createOrder() throws Exception{
    log.info("進(jìn)入了方法");
    DistributeLock lock = distributeLockMapper.selectDistributeLock("order");
    if(lock == null) throw new Exception("該業(yè)務(wù)分布式鎖未配置");
    log.info("拿到了鎖");
    //此處為了手動演示并發(fā),所以我們暫時在這里休眠1分鐘
    Thread.sleep(60000);

    KdProduct product = productMapper.selectByPrimaryKey(purchaseProductId);
    if (product==null){
      throw new Exception("購買商品:"+purchaseProductId+"不存在");
    }
    //商品當(dāng)前庫存
    Integer currentCount = product.getCount();
    log.info(Thread.currentThread().getName()+"庫存數(shù)"+currentCount);
    //校驗(yàn)庫存
    if (purchaseProductNum > currentCount){
      throw new Exception("商品"+purchaseProductId+"僅剩"+currentCount+"件,無法購買");
    }

    //在數(shù)據(jù)庫中完成減量操作
    productMapper.updateProductCount(purchaseProductNum,"kd",new Date(),product.getId());
    //生成訂單
    ...次數(shù)省略,源代碼可以到老貓的github下載:https://github.com/maoba/kd-distribute
    return order.getId();
  }
}

SQL的寫法如下:

select
  *
  from distribute_lock
  where business_code = #{business_code,jdbcType=VARCHAR}
  for update

以上為主要實(shí)現(xiàn)邏輯,關(guān)于代碼中的注意點(diǎn):

  • createOrder方法必須要有事務(wù),因?yàn)橹挥性谑聞?wù)存在的情況下才能觸發(fā)select for update的鎖。
  • 代碼中必須要對當(dāng)前鎖的存在性進(jìn)行判斷,如果為空的情況下,會報異常

我們來看一下最終運(yùn)行的效果,先看一下console日志,

8080的console日志情況:

11:49:41  INFO 16360 --- [nio-8080-exec-2] c.k.d.service.MySQLOrderService          : 進(jìn)入了方法
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          : 進(jìn)入了方法

通過日志情況,兩個不同的jvm,由于第一個到8080的請求優(yōu)先拿到了鎖,所以8081的請求就處于等待鎖釋放才會去執(zhí)行,這說明我們的分布式鎖生效了。
再看一下完整執(zhí)行之后的日志情況:

8080的請求:

11:58:01  INFO 15380 --- [nio-8080-exec-1] c.k.d.service.MySQLOrderService          : 進(jìn)入了方法
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庫存數(shù)1

8081的請求:

11:58:03  INFO 16276 --- [nio-8081-exec-1] c.k.d.service.MySQLOrderService          : 進(jìn)入了方法
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庫存數(shù)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 cause

java.lang.Exception: 商品100100僅剩0件,無法購買
 at com.kd.distribute.service.MySQLOrderService.createOrder(MySQLOrderService.java:61) ~[classes/:na]

很明顯第二個請求由于沒有庫存,導(dǎo)致最終購買失敗的情況,當(dāng)然這個場景也是符合我們正常的業(yè)務(wù)場景的。最終我們數(shù)據(jù)庫的情況是這樣的:

很明顯,我們到此數(shù)據(jù)庫的庫存和訂單數(shù)量也都正確了。到此我們基于數(shù)據(jù)庫的分布式鎖實(shí)戰(zhàn)演示完成,下面我們來歸納一下如果使用這種鎖,有哪些優(yōu)點(diǎn)以及缺點(diǎn)。

  • 優(yōu)點(diǎn):簡單方便、易于理解、易于操作。
  • 缺點(diǎn):并發(fā)量大的時候?qū)?shù)據(jù)庫的壓力會比較大。
  • 建議:作為鎖的數(shù)據(jù)庫和業(yè)務(wù)數(shù)據(jù)庫分開。

寫在最后

對于上述數(shù)據(jù)庫分布式鎖,其實(shí)在我們的日常開發(fā)中用的也是比較少的?;趓edis以及zk的鎖倒是用的比較多一些,本來老貓想把redis鎖以及zk鎖放在這一篇中一起分享掉,但是再寫在同一篇上面的話,篇幅就顯得過長了,因此本篇就和大家分享這一種分布式鎖。源碼大家可以在老貓的github中下載到。地址是:https://github.com/maoba/kd-distribute

到此這篇關(guān)于mysql居然還能實(shí)現(xiàn)分布式鎖的方法的文章就介紹到這了,更多相關(guān)mysql 分布式鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 一文掌握MySQL表的創(chuàng)建和約束

    一文掌握MySQL表的創(chuàng)建和約束

    這篇文章主要和大家分享一下數(shù)據(jù)庫的創(chuàng)建和銷毀語法以及詳細(xì)講解MySQL表的創(chuàng)建代碼和約束的使用,文中的示例代碼講解詳細(xì),感興趣的可以了解一下
    2022-07-07
  • mysql like 特殊字符搜索的實(shí)現(xiàn)

    mysql like 特殊字符搜索的實(shí)現(xiàn)

    在MySQL中,LIKE操作符用于在查詢數(shù)據(jù)時進(jìn)行模糊匹配,本文主要介紹了mysql like 特殊字符搜索的實(shí)現(xiàn),具有一定的參考價值,感興趣的可以了解一下
    2023-09-09
  • MySQL日期加減函數(shù)詳解

    MySQL日期加減函數(shù)詳解

    在本篇文章里小編給大家分享的是關(guān)于MySQL日期加減函數(shù)用法以及實(shí)例,需要的朋友們可以參考下。
    2020-05-05
  • MySQL 5.7安裝中的常見問題分析及解決方案

    MySQL 5.7安裝中的常見問題分析及解決方案

    在Linux服務(wù)器上安裝MySQL數(shù)據(jù)庫時,尤其是在CentOS 7等操作系統(tǒng)上,可能會遇到一些安裝包和依賴關(guān)系相關(guān)的錯誤,這些問題的產(chǎn)生原因可以是多個方面,本文將通過對具體錯誤的分析和解決方案的闡述,幫助大家快速解決在安裝MySQL 5.7版本過程中可能遇到的問題
    2024-11-11
  • MySQL按天分組統(tǒng)計(jì)一定時間內(nèi)的數(shù)據(jù)實(shí)例(沒有數(shù)據(jù)補(bǔ)0)

    MySQL按天分組統(tǒng)計(jì)一定時間內(nèi)的數(shù)據(jù)實(shí)例(沒有數(shù)據(jù)補(bǔ)0)

    我們在用Mysql制作數(shù)據(jù)可視化圖表時候,經(jīng)常需要按照天對數(shù)據(jù)進(jìn)行分組統(tǒng)計(jì),下面這篇文章主要給大家介紹了關(guān)于MySQL按天分組統(tǒng)計(jì)一定時間內(nèi)的數(shù)據(jù),沒有數(shù)據(jù)補(bǔ)0的相關(guān)資料,需要的朋友可以參考下
    2023-03-03
  • mysql獲取當(dāng)前日期年月的兩種實(shí)現(xiàn)方式

    mysql獲取當(dāng)前日期年月的兩種實(shí)現(xiàn)方式

    這篇文章主要介紹了mysql獲取當(dāng)前日期年月的兩種實(shí)現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • MySQL數(shù)據(jù)表基本操作實(shí)例詳解

    MySQL數(shù)據(jù)表基本操作實(shí)例詳解

    這篇文章主要介紹了MySQL數(shù)據(jù)表基本操作,結(jié)合實(shí)例形式較為詳細(xì)的分析了MySQL針對數(shù)據(jù)表的基本創(chuàng)建、表結(jié)構(gòu)查看、修改、刪除等相關(guān)操作技巧,需要的朋友可以參考下
    2018-06-06
  • MySQL如何導(dǎo)入csv格式數(shù)據(jù)文件解決方案

    MySQL如何導(dǎo)入csv格式數(shù)據(jù)文件解決方案

    本文將詳細(xì)介紹MySQL如何導(dǎo)入csv格式數(shù)據(jù)文件并提供詳細(xì)解決方案,需要了解的朋友可以參考下
    2012-11-11
  • mysql中find_in_set()函數(shù)用法及自定義增強(qiáng)函數(shù)詳解

    mysql中find_in_set()函數(shù)用法及自定義增強(qiáng)函數(shù)詳解

    這篇文章主要給大家介紹了關(guān)于mysql中find_in_set()函數(shù)用法及自定義增強(qiáng)函數(shù)的相關(guān)資料,在MySQL 數(shù)據(jù)庫中進(jìn)行復(fù)雜的查詢語句,例如對多個字段進(jìn)行篩選和排序,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-06-06
  • mysql decimal類型判斷是否為0

    mysql decimal類型判斷是否為0

    在MySQL中,DECIMAL是一種用于存儲精確小數(shù)的數(shù)據(jù)類型,本文主要介紹了mysql decimal類型判斷是否為0,具有一定的參考價值,感興趣的可以了解一下
    2024-02-02

最新評論

礼泉县| 沙坪坝区| 灵丘县| 东港市| 古丈县| 卫辉市| 拜泉县| 宾川县| 徐闻县| 巍山| 新化县| 宁都县| 高清| 天柱县| 镇赉县| 新安县| 平度市| 东台市| 阳高县| 山丹县| 仁布县| 锡林浩特市| 蓬溪县| 郑州市| 驻马店市| 华阴市| 应用必备| 通河县| 杂多县| 宣武区| 洛浦县| 冕宁县| 彭泽县| 合作市| 临沭县| 浪卡子县| 兰考县| 红桥区| 井冈山市| 观塘区| 济宁市|