成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

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

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

[[392389]]

分布式鎖的場景

秒殺場景案例

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

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

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

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

 

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

三種分布式鎖

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

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

非公平鎖

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

優(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)點: 如上借助于臨時順序節(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 機制和互斥鎖一樣。

分布式鎖實戰(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.     } 

打印結(jié)果如下,一開始會有 8 個輸出結(jié)果為 讀取庫存,當前庫存為: 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 分布式鎖來作為推薦的技術(shù)方案。如果對并發(fā)要求不是特別高的場景可以使用 Zookeeper 分布式來處理。

參考資料

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

http://curator.apache.org/

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

 

責任編輯:姜華 來源: 運維開發(fā)故事
相關(guān)推薦

2022-02-10 08:57:45

分布式線程鎖

2024-04-26 08:06:58

分布式系統(tǒng)

2019-06-19 15:40:06

分布式鎖RedisJava

2023-03-01 08:07:51

2024-01-09 08:20:05

2023-04-03 10:00:00

Redis分布式

2018-07-16 09:00:06

Ceph運維開源

2022-05-18 10:38:51

Redis分布式鎖數(shù)據(jù)

2018-06-28 08:18:56

Ceph運維存儲

2019-07-18 09:17:19

Kafka消息隊列服務器

2021-10-09 11:34:59

MySQL分布式鎖庫存

2018-07-17 08:14:22

分布式分布式鎖方位

2022-08-04 08:45:50

Redisson分布式鎖工具

2019-02-26 09:51:52

分布式鎖RedisZookeeper

2021-07-16 07:57:34

ZooKeeperCurator源碼

2018-11-27 16:17:13

分布式Tomcat

2021-11-26 06:43:19

Java分布式

2020-04-23 11:18:14

Redis分布式

2023-06-14 17:56:54

2022-07-06 08:01:05

數(shù)據(jù)庫分布式
點贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 特级丰满少妇一级aaaa爱毛片 | 在线成人免费视频 | 国产精品日本一区二区在线播放 | 久草.com | 精品中文字幕在线观看 | 日本一区二区电影 | 日韩在线中文 | 国产精品久久久久久亚洲调教 | 日本不卡一区二区三区在线观看 | 久久国产精品99久久久大便 | 亚洲成人国产综合 | 日韩在线欧美 | 久久精品影视 | 全部免费毛片在线播放网站 | 日韩精品一区二区三区 | av天天干| 国产91综合一区在线观看 | 亚洲人成人一区二区在线观看 | 久久久精品网站 | 成年人网站国产 | 久久国产视频网站 | 欧美精品一区二区三区四区 在线 | 亚洲一区二区三区久久久 | 99国产精品久久久 | 久久久av | 精品免费国产一区二区三区四区 | 91精品国产乱码麻豆白嫩 | 亚洲天堂av在线 | 国产精品久久久久影院色老大 | 黄a在线观看 | 中日韩欧美一级片 | 欧美精品在线一区 | 91天堂网 | 日韩久久久久 | 91高清在线 | 亚洲国产精品一区二区第一页 | 国产在线精品一区二区 | 亚洲精品久久区二区三区蜜桃臀 | 免费一区 | 久久精品亚洲一区二区三区浴池 | 欧美日韩视频在线第一区 |