摘要:年月日本文是關于記錄某次游戲服務端的性能優化此處涉及的技術包括引擎隨著游戲導入人數逐漸增加單個集合的文檔數已經超過經常有玩家反饋說卡特別是在服務器遷移后從核降到核卡頓更嚴重了遂開始排查問題確認服務器壓力首先使用命令查看總體情況此時占用不高
Last-Modified: 2019年6月13日11:08:19
本文是關于記錄某次游戲服務端的性能優化, 此處涉及的技術包括: MongoDB(MMAPv1引擎), PHP
隨著游戲導入人數逐漸增加, 單個集合的文檔數已經超過400W, 經常有玩家反饋說卡, 特別是在服務器遷移后(從8核16G降到4核8G), 卡頓更嚴重了, 遂開始排查問題.
確認服務器壓力首先使用top 命令查看總體情況, 此時cpu占用不高, %wa比例維持在40%左右, 初步判斷是磁盤IO過高
使用iotop命令以進程粒度來查看io統計, 發現MongoDB進程全速在讀操作.
使用MongoDB自帶的mongostat 命令, 發現 faults字段持續高達200以上, 這意味著每秒訪問失敗數高達200, 即數據被交換出物理內存, 放到SWAP
由于未設置交換空間, 因此無法通過 vmstat 命令查看是否正在操作SWAP
在mongo shell中執行 db.currentOp() 確認當前存在大量執行超久的操作
到了此時基本確定問題所在了: 大量的查詢(先不管是否合理)導致MongoDB不斷進行磁盤IO操作, 由于內存較小(相較之前的16G)導致查詢過的緩存數據不斷被移出內存.
開始處理 減小單個集合大小這一步驟主要是針對庫中幾個特別大的集合, 且這些集合中的數據不重要且易移除.
此處以Shop表為例(保存每個玩家各種商店的數據), 在移除超過N天未登錄玩家數據后, 集合大小從24G降為3G
通過減小集合大小, 不僅可以提高查詢效率, 同時可以加快每天的數據庫備份速度.
慢日志分析需要打開慢日志
profile=1 slowms=300
逐條確認所有慢日志, 分析執行語句問題
use xxx; db.system.profile.find({}, {}, 20).sort({millis:-1});
此時的重點在于確認執行統計字段(execStats)中 階段(stage)是全表掃描(COLLSCAN)的, 這是最大的性能殺手.
增加/修改索引通過慢日志分析, 發現大部分全表掃描的原因在于:
排行榜定期統計
游戲邏輯需要對某些集合中符合條件的所有文檔 update
…
針對這幾種情況, 可以通過增加索引來解決.
舉例1: 玩家等級排行榜
// 查詢語句 db.User.find({gm:0}, {}, 100).sort({Lv:-1, Exp:-1}); // 移除舊索引, 增加復合索引 db.User.createIndex({Lv:-1, Exp:-1}, {background:true}); db.User.dropIndex({Lv:-1})
生產環境建索引一定要加 {background: true}, 否則建索引期間會引起大量阻塞.
還有刪除舊索引前, 記得先建立好新的索引, 避免期間出現大量慢查詢.
通過 explain("allPlansExecution")查詢分析器可以看出, 此時最初階段是 IXSCAN, 即掃描索引.
舉例2: 玩家稱號處理
// 查詢語句 db.User.find({TitleData:{$exists:true}}); // 增加稀疏索引 db.createIndex({TitleData:1}, {sparse:true, background:true});
之所以使用稀疏索引, 是因為大部分玩家是不具有稱號(TitleData字段), 使用稀疏索引時只會索引存在該字段的文檔, 通過對比, User集合中, 默認的 _id_ 索引大小138MB, 剛建立的稀疏索引TitleData_1大小僅為8KB(最小大小).
修改查詢語句由于項目代碼經過多手, 部分人員經驗不足, 代碼編寫時未考慮到性能問題.
因此需要改造部分服務端代碼, 這部分就是苦力活了, 逐個去修改, 屬于業務代碼優化.
舉例1: 篩選玩家
// 原查詢語句: 發放全服獎勵 db.User.find({}); // 修改后: 篩選僅最近30天登陸, 利用現有索引 {LastVisit:-1} db.User.find({LastVisit:{$gt: 30天前的時間戳}})
舉例2: 公會成員信息
// 原查詢語句: 在User集合中搜索指定公會成員 db.User.find({GuildId:xx}); // 修改后: 利用Guild集合中已有的GuildMembers成員列表, 逐個獲取公會成員數據 db.Guild.find({Id:xx}, {Id:1, GuildMembers:1}, 1); db.User.find({Id:{$in: [xx, xx, xx]}})定時器增加鎖
早期服務器數據量較小時, 每個分鐘級定時器都能順利在1分鐘內跑完, 但一旦出現慢查詢(未優化之前出現過十幾分鐘的), 上一個定時器未跑完, 下一個定時器又來了, 大量的慢查詢語句堆在MongoDB中導致整個數據庫被拖垮, 直接雪崩. 這是玩家反饋卡頓的最直接原因.
盡管經過上面優化后不會出現一個查詢1分鐘以上這種情況, 但是多個查詢累加起來, 也有可能超過1分鐘.
為了避免定時器腳本堆疊, 因此需要加個鎖, 避免出現問題.
具體的加鎖方案有:
memcached
redis
很簡單.
避免客戶端超時定時器通常是用于執行一些耗時操作, 除了上面的鎖問題外, 還有一個不可忽視的: 客戶端超時.
PHP中對MongoDB的一些操作, 默認是30秒, 比如 find() 操作一旦超過30秒會拋出 "超時異常", 然而此時該語句還在MongoDB實例中執行.由于定時任務未完成, 下一個定時器來的時候還是會繼續嘗試進行同樣的操作..
解決方案很簡單, 以php代碼為例
$mongo->selectCollection("xx")->find([...])->timeout(-1);更多的優化考慮
更換存儲引擎: 將 MMAPv1 替換為 WiredTrigger
使用集群(或簡單的主從), 將數據導出及數據備份等直接從庫上操作, 更進一步是改造服務端邏輯代碼, 將部分慢查詢應用到從庫中(主要不要)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/19559.html
摘要:年月日本文是關于記錄某次游戲服務端的性能優化此處涉及的技術包括引擎隨著游戲導入人數逐漸增加單個集合的文檔數已經超過經常有玩家反饋說卡特別是在服務器遷移后從核降到核卡頓更嚴重了遂開始排查問題確認服務器壓力首先使用命令查看總體情況此時占用不高 Last-Modified: 2019年6月13日11:08:19 本文是關于記錄某次游戲服務端的性能優化, 此處涉及的技術包括: MongoDB...
摘要:這是多處理器系統中,調度器用來分散任務到不同的機制,通常也被稱為處理器間中斷,。文章編寫計劃 待完成: 詳細介紹用到的各個工具 作者: 萬千鈞(祝星) 適合閱讀人群 文中的調優思路無論是php, java, 還是其他任何語言都是用. 如果你有php使用經驗, 那肯定就更好了 業務背景 框架及相應環境 laravel5.7, mysql5.7, redis5, nginx1.15 cento...
摘要:這是多處理器系統中,調度器用來分散任務到不同的機制,通常也被稱為處理器間中斷,。文章編寫計劃 待完成: 詳細介紹用到的各個工具 作者: 萬千鈞(祝星) 適合閱讀人群 文中的調優思路無論是php, java, 還是其他任何語言都是用. 如果你有php使用經驗, 那肯定就更好了 業務背景 框架及相應環境 laravel5.7, mysql5.7, redis5, nginx1.15 cento...
平日學習接觸過的網站積累,以每月的形式發布。2017年以前看這個網址:http://www.kancloud.cn/jsfron... 03月份前端資源分享 1. Javascript 175453545 Redux compose and middleware 源碼分析 深入 Promise(二)——進擊的 Promise Effective JavaScript leeheys blog -...
閱讀 1125·2021-11-24 10:21
閱讀 2561·2021-11-19 11:35
閱讀 1662·2019-08-30 15:55
閱讀 1293·2019-08-30 15:54
閱讀 1192·2019-08-30 15:53
閱讀 3498·2019-08-29 17:21
閱讀 3308·2019-08-29 16:12
閱讀 3412·2019-08-29 15:23