国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - 編程技術 - 分布式鎖看了又看,優(yōu)秀方案我來告訴你

分布式鎖看了又看,優(yōu)秀方案我來告訴你

2021-04-12 23:32運維開發(fā)故事老鄭 編程技術

對于商品秒殺的場景,我們需要防止庫存超賣或者重復扣款等并發(fā)問題,我們通常需要使用分布式鎖,來解決共享資源競爭導致數(shù)據(jù)不一致的問題。本篇就講解如何用分布式鎖的來解決此類問題。

分布式鎖看了又看,優(yōu)秀方案我來告訴你

分布式鎖的場景

 

秒殺場景案例

對于商品秒殺的場景,我們需要防止庫存超賣或者重復扣款等并發(fā)問題,我們通常需要使用分布式鎖,來解決共享資源競爭導致數(shù)據(jù)不一致的問題。

以手機秒殺的場景為例子,在搶購的過程中通常我們有三個步驟:

扣掉對應商品的庫存;2. 創(chuàng)建商品的訂單;3. 用戶支付。

對于這樣的場景我們就可以采用分布式鎖的來解決,比如我們在用戶進入秒殺 “下單“ 鏈接的過程中,我們可以對商品庫存進行加鎖,然后完成扣庫存和其他操作,操作完成后。釋放鎖,讓下一個用戶繼續(xù)進入保證庫存的安全性;也可以減少因為秒殺失敗,導致 DB 回滾的次數(shù)。整個流程如下圖所示:

 分布式鎖看了又看,優(yōu)秀方案我來告訴你

注:對于鎖的粒度要根據(jù)具體的場景和需求來權衡。

三種分布式鎖

對于 Zookeeper 的分布式鎖實現(xiàn),主要是利用 Zookeeper 的兩個特征來實現(xiàn):

  1. Zookeeper 的一個節(jié)點不能被重復創(chuàng)建
  2. Zookeeper 的 Watcher 監(jiān)聽機制

非公平鎖

對于非公平鎖,我們在加鎖的過程如下圖所示。

分布式鎖看了又看,優(yōu)秀方案我來告訴你

優(yōu)點和缺點

其實上面的實現(xiàn)有優(yōu)點也有缺點:

優(yōu)點:

實現(xiàn)比較簡單,有通知機制,能提供較快的響應,有點類似 ReentrantLock 的思想,對于節(jié)點刪除失敗的場景由 Session 超時保證節(jié)點能夠刪除掉。

缺點:

重量級,同時在大量鎖的情況下會有 “驚群” 的問題。

“驚群” 就是在一個節(jié)點刪除的時候,大量對這個節(jié)點的刪除動作有訂閱 Watcher 的線程會進行回調(diào),這對Zk集群是十分不利的。所以需要避免這種現(xiàn)象的發(fā)生。

解決“驚群”:

為了解決“驚群“問題,我們需要放棄訂閱一個節(jié)點的策略,那么怎么做呢?

  1. 我們將鎖抽象成目錄,多個線程在此目錄下創(chuàng)建瞬時的順序節(jié)點,因為 Zookeeper 會為我們保證節(jié)點的順序性,所以可以利用節(jié)點的順序進行鎖的判斷。
  2. 首先創(chuàng)建順序節(jié)點,然后獲取當前目錄下最小的節(jié)點,判斷最小節(jié)點是不是當前節(jié)點,如果是那么獲取鎖成功,如果不是那么獲取鎖失敗。
  3. 獲取鎖失敗的節(jié)點獲取當前節(jié)點上一個順序節(jié)點,對此節(jié)點注冊監(jiān)聽,當節(jié)點刪除的時候通知當前節(jié)點。
  4. 當unlock的時候刪除節(jié)點之后會通知下一個節(jié)點。

公平鎖

基于非公平鎖的缺點,我們可以通過一下的方案來規(guī)避。

分布式鎖看了又看,優(yōu)秀方案我來告訴你

優(yōu)點和缺點

