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

無鎖緩存,每秒10萬并發(fā),究竟如何實現(xiàn)?

開發(fā) 開發(fā)工具 前端
有一類業(yè)務場景:超高吞吐量,每秒要處理海量請求;寫多讀少,大部分請求是對數(shù)據(jù)進行修改,少部分請求對數(shù)據(jù)進行讀取,這類業(yè)務,有什么實現(xiàn)技巧么?

有一類業(yè)務場景:

  • 超高吞吐量,每秒要處理海量請求;
  • 寫多讀少,大部分請求是對數(shù)據(jù)進行修改,少部分請求對數(shù)據(jù)進行讀取;

這類業(yè)務,有什么實現(xiàn)技巧么?

接下來,一起聽我從案例入手,娓娓道來。

[[358930]]

快狗打車,場景舉例:

  • 司機地理位置信息會隨時變化,可能每幾秒鐘地理位置要修改一次;
  • 用戶打車的時候查看某個司機的地理位置,查詢地理位置的頻率相對較低;

這里要用到兩個接口:

  • 大量修改司機信息:
    1. void SetDriverInfo(long driver_id, DriverInfo info); 
  • 相對少量查詢司機信息:
    1. DriverInfo GetDriverInfo(long driver_id);  

這一類業(yè)務,一般怎么實現(xiàn)呢?

具體到底層的實現(xiàn),往往是一個Map內(nèi)存緩存:

  • 查詢key定長,例如:司機ID;
  • 返回value也定長,例如:司機實體序列化后的二進制串;

即,類似這樣的一個kv緩存結(jié)構(gòu):

  1. Map<driver_id, DriverInfo> 

這個kv內(nèi)存緩存是一個臨界資源,對它的并發(fā)訪問,有什么注意事項么?臨界資源的訪問,需要注意加讀寫鎖,實施互斥。

