所遇不良設計(四)

 

1 飲鴆止渴的存儲過程

  可能剛開始開發的時候,圖一時的簡單,爲了在服務端少寫那麼多的C/C++代碼,用SQL語句來實現一些邏輯,只須要幾行sql語句的存儲就解決了,服務端直接調用存儲過程,好比說咱們要作兩張表的數據的關聯。兩張數據表的操做,若是用程序加載進入內存,而後在比對關聯的字段,彷佛步驟過多。當用sql語句,能夠用select … where … 就能解決問題。起初一個區的玩家可能比較少,性能的問題不能凸顯出來。當遊戲開始冷卻下來,玩家大量離開,爲了維持一個區域上的玩家的活躍度,就會去合服。有時候甚至幾個區的遊戲合成到一個區,兩張表的關聯數據過多,存儲過程執行速度過慢,會致使服務器死鎖。到了後期,見到這個問題,要人員再去修改這個模塊,可能有諸多抱怨,何況修改這個系統的人不是寫這個模塊的本人。最後運維將一些等級比較低的,好長時間沒有玩的玩家的清理掉,來減小表間數據的關聯。我以爲這樣子損害了一部分玩家的利益的,可能他某天忽然想起到這個遊戲,發現本身的帳號再也登不上了,雖然他不是付費的玩家。linux

   還有一個問題,就是排行榜的問題,若是用sql語句也是很簡單,直接select加上top函數而且排序就好了,多麼簡單。可是因爲玩家的數據一直在變化,排行必須實時獲取,用存儲過程也是會耗費不少時間的,其在不停的對大量的數據進行重複排序的。這些問題,咱們應該在服務端來解決,針對不一樣的問題,咱們應該區分對待。redis

   若是所排列的數據是有一個範圍的,並且這個範圍比較小,好比[0, 1000]。咱們可使用桶排序的, 數據結構是一個鏈表數組,數組有1000個桶。如圖:sql

  咱們直接將對應的值插入到對應的桶的鏈表裏面就OK,插入的速度是o(1),而後就能夠直接按照從大到小的順序抽出1000玩家就OK。數據庫

  若是所排列的數據是有沒有範圍的。咱們能夠這樣實現,用一個vector容器來裝前一千個玩家,而後用一個hashmap來容納其他的玩家,當有一個玩家的分值發生改的時候,若是能排上前1000, 就插入到vector裏面,插入的時間複雜度爲o(n);若是排不上1000, 就放到hashmap裏面去,插入的時間複雜度爲o(1),這樣子反應速度仍是可以忍受的。數組

  注意:使用存儲過程首先要估量存儲過程是否是使用頻率比較高; 其次其關聯表的多少,並且要考慮當玩家數量激增到必定值時,其反應速度是否可以被接收。緩存

2 一劍封喉的kill -9

  當咱們運行一個進程,不能關閉時,咱們常常這樣子:安全

1    ps -ef | grep "進程名" #查找到進程的id
2 
3    kill -9 progress's id #終止進程
View Code

   我發現有時候遊戲須要正常停服,維護的時候,常常這樣作; 雖然維護的時間是在夜深人靜的時間段,對服務器形成不了什麼影響。我實在擔憂服務器程序就這樣忽然閃停,會形成數據的紊亂。其實發如今linux下面常常能見到一些服務可以經過命令來中止、開啓、從新啓動。例如:服務器

1    /etc/netservice stop
2 
3    /etc/netservice start
4 
5    /etc/netservice restart 
View Code

  爲何咱們不能經過命令選項的方式來對服務進行安全的中止了,使其安全着陸了。要是系統數據發生問題,將很難找到問題出現的源頭。下面是我用鎖來解決問題的流程圖:數據結構

   

  圖片上有三條主線:運維

   1.直接進入start塊,來開啓服務,若是已經上鎖了,直接結束,提示已經運行了一個服務實例,保證永遠只有一個服務實例運行; 不然就啓動程序,由於已經獲取了鎖資源,再獲取鎖資源的時候會阻塞。

   2.直接經過外部程序stop,來爲已有的服務實例解鎖,start模塊獲取第二次的鎖資源會阻塞,這時候解鎖以後,會獲取第二次所資源,緊接着進行收尾工做,接着再進行解鎖,最後結束程序。

   3.是restart,先stop,中止已經有的實例,而後start來開啓服務。這一個主線就是2和1的合集。 這樣子比kill -9 安全多了,讓程序正常着陸。

3 萬里狂飆的緩存

  當沒有了緩存,常常要查詢和更改玩家的數據,直接對數據庫進行操做。當在一天中最活躍的時間段,往數據庫中插入的數據太頻繁,或者查詢數據的太頻繁,就會形成數據庫的死鎖,服務器會卡主,形成客戶端反應過慢,有時候形成服務端線程卡主,直接致使玩家登錄不進來,這種體驗對於玩家來講,糟糕透頂。

   這時候緩存就很重要了。緩存通常就是共享內存數據結構,共享內存的好處就是可以是實現進程間通訊,並且當一個服務進程崩掉的時候,它仍是存在的; 也就是共享內存脫離於進程以外。這時候咱們要設計緩存就是實現數據庫與服務端數據之間的過渡,不要讓查詢、更改數據庫太過頻繁。緩存基本上可以杜絕數據庫的查詢,其次能夠把數據庫的更改收集起來,蒐集的足夠多了,在必定的時間點把數據同步到數據庫,這樣子效率就高多了。程序-緩存-數據庫如圖:

   

  這彷佛很好,不過忽略了一個問題,那就是持久化的問題。就是緩存還沒來得及往數據庫裏面同步數據,忽然間宕機了,這樣子形成大量玩家的數據丟失,形成損失仍是挺大的。 要是實現緩存+持久化,仍是要費點腦子,我建議使用NOSQL數據庫。redis和mongdb均可以,我比較傾向於redis。還有一種緩存就是memcache,可是不支持持久化的,咱們能夠經過memcache來存儲遊戲模板數據,由於模板數據是不用去更改的,只用做查詢。這樣子能夠加快服務端的啓動速度(啓動時減小去加載模板表)。 使用redis和memcache,能夠儘快的加快服務的啓動,使數據更加安全了。

4 注意:

以上的三幅圖片都是用graphviz製做的,graphviz屬於語言做圖工具,設計的流程圖所想即所得。

相關文章
相關標籤/搜索