優(yōu)點: 如上借助于臨時順序節(jié)點,可以避免同時多個節(jié)點的并發(fā)競爭鎖,緩解了服務端壓力。

缺點: 對于讀寫場景來說,無法解決一致性的問題,如果讀的時候也去獲取鎖的話,這樣會導致性能下降,對于這樣的問題,我們可以通過讀寫鎖來實現(xiàn)如類似 jdk 中的 ReadWriteLock

讀寫鎖實現(xiàn)

對于讀寫鎖的特點:讀寫鎖在如果多個線程都是在讀的時候,是可以并發(fā)讀的,就是一個無鎖的狀態(tài),如果有寫鎖正在操作的時候,那么讀鎖需要等待寫鎖。在加寫鎖的時候,由于前面的讀鎖都是并發(fā),所以需要監(jiān)聽最后一個讀鎖完成后執(zhí)行寫鎖。步驟如下:

  1. read 請求, 如果前面是讀鎖,可以直接讀取,不需要監(jiān)聽。如果前面是一個或者多個寫鎖那么只需要監(jiān)聽最后一個寫鎖。
  2. write 請求,只需要對前面的節(jié)點監(jiān)聽。Watcher 機制和互斥鎖一樣。

分布式鎖看了又看,優(yōu)秀方案我來告訴你

分布式鎖實戰(zhàn)

本文源碼中使用環(huán)境:JDK 1.8 、Zookeeper 3.6.x

Curator 組件實現(xiàn)

 

POM 依賴

  1. <dependency> 
  2.   <groupId>org.apache.curator</groupId> 
  3.   <artifactId>curator-framework</artifactId> 
  4.   <version>2.13.0</version> 
  5. </dependency> 
  6. <dependency> 
  7.   <groupId>org.apache.curator</groupId> 
  8.   <artifactId>curator-recipes</artifactId> 
  9.   <version>2.13.0</version> 
  10. </dependency> 

互斥鎖運用

