移動互聯網實戰--社交游戲的排行榜設計和實現(1)


 

前言:
  游戲領域, 特別是移動端的社交類游戲, 排行榜成為了一種增強體驗交互, 提高用戶粘性的大法寶. 這邊講述在不同用戶規模下, 游戲服務化/游戲平台化趨勢下, 如何去設計和實現游戲排名榜. 本文側重於傳統關系型Mysql的方案實現, 后續會講解Nosql的作用. 小編(mumuxinfei)對這塊認識較淺, 所述觀點不代表主流(工業界)做法, 望能拋磚引玉. 

需求分析
  曾幾何時, 微信版飛機大戰紅極一時. 各路英雄刷排名, 曬成績. 不過該排名限制在自己的好友圈中, 而每個用戶的好友圈各不一樣, 因此每個用戶有自己的排名. 且排名按周重置清零. 一些簡單的移動端游戲(比如2048, 沒有好友概念), 則采用簡單的全局排名的方式, 且排名采用歷史最高.

  
  綜上的列子, 對於游戲排行榜, 我們可以依據屬性來進行划分.
  1). 按是否屬於好友圈划分
  * 游戲全局的Top N模式.
  * 以自身好友圈為界的Top N模式.
  2). 按時間周期來划分
  * 按時間周期重置, 比如按周清零
  * 歷史最高, 沒有重置清零機制

基礎篇: 
  社交類游戲, 在小規模用戶的前提下, 借助關系型數據庫(mysql)來實現, 采用單庫單表.
  定義好友表tb_friend

CREATE TABLE IF NOT EXISTS tb_friend (
  id INT PRIMARY KEY AUTO_INCREMENT, 
  user_id varchar(64),
  friend_id varchar(64),
  UNIQUE KEY `idx_tb_friend_user_id_friend_id` (`user_id`, `friend_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

  用戶得分表tb_score

CREATE TABLE IF NOT EXISTS tb_score (
  id INT PRIMARY KEY AUTO_INCREMENT,
  user_id varchar(64),
  score int,
  UNIQUE KEY `idx_tb_score_user_id` (`user_id`)	
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

  評注: tb_friend表中user_id/friend_id構成復合索引, 用於維護user_id的好友列表, tb_score用於記錄每個用戶的得分情況
  在該兩張表的前提之下, 如何獲取該好友的排行榜呢?
  利用兩表join來實現:

SELECT tf.friend_id AS friend_id, ts.score AS score 
FROM tb_friend tf JOIN tb_score ts ON tf.friend_id = ts.user_id 
WHERE tf.user_id = ? 
ORDER BY ts.score DESC	

  類似的結果如下:
  
  評注: 在sql的join中, 需要注意left join/equal join/right join的區別. 這邊選用等值join.

性能評估和執行分析:
  1). 小表+大表模式: 在tb_friend單表9801條(100個小伙伴, 互為好友)/tb_score單表53條(53個小伙伴有得分)記錄下, 進行join分析
  執行規划

EXPLAIN 
SELECT tf.friend_id AS friend_id, ts.score AS score 
FROM tb_friend tf JOIN tb_score ts ON tf.friend_id = ts.user_id 
WHERE tf.user_id = ? 
ORDER BY ts.score DESC

  

  評注: 這邊sql優化器非常的智能, 借助了小表驅動大表的join優化方式(小表tb_score驅動大表tb_friend進行join), 小表用到了file sort(總共53行記錄), 大表用了index(等值join對應一行大表記錄).
  2). 等表模式: 在tb_friend單表19602條/tb_score單表5092條記錄下, 進行join分析

  

  評注: 這邊tb_friend表驅動tb_score作join, tb_friend借助復合索引(user_id,friend_id)來加速優化. Mysql的sql優化器還是相當的智能和強大. 

進階篇:
  隨着數據規模越來越大, 並發訪問量的增加, mysql的訪問逐漸變成瓶頸. 同時該join的sql語句涉及的filesort非常耗CPU的. 如何破解這種狀況?
  *) 引入分布式mysql集群, 進行分庫分表.
  分庫分表作為互聯網的一大神器, 作用立竿見影. 但是有所得,就會有所失, 分庫分表后, 會失去很多特性. 比如事務性, 外鍵約束關聯. 在業務這層, 會導致涉及用戶的tb_score, tb_friend表數據不在同一庫中, 進而導致join失效. 最終導致, 在應用層做merge, 使得排名操作演變成 1+N sql操作(1 sql 用於獲取好友列表, N sql 用於獲取每個好友的得分). 這需要注意.

  1+N的SQL演化, 應用層做得分排序, 性能會演變成一場災難.

(1) 獲取用戶好友列表
SELECT friend_id FROM tb_friend_{N} WHERE user_id = ?
(2) 遍歷獲取每個好友的得分
foreach friend_id in friend_list(?)
SELECT score FROM tb_score_{M} WEHRE user_id = ? 
(3) 應用層做得分排序

  評注: {N},{M}是指具體的分表數, 當然在同一庫同一表, 可以借助SELECT * IN (...)來優化,這個得看具體的數據分布. 不過是種很好的思路.
  小編觀點: 由於tb_friend是大表, 而tb_score是小表, 因此tb_friend采用分庫分表(以user_id作為依據)的方式去實現, 而tb_score采用單庫單表(便於批量查詢)的方式實現. 
  當然在工業界, Mysql的優化方案非常的成熟, 不光是分庫分表,還有主從分離(Master/Slave機制, Master用於寫服務, 多Slave節點提供讀服務).
  可以參見如下的圖示:
  

總結&后續:
  這邊主要講述基於傳統關系型數據庫mysql來實現基於好友的游戲排行榜, 個人的戰績需要實時的去獲取, 而好友列表的戰績能允許有一定的延遲. 而好友戰績的排序實現,就成為了本文的中心議題. Mysql的實現方案在數據量/並發數增加的前提下,還是顯示了一定的疲態. 下文將講解, 如何引入Nosql系統, 在游戲rank中,扮演重要的角色. 期待你的關注.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM