• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            那誰的技術博客

            感興趣領域:高性能服務器編程,存儲,算法,Linux內核
            隨筆 - 210, 文章 - 0, 評論 - 1183, 引用 - 0
            數(shù)據(jù)加載中……

            對一個服務器的幾步優(yōu)化

            最近寫了一個服務器,業(yè)務邏輯很簡單,每個協(xié)議包往服務器上報數(shù)據(jù), 每個數(shù)據(jù)包中可能有N塊數(shù)據(jù)需要保存在數(shù)據(jù)庫中的.顯然, 這個業(yè)務邏輯是不能使用類似memcached這樣的緩存的, 因為每條數(shù)據(jù)都是相對獨立的, 而且必須保證每個數(shù)據(jù)都保存到數(shù)據(jù)庫中.這里拋開服務器最基本的那些IO模型之類的不說,談談對這個服務器的幾個優(yōu)化步驟.

            1) 最簡單的處理
            最簡單的處理就是按部就班的,每條數(shù)據(jù)老老實實的插入到數(shù)據(jù)庫中.顯然, 這樣做的效率是低的, 如果并發(fā)量大的時候,mysql負載變大,而服務器阻塞在數(shù)據(jù)庫操作上, 導致處理連接比較慢.

            2) 第一步優(yōu)化:定量插入數(shù)據(jù)
            這里基本的模型不變, 只是不是每個數(shù)據(jù)一來就插入, 而是緩存起來, 等到積累到了一定量才開始一起插入到數(shù)據(jù)庫中.這種方式比第一種方式并不能獲得太大的提高.

            3) 第二步優(yōu)化:開線程處理往數(shù)據(jù)庫中插入數(shù)據(jù)
            在這里, 多開一個線程用于往數(shù)據(jù)庫中插入數(shù)據(jù), 同時開辟了一個緩沖區(qū), 主線程不停的往這個緩沖區(qū)中倒數(shù)據(jù), 副線程負責將緩沖區(qū)中的數(shù)據(jù)導入數(shù)據(jù)庫.用偽代碼來表示, 這個優(yōu)化就是這樣的:
            主線程:
                往緩沖區(qū)A中添加數(shù)據(jù)(注意這里不需要加鎖)

            副線程
            加鎖
                將緩沖區(qū)A,B的指針互換, 這樣緩沖區(qū)B就指向了原來緩沖區(qū)A,A指向B緩沖區(qū)
            解鎖
            將緩沖區(qū)B的數(shù)據(jù)導入數(shù)據(jù)庫
            清空B緩沖區(qū)
            這樣的優(yōu)化效率獲得了極大的提高, 主線程只需要負責與客戶端之間的通信, 接收客戶端的數(shù)據(jù), 而副線程只需要將數(shù)據(jù)導入緩沖區(qū), 不需要關心通信問題.這樣,主線程就不會由于插入數(shù)據(jù)緩慢而導致接收數(shù)據(jù)和新的連接緩慢了.而由于是多線程, 主副線程之間的數(shù)據(jù)共享很容易做到, 主線程往緩沖區(qū)插入數(shù)據(jù)的時候甚至不需要加鎖, 而副線程倒數(shù)據(jù)的時候只需要加鎖然后把兩個緩沖區(qū)的指針互調就行了(我懷疑這個加鎖也是可以免去的).各司其職,又各不騷擾,簡單而高效.

            4)第三步優(yōu)化:將向數(shù)據(jù)庫導入數(shù)據(jù)的時間盡可能的平均分布
            這一步與上一步大體相同, 只是在副線程倒數(shù)據(jù)的時候定一個量, 當計數(shù)器達到這個量時, 副線程休眠一陣(比如sleep一秒).這樣的好處是可以將向數(shù)據(jù)庫導入數(shù)據(jù)的時間盡可能的平均分布, 減小峰值時間點數(shù)據(jù)庫的壓力.

            5)第四步優(yōu)化:從數(shù)據(jù)庫角度進行優(yōu)化
            因為服務器只需要往數(shù)據(jù)庫中插入數(shù)據(jù), 可以考慮在插入的時候將同一個表的數(shù)據(jù)緩存在一起, 然后寫在一條sql語句中一起插入, 這樣減少了sql語句的調用, IO減少了, 效率也提高了.我使用的是mysql, 關于mysql的優(yōu)化插入數(shù)據(jù), 可以參考這里.

            經過這幾個優(yōu)化步驟之后, 服務器的效率比之最開始有了極大的提高, 不僅是服務器的效率提高了, 整個系統(tǒng)的IO,數(shù)據(jù)庫壓力也減少了.


            以下是分割線:
            -------------------------------------------------------------------
            我仔細想了一下,覺得似乎真的可以去掉加鎖的步驟...

            我的緩沖區(qū)是一個STL的list鏈表,主線程不停的往list中push_back.

            而交換緩沖區(qū)的時候調用的是STL中的swap操作, 也就是交換兩個鏈表的頭結點而已.

            從這里來看,確實可以省去加鎖的步驟的.

            --------------------------------------------------------------------
            第二天,我決定還是加鎖了.由于臨界區(qū)內的操作并不多, 我想效率沒有太大的影響.

            posted on 2008-10-22 20:10 那誰 閱讀(5250) 評論(18)  編輯 收藏 引用 所屬分類: 網(wǎng)絡編程服務器設計 、Linux/Unix

            評論

            # re: 對一個服務器的幾步優(yōu)化  回復  更多評論   

            樓主要要是有例子代碼就好多了,希望能夠貼出代碼,那樣比較的直觀
            2008-10-22 20:17 | QQQ

            # re: 對一個服務器的幾步優(yōu)化[未登錄]  回復  更多評論   

            這樣的想法記錄也已經很棒了~代碼未必能清楚的表示作者的意圖。
            2008-10-22 21:03 | Xw.Y

            # re: 對一個服務器的幾步優(yōu)化  回復  更多評論   

            如果插入的數(shù)據(jù)是即時變化的,并要被其他地方(或不同客戶端、不同的進程、不同的線程...)使用的話,實時性就有問題。并且如果有一個計算是相關不同的記錄進行計算的話,計算的結果就可能會錯誤。
            2008-10-22 21:29 | wind

            # re: 對一個服務器的幾步優(yōu)化  回復  更多評論   

            還有做好數(shù)據(jù)的壓縮和緩存問題,有的服務器把緩存量設的太大了。
            2008-10-22 21:34 | 金山毒霸2008

            # re: 對一個服務器的幾步優(yōu)化  回復  更多評論   

            加鎖可以免去,因為Exchange有原子操作。
            2008-10-22 22:06 | 空明流轉

            # re: 對一個服務器的幾步優(yōu)化  回復  更多評論   

            @QQQ
            要代碼有什么用,代碼往往是最沒用的。。。
            2008-10-22 22:06 | 空明流轉

            # re: 對一個服務器的幾步優(yōu)化  回復  更多評論   

            不過我還是有個問題。如果主線程正在填充某條記錄的一半的時候就被Exchange掉怎么辦。
            2008-10-22 22:09 | 空明流轉

            # re: 對一個服務器的幾步優(yōu)化  回復  更多評論   

            為啥要用A、B兩個緩沖區(qū)呢?
            可以用一個循環(huán)數(shù)組data[N]來做緩存,in_index用于主線程寫數(shù)據(jù),指向下一個空位, out_index用于副線程讀數(shù)據(jù),指向下一個數(shù)據(jù)位。in_index和out_index都模N運算。
            當out_index = (in_index + 1)%N時,緩沖已慢,等待。
            ---------
            2008-10-22 22:15 | ronliu

            # re: 對一個服務器的幾步優(yōu)化  回復  更多評論   

            只對一個加鎖?跟沒加有區(qū)別么?
            2008-10-22 22:16 | t

            # re: 對一個服務器的幾步優(yōu)化[未登錄]  回復  更多評論   

            @ronliu
            這個方法不錯,我回頭試試.
            2008-10-22 22:29 | 創(chuàng)

            # re: 對一個服務器的幾步優(yōu)化[未登錄]  回復  更多評論   


            @t
            是的,所以我才有前面的疑問.
            2008-10-22 22:30 | 創(chuàng)

            # re: 對一個服務器的幾步優(yōu)化[未登錄]  回復  更多評論   

            我仔細想了一下,覺得似乎真的可以去掉加鎖的步驟...

            我的緩沖區(qū)是一個list鏈表,主線程不停的往list中push_back.

            而交換緩沖區(qū)的時候調用的是STL中的swap操作, 也就是交換兩個鏈表的頭結點而已.

            從這里來看,確實可以省去加鎖的步驟的.
            2008-10-22 22:36 | 創(chuàng)

            # re: 對一個服務器的幾步優(yōu)化  回復  更多評論   

            哦如果是這樣的話,你可以使用Windows的LIST,提供了添加和刪除節(jié)點的原子操作
            2008-10-22 23:35 | 空明流轉

            # re: 對一個服務器的幾步優(yōu)化  回復  更多評論   

            摟主覺得可以“不加鎖”的想法值得商榷

            首先,STL 中l(wèi)ist swap操作的實現(xiàn)是兩步:(以VC6 STL實現(xiàn)為例)
            std::swap(_Head, _X._Head);
            std::swap(_Size, _X._Size);
            此處不能保證兩步是原子操作。

            其次,也與std::swap的實現(xiàn)有關:(以VC6 STL實現(xiàn)為例)
            template<class _Ty> inline
            void swap(_Ty& _X, _Ty& _Y)
            {_Ty _Tmp = _X;
            _X = _Y, _Y = _Tmp; }
            不能保證編譯器能把這三步操作優(yōu)化成原子操作。

            再次,與容器中的對象類型有關,現(xiàn)有的Exchange原子操作只針對int有效,浮點操作尚無原子操作(或許本人孤陋寡聞),更何況自定義類型乎?

            綜上所述,摟主的實現(xiàn)中鎖操作不可少。
            若要實現(xiàn)成鎖無關的數(shù)據(jù)結構,是不是應該交換的是兩個list*而不是list的內容,請參考http://blog.csdn.net/pongba/archive/2006/01/29/589864.aspx
            2008-10-23 00:34 | Cox

            # re: 對一個服務器的幾步優(yōu)化  回復  更多評論   

            "而交換緩沖區(qū)的時候調用的是STL中的swap操作, 也就是交換兩個鏈表的頭結點而已." 誰說只是交換表頭.
            你看下邊STL的源碼..
            template<typename _Tp>
            inline void
            swap(_Tp& __a, _Tp& __b)
            {
            // concept requirements
            __glibcxx_function_requires(_SGIAssignableConcept<_Tp>)

            const _Tp __tmp = __a;
            __a = __b;
            __b = __tmp;
            }

            不可能不加鎖. 而且你的sleep用得也很猥瑣. 要用條件變量.做一個高水標就可以了.
            2008-10-23 10:30 | cui

            # re: 對一個服務器的幾步優(yōu)化[未登錄]  回復  更多評論   

            各位,我想了想,還是改成加鎖了...
            2008-10-23 11:25 | 創(chuàng)

            # re: 對一個服務器的幾步優(yōu)化[未登錄]  回復  更多評論   

            一讀一寫的話,循環(huán)buf就可以。以前寫過一個類似的,主線程寫,多個線程讀,用的線程鎖,性能不錯。
            2008-12-29 13:52 | dd

            # re: 對一個服務器的幾步優(yōu)化[未登錄]  回復  更多評論   

            操作數(shù)據(jù)庫失敗會怎么樣?如果服務器都要等待數(shù)據(jù)庫返回才能進行下一步操作你這種就不行了
            2009-11-19 23:30 | xu
            久久亚洲AV无码精品色午夜| 久久精品卫校国产小美女| 国产欧美久久久精品影院| 国产99久久久久久免费看| 久久99精品久久久久久久不卡| 久久久亚洲裙底偷窥综合| 亚洲日韩欧美一区久久久久我| 久久人人爽人人爽人人片AV麻豆| 久久精品亚洲男人的天堂| 久久综合成人网| 亚州日韩精品专区久久久| 久久这里只有精品首页| 亚洲午夜久久久久妓女影院| 久久香蕉超碰97国产精品| 国产精品一久久香蕉国产线看| 99久久国语露脸精品国产| 亚洲欧美日韩精品久久| 亚洲性久久久影院| 久久久av波多野一区二区| 99久久成人18免费网站| 人妻丰满?V无码久久不卡| 亚洲AV无码久久精品色欲| 婷婷综合久久狠狠色99h| 久久精品亚洲乱码伦伦中文| 久久综合噜噜激激的五月天| 婷婷久久香蕉五月综合加勒比| 成人妇女免费播放久久久| 久久精品国产WWW456C0M| 一本色道久久88—综合亚洲精品| 国产91色综合久久免费| 日韩久久无码免费毛片软件| 久久久久久午夜成人影院| 99久久99久久精品国产| 久久午夜无码鲁丝片| 久久精品二区| 国产美女久久精品香蕉69| 亚洲国产成人精品女人久久久| 国产精品久久久久AV福利动漫 | 9999国产精品欧美久久久久久| 一本一本久久a久久精品综合麻豆| 国产精品久久99|