前言:
游戲領域, 特別是移動端的社交類游戲, 排行榜成為了一種增強體驗交互, 提高用戶粘性的大法寶. 這邊講述在不同用戶規模下, 游戲服務化/游戲平台化趨勢下, 如何去設計和實現游戲排名榜. 本文側重講解Nosql在游戲排名榜中的作用. 小編(mumuxinfei)對這塊認識較淺, 所述觀點不代表主流(工業界)做法, 望能拋磚引玉.
秉承:
上一篇文章, 詳見: 社交游戲的排行榜設計和實現(1)
進階篇:
隨着數據量/並發量的上漲, Mysql集群也呈現了一些疲態.
(1). 數據庫分庫分表后, 表間的join事實上失去了意義. 要join的表數據在不同的庫中.
(2). 數據庫的讀寫性能很容易達到上限.
如何破解:
我們先回過頭來看些表定義以及使用
表tb_score使用user_id訪問score得分,其實user_id相當於key, score相當於value. 因此可借助Nosql的持久化key/value系統去實現,redis/mongodb/leveldb/hbase, 這樣無論在讀寫速度上, 還是在應用擴展性上,都獲得了很大的提升.
表tb_friend借助user_id來獲取friend_id列表, 其本質相當於user_id為key, list<friend_id>為value, 而key對應value列表, 可借助redis(數據結構服務器),也可借助sorted key/value db系統. 這邊我們選用sorted key/value db, 因其數據按key的順序存儲.
sorted key/value db的特點是:
(1) 支持key的前綴查找
(2) 支持批量/范圍查詢
我們可以如下好友列表的key/value格式
key => user_id:friend_id value => score
評注:
key=>user_id:friend_id表示friend_id為user_id的好友.
那我們好友列表的查詢, 就演變為前綴為"user_id"的范圍查詢.
系統演變: 關系型數據庫mysql轉化為Nosql系統.
緩存篇:
分布式緩存, 永遠是互聯網界的狗皮膏葯, 無論什么疼痛, 貼一下總有療效. 緩存的引入也是見血封喉, 效果非常顯著, 不過需要注意數據一致性問題. 不過互聯網能忍受弱事務性/弱數據一致性(C), 而強調可用性和可擴展性(AP). 移動端游戲, 其實也是類似的執行策略(除了和錢相關的業務). 常用的緩存有memcache/redis, 這些都是hash型散列的內存緩存.
簡單的采用Key/Value, 而不采用redis的key/sort-list的方式.
value為json格式的列表:
json格式的內容 [{'user_id' : 'score'}, {'user_id' : 'score'}, {...}]
排名相對靜止, 因此緩存系統能擋掉大部分的數據讀.
但是引入緩存以后,數據的一致性,如何去保證?
模擬如下應用場景:
1). 好友破了新的記錄
2). 新增/刪除好友關系
這些情況發生后, 會更新好友的排行榜. 需要更新緩存, 使得緩存和后端服務的持久數據保持一致.
那么如何去實現?
這邊談談常見的三種思路
1). 同步更新: 當好友添加/刪除以后, 主動刪除排行榜緩存. 而用戶的分數創新高后, 主動遍歷好友列表,通知刪除相應的緩存.
2). 異步更新: 當好友添加/刪除, 或者用戶分數創新高, 其投遞一個事件到一個隊列中, 有隊列消費者做這個耗時的同步操作.
3). 緩存定期刪除: 設定緩存key的有效期ttl, 不關注好友添加/刪除, 得分更新事件的發生, 允許數據的一致有一定的延遲.
這三種方案的優缺點, 可以對比如下:
實時性 | 操作代價 | 擴展性 | |
同步更新 | 最好 | 有副作用,嵌入好友添加/刪除代碼邏輯中, 響應變大 | 不好, 將來的好友添加/刪除邏輯會越發的臃腫 |
異步更新 | 一般 | 影響小 | 好, 引入隊列, 可以由不同消費端做不同的處理 |
緩存定期刪除 | 一般 | 幾乎無影響 | --- |
評注: 通知排行版更新是個重操作,比較耗時, 同步操作會影響響應時間.
對於游戲而言, 如果排行榜不是實時排名, 采用方案2/3, 都是可接受的. 對於方案3, 這種沒心沒肺的做法, 其實最簡單有效了(個人觀點).
服務/平台化
當一個社交類App演化為一個平台時, 好友模塊和游戲模塊就自然分開了, 其數據庫/Nosql系統邏輯上不在一塊了,比如微信App, 其內部肯定把各類服務做了拆分, 其數據是彼此隔離的. 在這種服務/平台化的演進下, 好友特定的游戲排行榜, 又該如何破?
我們假定如下的服務, 其交互流程如下.
GameService/FriendService模塊
評注:好友更新事件主動觸發Game模塊, 代價也特別大(如之上所述). 同時也需要Friend模塊添加相關的邏輯代碼,這使得模塊之間緊耦合了.
借助隊列, 采用異步的方式來實現. 這相當於在模塊之間采用了觀察者(Observer)模式, 事件的觸發者只要簡單的投遞事件於(topic模式)隊列中. 然后由需要關注該事件的服務模塊主動去訂閱它. 新模式轉化為如下:
評注: 通過隊列異步化事件, 采用訂閱的方式, 來實現解耦.
服務平台化后, 這種做法, 在工業界常常被采用.
后記:
本來只想寫一篇文章, 關於社區游戲排行榜的設計的. 但發現內容有些長, 於是就拆分成了兩篇, 里面的內容簡單的涉及了一些, 並沒有具體展開. 小編(mumuxinfei)對這塊還是入門尚淺, 如果有什么不足, 希望能指正.