• <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>

            那誰的技術(shù)博客

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

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

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

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

            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ù)導(dǎo)入數(shù)據(jù)庫.用偽代碼來表示, 這個優(yōu)化就是這樣的:
            主線程:
                往緩沖區(qū)A中添加數(shù)據(jù)(注意這里不需要加鎖)

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

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

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

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


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

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

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

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

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

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

            評論

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            為啥要用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: 對一個服務(wù)器的幾步優(yōu)化  回復(fù)  更多評論   

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

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

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

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


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

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

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

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

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

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

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

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

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

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

            首先,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)有關(guān):(以VC6 STL實現(xiàn)為例)
            template<class _Ty> inline
            void swap(_Ty& _X, _Ty& _Y)
            {_Ty _Tmp = _X;
            _X = _Y, _Y = _Tmp; }
            不能保證編譯器能把這三步操作優(yōu)化成原子操作。

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

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

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

            "而交換緩沖區(qū)的時候調(diào)用的是STL中的swap操作, 也就是交換兩個鏈表的頭結(jié)點而已." 誰說只是交換表頭.
            你看下邊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: 對一個服務(wù)器的幾步優(yōu)化[未登錄]  回復(fù)  更多評論   

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

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

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

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

            操作數(shù)據(jù)庫失敗會怎么樣?如果服務(wù)器都要等待數(shù)據(jù)庫返回才能進行下一步操作你這種就不行了
            2009-11-19 23:30 | xu
            亚洲精品乱码久久久久久蜜桃不卡 | 日韩精品久久久久久久电影| 99re久久精品国产首页2020| 国内精品综合久久久40p| 久久久久亚洲AV成人网| 国产精品99久久久久久www| 久久成人国产精品二三区| 精品久久久久久成人AV| 久久99精品国产自在现线小黄鸭| 无码国产69精品久久久久网站| 伊人久久大香线蕉综合影院首页| 思思久久好好热精品国产| 亚洲欧美久久久久9999| 久久这里只有精品首页| 亚洲精品乱码久久久久久蜜桃图片 | 亚洲精品无码久久千人斩| 久久综合给合久久狠狠狠97色| 国产美女久久精品香蕉69| 久久精品国产亚洲AV大全| 九九久久99综合一区二区| 精品国产热久久久福利| 人妻系列无码专区久久五月天| 无码八A片人妻少妇久久| 欧美精品久久久久久久自慰| 996久久国产精品线观看| 人妻少妇精品久久| 国产三级久久久精品麻豆三级 | 久久久久亚洲Av无码专| 国产精品久久久久一区二区三区 | 国产99久久久久久免费看| 久久精品亚洲男人的天堂| 亚洲精品乱码久久久久久自慰| 91精品国产乱码久久久久久| 久久久久久久综合日本| 日韩久久久久久中文人妻| 久久精品无码一区二区三区免费| 怡红院日本一道日本久久 | 日韩人妻无码一区二区三区久久99| 久久综合给久久狠狠97色 | 久久久久亚洲AV无码专区首JN| 狠狠狠色丁香婷婷综合久久俺|