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