Redis在遊戲業務中的使用
本文介紹一下我在多個遊戲專案中,對Redis使用的經驗。Redis是個好東西,可以用在不同的業務場景。
0 Redis是什麼
Redis最大的特點是所有的資料都存放在記憶體裡,它常用於資料庫、cache和訊息佇列等場景。
1 Redis特性
讀寫效能高
首先,redis資料都在記憶體裡,其次,整體來看可以把redis看成一個大hashmap,所以基於key索引資料都可以在O(1)時間複雜度內完成,所以一個Redis節點(Redis程式)的QPS可以達到10w,特別適合作為傳統資料庫(mongo/mysql)的補充提升讀寫效率。
單執行緒
所有的讀寫請求都是單執行緒按照順序執行的,每個請求都是原子的,不會被打斷。
分散式
Redis支援分散式叢集,比如阿里雲會在叢集的多個節點前面放一個負載均衡,就可以像訪問單節點一樣訪問redis叢集。
如下圖所示,是我們在阿里雲的一個16節點redis叢集。
阿里雲的redis叢集
由於阿里雲提供的redis叢集節點數量總是有上限的(其實可以做到無上限),因此為了獲得更好和更靈活的水平擴充套件能力,我們遊戲在業務層又增加了一層分散式功能。在操作redis的同時,可以傳入一個引數作為index通過hash對映到某一個redis叢集上。
持久化
因為支援持久化,若同時開啟RDB和AOF持久化選項,可以作為持久化資料庫儲存資料,重啟redis資料不會丟失。
阿里雲同時開啟了RDB和AOF(存疑)。
少量資料的持久化儲存可以使用redis,但是大量資料不可以。因為redis會把資料全部放到記憶體裡,所以對於大量數量,記憶體成本太高。
比如好友關係這類關係資訊屬於多個玩家之間的資訊,儲存在玩家身上也不合適。而且這類資訊使用比較頻繁並且資料量不大,就可以持久化儲存在redis中。
其他高階特性
Redis還有一些高階特性:
- 事務:watch關鍵詞。
- 多命令:可以將多個命令同時發給某個redis節點,降低網路通訊時間。
- lua指令碼:通過lua指令碼,可以將一些redis操作包裝成原子操作。
- 釋出/訂閱:可以用於訊息分發。
- stream:redis抄kafka做的訊息佇列,4.0的功能...
2 Redis資料結構
redis整體來看儲存的是一個很大的hashmap,其中key是一個字串,value可以有多種資料結構。
value支援String、Hash、List、Set、SortedSet五種資料儲存結構,可以實現不同的業務需求:·
- String:儲存簡單的int\string
- List:列表/佇列,也可以作為訊息佇列使用。
- Hash:一個key/value的鍵值對,這裡的value只支援簡單的型別(int\string)
- Set:元素不會重複的集合。
- SortedSet:有序集合,元素不會重複,同時元素會有一個score值,這個集合會根據score進行實時排序。常用的需求是排行榜。
大家使用redis時,使用方式的就是通過介面操作這些資料結構,所以建議大家看一下redis的基本操作http://doc.redisfans.com/
3 Redis在遊戲業務中的常見應用
一般來說,遊戲會使用mongo或者mysql儲存各類遊戲資訊,但這種資料庫將資料存在硬碟裡,讀些速度比較慢。而redis將資料放在記憶體裡,讀寫速度可以提高一個數量級。因此,可以配合mongo/mysql使用。
Cache
redis最常見的功能就是作為cache使用,遊戲中很多邏輯如果去讀mongodb,會有效能問題,所以使用redis作為快取,降低對mongodb的讀寫。
此外,還有些需求可以使用Reids作為快取,比如玩家個人簡要資訊。當我們檢視其他玩家資訊時,往往不需要全量資訊,只需要部分資訊(線上狀態、等級、部分資訊等),而這些資訊要是去讀取mongo會產生非常大的壓力。因此,可以將指定的玩家資訊放在Redis裡,提高訪問效率。
業務狀態/資料儲存
因為redis本身就支援持久化儲存,所以redis在一定程度上可以替代mongo。
那麼,什麼資料適合存在mongo,什麼資料適合存在redis呢?
對於一個資料應該放在redis還是mongo中儲存,主要考慮兩個方面:1.資料量。2.資料訪問頻率。
若資料量較小,比如記錄哪些區域聊天頻道需要關閉,這種資訊一般使用redis記錄即可。
若資料量較大但不是特別大,但資料訪問頻率比較高,比如玩家好友關係資訊,那麼一般也可以使用redis儲存。
若資料量特別大,比如玩家/家族全量資訊,一般使用mongo進行儲存。
無狀態服務開發支援
一般遊戲伺服器都是有狀態的,也就是說記憶體裡是有資料的。而無狀態的服務的意思是記憶體裡不儲存資料(有可能在執行邏輯過程中有些臨時變數)。
關於有狀態無狀態的描述,可以參考https://blog.csdn.net/yinxiangbing/article/details/53353940
記憶體裡不儲存狀態,那麼狀態存在哪裡呢?redis就可以儲存狀態的地方。
舉個例子,我們要開發一個組隊服務,那麼就需要一個組隊匹配池,我們可以將匹配池儲存在服務記憶體中(有狀態),當然也可以將這個匹配池資訊儲存在redis中(無狀態)。
無狀態(資訊儲存在redis)的好處如下:
若我們將匹配池儲存在記憶體中,那麼這個業務效能的瓶頸最大是一個程式,哪怕使用多執行緒,那麼天花板就是一臺伺服器。而若使用redis儲存資料,我們就可以寫多個程式來執行業務邏輯,效能瓶頸就不再存在,理論上就不會出現效能瓶頸。
若資料放在記憶體裡,機器和程式是有概率crash,資料也就丟失了。而redis可以使用主從切換,資料不會丟失,容錯能力強。
壞處:成本高、程式設計麻煩。
我在網易曾經寫過一套微服務框架,其實就是用的無狀態的原理寫遊戲邏輯。為服務框架非常適合寫非戰鬥邏輯,對大DAU遊戲可以提供非常強力的支援。後面有機會可以寫文章介紹下思想。
多程式/執行緒/執行緒之間的共享資料儲存
有些不同的程式需要共享資料,可以使用redis儲存這些共享資料。
比如有個遊戲需求是全服玩家給最喜愛的角色投票,所有玩家在不同的程式都需要操作這些資訊,就可以將這些資訊儲存在redis中進行共享。
臨時資料管理
redis的資料支援生命週期,所以若一些資料是臨時的(比如活動資料,活動結束後就不需要了),就可以將資料存在redis中,並且給一個生存時間(通過EXPIRE命令),當時間超過生存時間就會自動刪除。
排行榜
Redis的SortedSet非常適合做排行榜功能,在幾年前Redis不火時,如何寫一個好用的遊戲排行榜還是一個課題,近兩年就再也沒有人提這個事情了,沒啥好說的,用redis就好了。
分散式鎖
不同程式中的邏輯若希望使用鎖寫同步邏輯,可以基於redis的SETNX命令實現。
參考:https://juejin.im/post/5b737b9b518825613d3894f4
訊息佇列
遊戲中不同的程式之間的通訊,也可以使用redis作為訊息佇列來通訊。
比如給遊戲伺服器發一個控制命令,就可以將命令存在redis的list中,遊戲伺服器實時檢查redis是否有命令,有則pop出來執行。
Redis能否作為訊息佇列使用是一個常常被爭論的話題,因為有更專業的訊息佇列,比如Kfaka。我曾經服務的一個百萬級DAU專案曾經使用Redis作為簡單功能的訊息佇列(只有訊息傳輸功能),由此可見Redis作為訊息佇列在生產環境使用問題並不大。
4 Redis使用注意事項
避免出現大key,通過小key進行分散
大key表示一個key對應的value非常大。比如,我們希望記錄每個玩家的當前線上狀態,若我們建立了一個key為online_status的hashmap,這個hashmap的格式為uid:status。那麼,所有的玩家的線上資訊都儲存在這個key中。這種情況,就會導致online_status這個key對應的value極大。
redis雖然沒有限制一個key對應的value的大小(應該沒有),但是不建議使用大key。正常的使用方式是將每個玩家的資訊作為一個字串直接儲存在redis中,比如key為online_status_{uid},value為是否線上。
原因是redis基於key來做的分散式,若建立了一個大key,就會導致分散式功能失效,所有的請求都會到達同一個redis節點,導致這個節點卡頓,其他節點空閒。
Bigkey沒辦法檢測
避免hotkey
hotkey的含義是某個key讀寫頻率很高,特別繁忙。
需要避免這種情況的原因和大key類似,會導致分散式失效,某些節點卡頓。
hotkey在Redis4.0版本有檢測方式
給key增加字首
大家需要建立一個新的key前,需要保證key在所有業務中是獨一無二的,因此key最好有namespace的概念。
我們可以通過給key前面加一個字首,來避免key重複的情況。比如我們需要記錄玩家線上資訊,key設計為player:online_status,而當我們又需要記錄家族的活躍狀態,key設計為family:online_status。
(舉個栗子,家族活躍狀態應該叫active_status更合適...)
5 redis冷熱資料分離
上文說過,Redis不適合做大資料的持久化儲存,因為Redis會將所有資料都放在記憶體裡,相比硬碟儲存,成本極高(作為一個成功遊戲專案都覺得貴)。
其實有一種折中方案,就是將資料分為冷資料(表示長時間不活躍的資料,比如流失玩家資料)和熱資料(活躍資料,比如經常登入玩家資訊),將冷資料放在硬碟裡,將熱資料放在記憶體裡。然後當長期不活躍玩家迴流後,就將這個玩家的資料從冷資料轉為熱資料,從硬碟中轉到記憶體中,玩家長期不線上則把他的資料從熱資料轉為冷資料,從記憶體中轉到硬碟中。這樣的話,記憶體成本就會降低很多。
之前專案同事曾經嘗試實現過這個功能。就是寫一個proxy,後面掛一個redis和mongo,proxy實現一套redis協議解析,並且實現冷熱資料切換和管理。冷資料存在mongo,當訪問冷資料時將資料載入到redis中轉為熱資料,熱資料超過一個ttl後,又變為冷資料存到mongo中。
這個有一些問題,比如redis資料和mongo資料格式如何對應等,如何保證資料一致性和高可用性等。此外,對redis的使用API各種限制也比較多。
騰訊雲有一個類似的產品TcaplusDB,但是不支援Redis命令,據說騰訊遊戲內部用的都是這個東西(騰訊的同學可以說說)。騰訊還有一個類似的開源專案DCache,估計就是TcaplusDB的開源產品。
阿里雲已經有Redis冷熱資料分離的beta版本,等成熟了,甚至可以一定程度上取代mongodb。我認為這個專案用在遊戲行業非常合適,關注已久。
雲資料庫Redis_混合型儲存_冷熱資料分離_節約成本
promotion.aliyun.com
附:《忍者必須死3》團隊招人
我們忍3團隊目前還是一箇中小團隊,但是遊戲玩家數量大,遊戲質量高。專案裡需要做的事情非常多,公司在緊鑼密鼓的招人。
所以,招人,客戶端、服務端都招,高階或資深都有需求。
我們內部是全棧開發模式,但只要求你有一定的客戶端或服務端經驗。只要人靠譜,技術棧啥的都問題不大。
公司在杭州濱江,團隊氛圍很好,具有競爭力(不輸大廠)的薪資待遇。
另外,策劃、QA、美術、海外發行等崗位都在招,有需要我可以幫忙轉簡歷。
其他資訊,私信我或加我微信詳聊:shinepengwei
作者:水風
專欄地址:https://zhuanlan.zhihu.com/p/81639208
相關文章
- Nebula Graph 在網易遊戲業務中的實踐遊戲
- Redis在.net中的使用(7)redis部署為Windows服務RedisWindows
- Redis在.net中的使用(2).net專案中的Redis使用Redis
- Redis在.net中的使用(5)Redis持久化Redis持久化
- Redis在.net中的使用(6)Redis併發鎖Redis
- 解讀:在什麼業務場景適合使用Redis?Redis
- Redis樂觀鎖在電影購票業務中的應用Redis
- Redis在Window服務下的安裝使用Redis
- 在 SAP Kyma 上使用 Redis 服務Redis
- Redis在.net中的使用(1)下載安裝RedisRedis
- Protobuf在微信小遊戲開發中的使用技巧遊戲開發
- Redis的安裝及在Java中的使用RedisJava
- Redis 哨兵使用以及在 Laravel 中的配置RedisLaravel
- 在索尼未來戰略中,遊戲業務關鍵詞是「沉浸」與「無縫」遊戲
- 深度分析Redis分散式鎖在電商超賣業務場景下的使用Redis分散式
- 食物在電子遊戲中的妙用遊戲
- SPI擴充套件點在業務中的使用及原理分析套件
- Redis 中的事務Redis
- 短訊 | 騰訊考慮通過債務融資收購潛在的遊戲業務遊戲
- Lua 指令碼在 Redis 事務中的應用實踐指令碼Redis
- Lua指令碼在Redis事務中的應用實踐指令碼Redis
- 玩家在遊戲中面臨的道德困境遊戲
- AI在視訊遊戲中的應用AI遊戲
- 遊戲推薦業務中基於 sentinel 的動態限流實踐遊戲
- 「找找看」遊戲:埋沒在歷史中的遊戲文化功臣遊戲
- 領先移動遊戲平臺Catappult透過擴充團隊加強其在中國的業務遊戲APP
- 在中國遊戲業當“老三”,為何這麼難?遊戲
- 什麼業務場景適合使用Redis?Redis
- GPC:2020年中國遊戲產業報告 中國遊戲使用者達6.65億遊戲產業
- 遊戲基礎知識——“骸骨”元素在遊戲中的設計要點遊戲
- Spring事件,ApplicationEvent在業務中的應用Spring事件APP
- SpringPlugin-Core在業務中的應用SpringPlugin
- 中國遊戲的得與失,中國遊戲的工業化還缺什麼?遊戲
- Redis在.net中的使用(3)簡單的主從複製Redis
- 網易推出新遊戲業務“發燒遊戲” 主打引進精品遊戲遊戲
- 透過示例在 Unity 和 NodeJS 上的遊戲中建立安全、快速的多人遊戲UnityNodeJS遊戲
- Redis 中的事務分析,Redis 中的事務可以滿足ACID屬性嗎?Redis
- redis在nodejs中的應用RedisNodeJS