由于 Zookeeper 非公平鎖的 “驚群” 效應,非公平鎖在 Zookeeper 中其實并不是最好的選擇。下面是一個模擬秒殺的例子來使用 Zookeeper 分布式鎖。

  1. public class MutexTest { 
  2.     static ExecutorService executor = Executors.newFixedThreadPool(8); 
  3.     static AtomicInteger stock = new AtomicInteger(3); 
  4.     public static void main(String[] args) throws InterruptedException { 
  5.         CuratorFramework client = getZkClient(); 
  6.         String key = "/lock/lockId_111/111"
  7.         final InterProcessMutex mutex = new InterProcessMutex(client, key); 
  8.         for (int i = 0; i < 99; i++) { 
  9.             executor.submit(() -> { 
  10.                 if (stock.get() < 0) { 
  11.                     System.err.println("庫存不足, 直接返回"); 
  12.                     return
  13.                 } 
  14.                 try { 
  15.                     boolean acquire = mutex.acquire(200, TimeUnit.MILLISECONDS); 
  16.                     if (acquire) { 
  17.                         int s = stock.decrementAndGet(); 
  18.                         if (s < 0) { 
  19.                             System.err.println("進入秒殺,庫存不足"); 
  20.                         } else { 
  21.                             System.out.println("購買成功, 剩余庫存: " + s); 
  22.                         } 
  23.                     } 
  24.                 } catch (Exception e) { 
  25.                     e.printStackTrace(); 
  26.                 } finally { 
  27.                     try { 
  28.                         if (mutex.isAcquiredInThisProcess()) 
  29.                             mutex.release(); 
  30.                     } catch (Exception e) { 
  31.                         e.printStackTrace(); 
  32.                     } 
  33.                 } 
  34.             }); 
  35.         } 
  36.         while (true) { 
  37.             if (executor.isTerminated()) { 
  38.                 executor.shutdown(); 
  39.                 System.out.println("秒殺完畢剩余庫存為:" + stock.get()); 
  40.             } 
  41.             TimeUnit.MILLISECONDS.sleep(100); 
  42.         } 
  43.     } 
  44.     private static CuratorFramework getZkClient() { 
  45.         String zkServerAddress = "127.0.0.1:2181"
  46.         ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(1000, 3, 5000); 
  47.         CuratorFramework zkClient = CuratorFrameworkFactory.builder() 
  48.                 .connectString(zkServerAddress) 
  49.                 .sessionTimeoutMs(5000) 
  50.                 .connectionTimeoutMs(5000) 
  51.                 .retryPolicy(retryPolicy) 
  52.                 .build(); 
  53.         zkClient.start(); 
  54.         return zkClient; 
  55.     } 

讀寫鎖運用

讀寫鎖可以用來保證緩存雙寫的強一致性的,因為讀寫鎖在多線程讀的時候是無鎖的, 只有在前面有寫鎖的時候才會等待寫鎖完成后訪問數(shù)據(jù)。

  1. public class ReadWriteLockTest { 
  2.     static ExecutorService executor = Executors.newFixedThreadPool(8); 
  3.     static AtomicInteger stock = new AtomicInteger(3); 
  4.     static InterProcessMutex readLock; 
  5.     static InterProcessMutex writeLock; 
  6.     public static void main(String[] args) throws InterruptedException { 
  7.         CuratorFramework client = getZkClient(); 
  8.         String key = "/lock/lockId_111/1111"
  9.         InterProcessReadWriteLock readWriteLock = new InterProcessReadWriteLock(client, key); 
  10.         readLock = readWriteLock.readLock(); 
  11.         writeLock = readWriteLock.writeLock(); 
  12.         for (int i = 0; i < 16; i++) { 
  13.             executor.submit(() -> { 
  14.                 try { 
  15.                     boolean read = readLock.acquire(2000, TimeUnit.MILLISECONDS); 
  16.                     if (read) { 
  17.                         int num = stock.get(); 
  18.                         System.out.println("讀取庫存,當前庫存為: " + num); 
  19.                         if (num < 0) { 
  20.                             System.err.println("庫存不足, 直接返回"); 
  21.                             return
  22.                         } 
  23.                     } 
  24.                 } catch (Exception e) { 
  25.                     e.printStackTrace(); 
  26.                 }finally { 
  27.                     if (readLock.isAcquiredInThisProcess()) { 
  28.                         try { 
  29.                             readLock.release(); 
  30.                         } catch (Exception e) { 
  31.                             e.printStackTrace(); 
  32.                         } 
  33.                     } 
  34.                 } 
  35.                 try { 
  36.                     boolean acquire = writeLock.acquire(2000, TimeUnit.MILLISECONDS); 
  37.                     if (acquire) { 
  38.                         int s = stock.get(); 
  39.                         if (s <= 0) { 
  40.                             System.err.println("進入秒殺,庫存不足"); 
  41.                         } else { 
  42.                             s = stock.decrementAndGet(); 
  43.                             System.out.println("購買成功, 剩余庫存: " + s); 
  44.                         } 
  45.                     } 
  46.                 } catch (Exception e) { 
  47.                     e.printStackTrace(); 
  48.                 } finally { 
  49.                     try { 
  50.                         if (writeLock.isAcquiredInThisProcess()) 
  51.                             writeLock.release(); 
  52.                     } catch (Exception e) { 
  53.                         e.printStackTrace(); 
  54.                     } 
  55.                 } 
  56.             }); 
  57.         } 
  58.         while (true) { 
  59.             if (executor.isTerminated()) { 
  60.                 executor.shutdown(); 
  61.                 System.out.println("秒殺完畢剩余庫存為:" + stock.get()); 
  62.             } 
  63.             TimeUnit.MILLISECONDS.sleep(100); 
  64.         } 
  65.     } 
  66.     private static CuratorFramework getZkClient() { 
  67.         String zkServerAddress = "127.0.0.1:2181"
  68.         ExponentialBackoffRetry retryPolicy = new ExponentialBackoffRetry(1000, 3, 5000); 
  69.         CuratorFramework zkClient = CuratorFrameworkFactory.builder() 
  70.                 .connectString(zkServerAddress) 
  71.                 .sessionTimeoutMs(5000) 
  72.                 .connectionTimeoutMs(5000) 
  73.                 .retryPolicy(retryPolicy) 
  74.                 .build(); 
  75.         zkClient.start(); 
  76.         return zkClient; 
  77.     } 

打印結果如下,一開始會有 8 個輸出結果為 讀取庫存,當前庫存為: 3 然后在寫鎖中回去順序的扣減少庫存。

  1. 讀取庫存,當前庫存為: 3 
  2. 讀取庫存,當前庫存為: 3 
  3. 讀取庫存,當前庫存為: 3 
  4. 讀取庫存,當前庫存為: 3 
  5. 讀取庫存,當前庫存為: 3 
  6. 讀取庫存,當前庫存為: 3 
  7. 讀取庫存,當前庫存為: 3 
  8. 讀取庫存,當前庫存為: 3 
  9. 購買成功, 剩余庫存: 2 
  10. 購買成功, 剩余庫存: 1 
  11. 購買成功, 剩余庫存: 0 
  12. 進入秒殺,庫存不足 
  13. 進入秒殺,庫存不足 
  14. 進入秒殺,庫存不足 
  15. 進入秒殺,庫存不足 
  16. 進入秒殺,庫存不足 
  17. 讀取庫存,當前庫存為: 0 
  18. 讀取庫存,當前庫存為: 0 
  19. 讀取庫存,當前庫存為: 0 
  20. 讀取庫存,當前庫存為: 0 
  21. 讀取庫存,當前庫存為: 0 
  22. 讀取庫存,當前庫存為: 0 
  23. 讀取庫存,當前庫存為: 0 
  24. 讀取庫存,當前庫存為: 0 
  25. 進入秒殺,庫存不足 
  26. 進入秒殺,庫存不足 
  27. 進入秒殺,庫存不足 
  28. 進入秒殺,庫存不足 
  29. 進入秒殺,庫存不足 
  30. 進入秒殺,庫存不足 
  31. 進入秒殺,庫存不足 
  32. 進入秒殺,庫存不足 

分布式鎖的選擇

咱們最常用的就是 Redis 的分布式鎖和 Zookeeper 的分布式鎖,在性能方面 Redis 的每秒鐘 TPS 可以上輕松上萬。在大規(guī)模的高并發(fā)場景我推薦使用 Redis 分布式鎖來作為推薦的技術方案。如果對并發(fā)要求不是特別高的場景可以使用 Zookeeper 分布式來處理。

參考資料

https://www.cnblogs.com/leeego-123/p/12162220.html

http://curator.apache.org/

https://blog.csdn.net/hosaos/article/details/89521537

原文地址:https://mp.weixin.qq.com/s/k1HR_2rUWUe-ARN7KzhlYg

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 日韩在线免费 | 欧美在线播放 | 男人的天堂在线视频 | 精品久久国产字幕高潮 | 欧美一区二区三区在线 | av官网| 欧美麻豆视频 | 国产二区视频 | 亚洲综合在线视频 | 一区二区三区在线播放 | 亚洲高清一区二区三区 | 婷婷综合在线 | 免费一区在线观看 | 搞黄免费视频 | 色综合社区| 日韩高清在线 | 特级西西人体444www高清大胆 | 免费一级在线视频 | 国产真实精品久久二三区 | 在线观看三区 | 国产精品久久久久久久久 | 日韩av在线电影 | 国产欧美一区二区精品性色 | 夜夜操av | 青青草网站 | 久久久久久不卡 | 欧美日韩不卡在线 | 日本精品一区二区三区在线观看视频 | 色天天综合 | 国产免费一区二区三区最新6 | 成人二区 | 国产一级成人 | 亚洲一区二区中文字幕 | 中文字幕久久网 | 91精彩视频在线观看 | 亚洲精品一区二区网址 | 狠狠久 | 午夜免费| 黄色小网站在线观看 | 一级黄色免费网站 | 91免费在线视频 |