最近寫了一個服務(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)的操作并不多, 我想效率沒有太大的影響.