以下,是加鎖寫入的偽代碼:

  1. void SetDriverInfo(long driver_id, DriverInfo info){ 
  2.          WriteLock (m_lock); 
  3.          Map<driver_id>= info; 
  4.          UnWriteLock(m_lock); 

畫外音:假設info已經(jīng)序列化。

以下,是加鎖讀取的偽代碼:

  1. DriverInfo GetDriverInfo(long driver_id){ 
  2.          DriverInfo t; 
  3.          ReadLock(m_lock); 
  4.          tMap<driver_id>
  5.          UnReadLock(m_lock); 
  6.          return t; 

當吞吐量很高時,上述流程可能存在什么問題?

  • 假設快狗打車有100w司機同時在線,每個司機每5秒更新一次經(jīng)緯度狀態(tài),那么每秒就有20w次寫并發(fā)操作。
  • 假設快狗打車日訂單1000w個,平均每秒大概也有300個下單,對應到查詢并發(fā)量,大概每秒1000級別的并發(fā)讀操作。

在這樣的吞吐量下(每秒20w寫,1k讀),鎖m_lock會成為潛在瓶頸,導致Map訪問效率極低。

有什么潛在的優(yōu)化方法么?

鎖沖突之所以嚴重,是因為整個Map共用一把鎖,鎖的粒度太粗。

畫外音:可以認為是一個數(shù)據(jù)庫的“庫級別鎖”。

是否可能進行水平拆分,來降低鎖沖突呢?

答案是肯定的。

畫外音:類似于數(shù)據(jù)庫里的分庫,把一個庫鎖變成多個庫鎖,來提高并發(fā),降低鎖沖突。

我們可以把1個Map水平切分成N個Map:

  1. void SetDriverInfo(long driver_id, DriverInfo info){ 
  2.          i = driver_id % N; // 水平拆分成N份,N個Map,N個鎖 
  3.          WriteLock (m_lock[i]);  //鎖第i把鎖 
  4.          Map[i]<driver_id>= info;  // 操作第i個Map 
  5.          UnWriteLock (m_lock[i]); // 解鎖第i把鎖 

如此優(yōu)化,能否提高性能?

  • 一個Map變成了N個Map,每個Map的并發(fā)量,變成了1/N;
  • 同時,每個Map的數(shù)據(jù)量,變成了1/N;

所以理論上,鎖沖突會成平方指數(shù)降低,性能會提升。

有沒有可能,進一步細化鎖粒度,一個元素一把鎖呢?

答案也是肯定的。

畫外音:可以認為是一個數(shù)據(jù)庫的“庫級別鎖”,優(yōu)化為“行級別鎖”。

不妨設driver_id是遞增生成的,并且假設內(nèi)存比較大,此時可以把Map優(yōu)化成Array,并把鎖的粒度細化到最細的,每個司機信息一個鎖:

  1. void SetDriverInfo(long driver_id, DriverInfo info){ 
  2.          index = driver_id
  3.          WriteLock (m_lock[index]);  //超級大內(nèi)存,一條記錄一個鎖,鎖行鎖 
  4.          Array[index]= info; //driver_id就是Array下標 
  5.          UnWriteLock (m_lock[index]); // 解鎖行鎖 

這個方案使得鎖沖突降到了最低,但鎖資源大增,在數(shù)據(jù)量非常大的情況下,內(nèi)存往往是裝不下的。畫外音:數(shù)據(jù)量比較小的時候,可以一個元素一把鎖,典型的是連接池,每個連接用一把鎖表示連接是否可用。

還沒有方法進一步降低鎖沖突,提升并發(fā)量呢?

寫多讀少的業(yè)務,有一種優(yōu)化方案:無鎖緩存,將鎖沖突降低到。

無鎖緩存,可能存在什么問題?

如果緩存不加鎖,讀寫吞吐量可以達到極限,但是多線程對緩存中同一塊定長數(shù)據(jù)進行寫操作時,有可能出現(xiàn)不一致的臟數(shù)據(jù)。

這個方案為了提高性能,犧牲了一致性。

讀取時,獲取到了錯誤的數(shù)據(jù),是不能接受的。

畫外音:作為緩存,允許cache miss,卻不允許讀臟數(shù)據(jù)。

臟數(shù)據(jù)是如何產(chǎn)生的?

不加鎖,在多線程并發(fā)寫時,可能出現(xiàn)以下情況:

  • 線程1對緩存進行操作,對key想要寫入value1;
  • 線程2對緩存進行操作,對key想要寫入value2;
  • 不加鎖,線程1和線程2對同一個定長區(qū)域進行一個并發(fā)的寫操作,可能每個線程寫成功一半,導致出現(xiàn)臟數(shù)據(jù)產(chǎn)生,最終的結(jié)果即不是value1也不是value2,而是一個亂七八糟的不符合預期的值value-unexpected;

如何解決上述問題呢?

本質(zhì)上,這是一個數(shù)據(jù)完整性問題。

并發(fā)寫入的數(shù)據(jù)分別是value1和value2,讀出的數(shù)據(jù)是value-unexpected,數(shù)據(jù)被篡改,這本質(zhì)上是一個數(shù)據(jù)完整性的問題。

通常如何保證數(shù)據(jù)的完整性呢?

例如:運維如何保證,從中控機分發(fā)到上線機上的二進制沒有被篡改?md5。

又例如:即時通訊系統(tǒng)中,如何保證接受方收到的消息,就是發(fā)送方發(fā)送的消息?發(fā)送方除了發(fā)送消息本身,還要發(fā)送消息的簽名,接收方收到消息后要校驗簽名,以確保消息是完整的,未被篡改。

“簽名”是一種常見的保證數(shù)據(jù)完整性的方案。

加入“簽名”保證數(shù)據(jù)的完整性之后,讀寫流程需要如何升級?

加上簽名之后,不但緩存要寫入定長value本身,還要寫入定長簽名(例如16bitCRC校驗):

(1)線程1對緩存進行操作,對key想要寫入value1,寫入簽名v1-sign;

(2)線程2對緩存進行操作,對key想要寫入value2,寫入簽名v2-sign;

(3)如果不加鎖,線程1和線程2對同一個定長區(qū)域進行一個并發(fā)的寫操作,可能每個線程寫成功一半,導致出現(xiàn)臟數(shù)據(jù)產(chǎn)生,最終的結(jié)果即不是value1也不是value2,而是一個亂七八糟的不符合預期的值value-unexpected,但簽名,一定是v1-sign或者v2-sign中的任意一個;

畫外音:16bit/32bit的寫可以保證原子性。

(4)數(shù)據(jù)讀取的時候,不但要取出value,還要像消息接收方收到消息一樣,校驗一下簽名,如果發(fā)現(xiàn)簽名不一致,緩存則返回NULL,即cache miss;

當然,對應到司機地理位置,除了內(nèi)存緩存之前,肯定需要timer對緩存中的數(shù)據(jù)定期落盤,寫入數(shù)據(jù)庫,如果cache miss,可以從數(shù)據(jù)庫中讀取數(shù)據(jù)。

巧不巧秒?

總結(jié)

當業(yè)務滿足:

  • 超高并發(fā);
  • 寫多讀少;
  • 定長value;

時,可以用以下方法來提升吞吐量:

(1)水平拆分來降低鎖沖突;

思路:單庫變多庫。

(2)Map轉(zhuǎn)Array的方式來最小化鎖沖突,一條記錄一個鎖;

思路:庫鎖變行鎖。

(3)無鎖,最大化并發(fā);

思路:行鎖變無鎖,完整性與性能的折衷。

(4)通過簽名的方式保證數(shù)據(jù)的完整性,實現(xiàn)無鎖緩存;

思路:寫時寫簽名,讀時校驗簽名。

【本文為51CTO專欄作者“58沈劍”原創(chuàng)稿件,轉(zhuǎn)載請聯(lián)系原作者】

戳這里,看該作者更多好文

 

責任編輯:趙寧寧 來源: 51CTO專欄
相關(guān)推薦

2019-08-14 15:08:51

緩存存儲數(shù)據(jù)

2019-11-11 15:33:34

高并發(fā)緩存數(shù)據(jù)

2025-04-22 08:55:31

2024-07-30 11:40:00

數(shù)據(jù)庫NoSQLSQL

2024-09-12 15:28:38

localhost?網(wǎng)絡IPv4

2017-04-26 15:07:56

騰訊云

2020-10-18 07:25:55

MQ消息冪等架構(gòu)

2022-11-26 00:00:07

內(nèi)存數(shù)組程序

2025-05-12 04:20:00

Linux系統(tǒng)epoll

2024-09-03 14:16:54

2019-05-05 09:28:59

架構(gòu)數(shù)據(jù)查詢

2019-07-29 14:40:26

架構(gòu)存儲檢索

2016-01-28 09:51:55

2015-07-09 10:44:53

微服務分布式DevOps

2018-02-01 09:32:16

傳統(tǒng)運維SRE

2011-05-07 15:13:24

兼容墨盒評測

2012-10-22 13:18:05

KVM

2019-03-06 09:36:12

Kafka緩存磁盤

2011-08-04 10:33:39

筆記本用戶體驗

2010-06-30 09:09:15

預覽版SQL Serv
點贊
收藏

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

主站蜘蛛池模板: 欧美成人二区 | 日韩在线免费 | 国产一区二区三区色淫影院 | 日韩欧美国产综合 | 日本超碰| 免费的av | 精品国产乱码久久久久久蜜退臀 | 色视频网站在线观看 | 美女久久 | 精品99爱视频在线观看 | 国产精品久久久久久久久久免费看 | 国产一区二区三区 | 亚洲综合热| 国产精品久久久久久久久久久久 | 国产一区二区三区色淫影院 | 精品区 | 久久精品国产一区二区三区不卡 | 国产成人亚洲精品 | 久久精品国产免费一区二区三区 | 亚洲一区日韩 | 毛片1 | 狠狠爱免费视频 | 亚洲在线 | 玖草资源 | 99re6在线视频精品免费 | 麻豆av网| 欧美在线二区 | 米奇7777狠狠狠狠视频 | 色视频一区二区 | 久草在线在线精品观看 | 欧美精品a∨在线观看不卡 国产精品久久国产精品 | 成人免费av在线 | 日本免费视频在线观看 | 久久99这里只有精品 | 免费一区二区三区 | 精品免费在线 | 亚洲毛片在线 | 麻豆91av| 搞av.com | 久久亚洲精品久久国产一区二区 | 精品一二区 |