1. 程式人生 > >遊戲服務器緩存策略

遊戲服務器緩存策略

roc 自己 proc 信息 數據同步 數據庫操作 清除緩存 ces 而是

1:什麽是緩存

在數據庫與服務器邏輯之間加入的數據層


2:作用

減少數據庫操作

服務器使用mysql作為數據庫,mysql每秒鐘並發數量有限,所以我們要減少mysql的操作。


3:erlang的緩存

erlang 在內存中可用 進程字典/ gen state / ets 儲存變量,理論上三種方式都可以作為緩存


4:緩存實現方案

方案 1

原理

讀數據時從緩存內讀取,如果緩存不存在則從數據庫中篩選,並放入緩存內

數據變更時直接操作數據庫,並清除緩存

實現方式

啟動一個gen_server進程將所有 ets建立起來,ets會分為 ets_player,ets_bag 等將數據分開儲存到ets表中,一般以玩家id作為ets的key,讀取某個數據會優先訪問ets表中數據,再從數據庫篩選


這是我們最初的方案,缺點十分明顯,只能減少部分讀取操作,在頻繁更改數據的情況下,緩存幾乎沒有作用,緩存在不停的被清理,當需要讀取數據時又需要重新從數據庫中拉取。


方案2

原理

讀取策略與方案1一致

數據變更時變更緩存內數據,不操作數據庫,當玩家下線時再將緩存數據同步到數據庫中


實現方式

同樣會啟動一個gen_server進程將所有 ets建立起來,不同的是ets中的每一行多了一個是否修改的標識位,玩家讀取數據依然優先訪問ets表中數據

當玩家修改數據,例如升級後,修改ets_player中對應的數據,並將ets中修改標識設為已修改

玩家下線後,順序檢查各個ets中的數據,將標識為已修改的數據同步到數據庫中


方案2與方案1相比,最大的變更在於數據更改時不清理緩存,而是修改緩存,這就避免了頻繁的從數據庫中篩選數據。

將數據庫同步操作積累到玩家下線而不是立即寫庫,可以減少許多sql語句,例如玩家在上線期間由 1級升到5級,下線寫庫只會執行一條sql,而即時寫庫則需要4條,對2,3,4,5分別執行update

最初我們以為這套方案已經可以滿足遊戲線上運行需求,直到我們做了一個壓力測試(膝蓋中了一箭?),例如你有10多個系統,玩家,競技場,背包,抽獎,等等等等那麽在玩家下線時會出現數據庫操作高峰。在壓力測試時峰值十分明顯,尤其是開服初期導量十分多的情況。


方案3

原理

由於玩家下線時間可能會出現峰值,所以我們想到了使用定時寫庫這種方式,將寫庫時間設定為我們認為合適的間隔


實現方式

每一張數據表都啟動一個對應的gen_server來管理數據,進程內部會創建一個ets表,玩家讀取/修改數據時都會訪問對應的gen_server

當玩家修改信息後,gen_server內部會記錄修改的信息。

gen_server內部啟動一個定時器,每隔一段時間將修改的數據同步到數據庫中


與方案2相比,方案3將寫庫時間控制在自己手裏,在遊戲開新服導量期間,可能設定數小時同步一次數據,可以很大程度減少sql壓力

但這套方案也有缺陷,由於玩家查詢/修改數據都需要訪問同一個gen_server,部分gen_server 如玩家信息,就會出現瓶頸,出現超時現象

方案4

原理

為了解決方案3中gen_server訪問的瓶頸問題,我們在gen_server外層又添加了一層緩存


實現方式

在方案3的基礎上,又將方案1,2中的cache_process添加回來,玩家讀取數據會先訪問cache_proces中的數據,如果其中沒有再訪問對應的gen_server進程


最終 讀取數據流程 玩家獲取數據 -> cache_process中查找 -> gen_server 中查找 -> 數據庫中查找

玩家更改數據 -> 更改cache_process

-> 更改 gen_server 中數據 -> 一定時間後 gen_server將數據寫回數據庫

遊戲服務器緩存策略