<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    paulwong

    高并發(fā)情況下怎樣盡量實(shí)現(xiàn)無鎖編程

      一個在線2k的游戲,每秒鐘并發(fā)都嚇?biāo)廊恕鹘y(tǒng)的hibernate直接插庫基本上是不可行的。我就一步步推導(dǎo)出一個無鎖的數(shù)據(jù)庫操作。

      

      1. 并發(fā)中如何無鎖。

      一個很簡單的思路,把并發(fā)轉(zhuǎn)化成為單線程。Java的Disruptor就是一個很好的例子。如果用java的concurrentCollection類去做,原理就是啟動一個線程,跑一個Queue,并發(fā)的時候,任務(wù)壓入Queue,線程輪訓(xùn)讀取這個Queue,然后一個個順序執(zhí)行。

      在這個設(shè)計模式下,任何并發(fā)都會變成了單線程操作,而且速度非常快。現(xiàn)在的node.js, 或者比較普通的ARPG服務(wù)端都是這個設(shè)計,“大循環(huán)”架構(gòu)。

      這樣,我們原來的系統(tǒng)就有了2個環(huán)境:并發(fā)環(huán)境 + ”大循環(huán)“環(huán)境

      并發(fā)環(huán)境就是我們傳統(tǒng)的有鎖環(huán)境,性能低下。

      ”大循環(huán)“環(huán)境是我們使用Disruptor開辟出來的單線程無鎖環(huán)境,性能強(qiáng)大。

      

      2. ”大循環(huán)“環(huán)境 中如何提升處理性能。

      一旦并發(fā)轉(zhuǎn)成單線程,那么其中一個線程一旦出現(xiàn)性能問題,必然整個處理都會放慢。所以在單線程中的任何操作絕對不能涉及到IO處理。那數(shù)據(jù)庫操作怎么辦?

      增加緩存。這個思路很簡單,直接從內(nèi)存讀取,必然會快。至于寫、更新操作,采用類似的思路,把操作提交給一個Queue,然后單獨(dú)跑一個Thread去一個個獲取插庫。這樣保證了“大循環(huán)”中不涉及到IO操作。

      

      問題再次出現(xiàn):

      如果我們的游戲只有個大循環(huán)還容易解決,因?yàn)槔锩嫣峁┝送昝赖耐綗o鎖。

      但是實(shí)際上的游戲環(huán)境是并發(fā)和“大循環(huán)”并存的,即上文的2種環(huán)境。那么無論我們怎么設(shè)計,必然會發(fā)現(xiàn)在緩存這塊上要出現(xiàn)鎖。

      

      3. 并發(fā)與“大循環(huán)”如何共處,消除鎖?

      我們知道如果在“大循環(huán)”中要避免鎖操作,那么就用“異步”,把操作交給線程處理。結(jié)合這2個特點(diǎn),我稍微改下數(shù)據(jù)庫架構(gòu)。

      原本的緩存層,必然會存在著鎖,例如:

    public TableCache
    {
      private HashMap<String, Object> caches = new ConcurrentHashMap<String, Object>();
    }

      這個結(jié)構(gòu)是必然的了,保證了在并發(fā)的環(huán)境下能夠準(zhǔn)確的操作緩存。但是”大循環(huán)“卻不能直接操作這個緩存進(jìn)行修改,所以必須啟動一個線程去更新緩存,例如:

      private static final ExecutorService EXECUTOR = Executors.newSingleThreadExecutor();

      EXECUTOR.execute(new LatencyProcessor(logs));

      class LatencyProcessor implements Runnable
      {
          public void run()
         {
           // 這里可以任意的去修改內(nèi)存數(shù)據(jù)。采用了異步。
        }
      }

      OK,看起來很漂亮。但是又有個問題出現(xiàn)了。在高速存取的過程中,非常有可能緩存還沒有被更新,就被其他請求再次獲取,得到了舊的數(shù)據(jù)。

      

      4. 如何保證并發(fā)環(huán)境下緩存數(shù)據(jù)的唯一正確?

      我們知道,如果只有讀操作,沒有寫操作,那么這個行為是不需要加鎖的。

      我使用這個技巧,在緩存的上層,再加一層緩存,成為”一級緩存“,原來的就自然成為”二級緩存“。有點(diǎn)像CPU了對不?

      一級緩存只能被”大循環(huán)“修改,但是可以被并發(fā)、”大循環(huán)“同時獲取,所以是不需要鎖的。

      當(dāng)發(fā)生數(shù)據(jù)庫變動,分2種情況:

      1)并發(fā)環(huán)境下的數(shù)據(jù)庫變動,我們是允許有鎖的存在,所以直接操作二級緩存,沒有問題。

      2)”大循環(huán)“環(huán)境下數(shù)據(jù)庫變動,首先我們把變動數(shù)據(jù)存儲在一級緩存,然后交給異步修正二級緩存,修正后刪除一級緩存。

      這樣,無論在哪個環(huán)境下讀取數(shù)據(jù),首先判斷一級緩存,沒有再判斷二級緩存。

      這個架構(gòu)就保證了內(nèi)存數(shù)據(jù)的絕對準(zhǔn)確。

      而且重要的是:我們有了一個高效的無鎖空間,去實(shí)現(xiàn)我們?nèi)我獾臉I(yè)務(wù)邏輯。

      

      最后,還有一些小技巧提升性能。

      1. 既然我們的數(shù)據(jù)庫操作已經(jīng)被異步處理,那么某個時間,需要插庫的數(shù)據(jù)可能很多,通過對表、主鍵、操作類型的排序,我們可以刪除一些無效操作。例如:

      a)同一個表同一個主鍵的多次UPdate,取最后一次。

      b)同一個表同一個主鍵,只要出現(xiàn)Delete,前面所有操作無效。

      2. 既然我們要對操作排序,必然會存在一個根據(jù)時間排序,如何保證無鎖呢?使用

         private final static AtomicLong _seq = new AtomicLong(0);

      即可保證無鎖又全局唯一自增,作為時間序列。

    posted on 2015-07-13 18:20 paulwong 閱讀(777) 評論(0)  編輯  收藏 所屬分類: 性能優(yōu)化

    主站蜘蛛池模板: ZZIJZZIJ亚洲日本少妇JIZJIZ | 羞羞的视频在线免费观看| 久久亚洲精品无码av| 成人午夜免费视频| 中文字幕无码日韩专区免费 | a一级爱做片免费| 日韩精品在线免费观看| 国产1024精品视频专区免费| 日本黄色免费观看| 亚洲一区AV无码少妇电影☆| 久久精品国产亚洲AV香蕉| 亚洲人成网亚洲欧洲无码| 岛国精品一区免费视频在线观看| 永久免费视频网站在线观看| 全免费a级毛片免费看不卡| 亚洲男女内射在线播放| 亚洲黄色免费电影| 豆国产96在线|亚洲| 国产成人精品一区二区三区免费| 免费三级毛片电影片| 亚洲第一福利网站在线观看| 久久精品亚洲综合| 亚洲欧美国产日韩av野草社区| 久青草视频97国内免费影视| 在线视频精品免费| 亚洲国产综合人成综合网站| 亚洲精品中文字幕乱码影院| 黄色a三级免费看| 国产92成人精品视频免费| 亚洲а∨天堂久久精品| 久久av无码专区亚洲av桃花岛| 在线观看免费亚洲| 国产精品白浆在线观看免费| 日韩一级视频免费观看| 亚洲AV日韩AV高潮无码专区| 色欲aⅴ亚洲情无码AV蜜桃| 永久免费av无码网站yy| 在线观看国产情趣免费视频| 亚洲av女电影网| 一级毛片一级毛片免费毛片| 国产免费不卡v片在线观看|