<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                [TOC] ## 業務場景 有一類**寫多讀少**的業務場景:大部分請求是對數據進行修改,少部分請求對數據進行讀取。 如:滴滴,出租車司機的定位(寫多),查詢(讀少) ## 普通方案 具體到底層的實現,往往是一個Map(本質是一個定長key,定長value的緩存結構)來存儲司機的信息,或者某個類型的計數。 `Map<driver_id, DriverInfo>` 作為臨界資源,在讀寫之前,一般要進行加鎖操作 ``` void SetDriverInfo(long driver_id, DriverInfoinfo){ WriteLock (m_lock); Map<driver_id>= info; UnWriteLock(m_lock); } DriverInfo GetDriverInfo(long driver_id){ DriverInfo t; ReadLock(m_lock); t= Map<driver_id>; UnReadLock(m_lock); return t; } ``` 在并發量很大的時候(每秒20w寫,1k讀),鎖m_lock會成為潛在瓶頸 ## 水平切分+鎖粒度優化 上文中之所以鎖沖突嚴重都公用一把鎖,鎖的粒度太粗(“庫級別鎖”)把1個Map水平切分成多個Map,可降低并發 ``` void SetDriverInfo(long driver_id, DriverInfoinfo){ i= driver_id % N; // 水平拆分成N份,N個Map,N個鎖 WriteLock (m_lock [i]); //鎖第i把鎖 Map[i]<driver_id>= info; // 操作第i個Map UnWriteLock (m_lock[i]); // 解鎖第i把鎖 } ``` 每個Map的并發量(變成了1/N)和數據量都降低(變成了1/N)了,所以理論上,鎖沖突會成平方指數降低,分庫之后,仍然是庫鎖 ## MAP變Array+最細鎖粒度優化 假設driver_id是遞增生成的,并且緩存的內存比較大,是可以把Map優化成Array,而不是拆分成N個Map,是有可能把鎖的粒度細化到最細的(每個記錄一個鎖)。 ``` void SetDriverInfo(long driver_id, DriverInfoinfo){ index= driver_id; WriteLock (m_lock [index]); //超級大內存,一條記錄一個鎖,鎖行鎖 Array[index]= info; //driver_id就是Array下標 UnWriteLock (m_lock[index]); // 解鎖行鎖 } ``` 鎖沖突降到了最低,但鎖資源大增 ## 變成無鎖緩存 ``` void AddCountByType(long type /*, int count*/){ //不加鎖 Array[type]++; // 計數++ //Array[type] += count; // 計數增加count } ``` 可以達到最高的并發,但是多線程對緩存中同一塊定長數據進行操作時,有可能出現不一致的數據塊,這個方案為了提高性能,犧牲了一致性。在讀取計數時,獲取到了錯誤的數據,是不能接受的(作為緩存,允許cache miss,卻不允許讀臟數據) ### 臟數據是如何產生的 ![UTOOLS1577611201657.png](http://yanxuan.nosdn.127.net/5afa2d26635801ab0313b746f21116c0.png) 可能每個線程寫成功一半,導致出現臟數據產生,最終的結果即不是value1也不 是value2,而是一個亂七八糟的不符合預期的值value-unexpected 即時通訊系統中,通過驗證簽名,保證接受方收到的消息,就是發送方發送的消息. ![UTOOLS1577611404281.png](http://yanxuan.nosdn.127.net/58465675b0895f07e77b159331f3945f.png) 加上簽名之后,不但緩存要寫入定長value本身,還要寫入**定長簽名**(例如16bitCRC校驗): 1. 線程1對緩存進行操作,對key想要寫入value1,寫入簽名v1-sign 2. 線程2對緩存進行操作,對key想要寫入value2,寫入簽名v2-sign 3. 如果不加鎖,線程1和線程2對同一個定長區域進行一個并發的寫操作,可能每個線程寫成功一半,導致出現臟數據產生,最終的結果即不是value1也不是value2,而是一個亂七八糟的不符合預期的值value-unexpected,但簽名,一定是v1-sign或者v2-sign中的任意一個 4. 數據讀取的時候,不但要取出value,還要像消息接收方收到消息一樣,校驗一下簽名,如果發現簽名不一致,緩存則返回NULL,即cache miss。 對應到司機地理位置,與URL訪問計數的case,除了內存緩存之前,肯定需要timer對緩存中的數據定期落盤,寫入數據庫,如果cache miss,可以從**數據庫中讀取數據**
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看