? ? 收到來自運(yùn)營方消息,外網(wǎng)玩家無限回檔,關(guān)鍵回檔的都是排行榜前幾名。瞬間菊花一緊,R君甚有吃我之勢。天天夾著尾巴做人。不能光夾著,事情還是解決的。自此開始無盡痛苦之旅。
將玩家登錄日志,經(jīng)驗(yàn)增長日志,服務(wù)器運(yùn)行日志,MYSQL存儲(chǔ)日志,綜合日志分析得出如下:
? ? 玩家本人日常操作,所有的一切正常。證明存儲(chǔ)不當(dāng),倒導(dǎo)的。
? ? 玩家下線后, MYSQL數(shù)據(jù)被莫名其妙修改,服務(wù)器運(yùn)行日志里頭該玩家有被離線加載過,作者與H君,對整個(gè)人游戲離線加載的代碼,進(jìn)行多次反復(fù)查驗(yàn),如果這里出了問題那玩家應(yīng)該早就回檔了。讀寫玩家肯定沒得問題,那么這份神秘的數(shù)據(jù)來自于哪里呢,Game服務(wù)器Player有多個(gè)?共享內(nèi)存里頭有殘留?redis里頭有多份player cache? ? Game服所有代碼都是用UUID作KEY,不可能存在兩份,共享內(nèi)存爆了? 不可能,如果爆了,出現(xiàn)問題也不可能 都是排行榜上的。redis操作失誤?很簡單的 set get用錯(cuò)了?關(guān)鍵是key validtime 也才3分鐘,玩家加載與釋放有多處都是超過三分鐘,KEY不斷被刷新的可能 性,被否定。
? ? 自此,作者與H君,陷入無限痛苦之中,那份神秘的玩家數(shù)據(jù)來自于何處。代碼看不出任何BUG,redis,mysql基本上也不會(huì)出問題。 ?
? ? 對mysql所有游戲服數(shù)據(jù)庫進(jìn)行全局查詢,該玩家的數(shù)據(jù),竟然存在于多個(gè)游戲服數(shù)據(jù)庫里頭呢? 結(jié)合最近提交的功能。終于發(fā)現(xiàn)問題所在。
分析結(jié)論:
? ? ?? 游戲服務(wù)器框架? 如下 多Gate->GameServer->DataServer->redis作為緩存->mysql為最終數(shù)存儲(chǔ)
? ? ?? 功能描述:查看跨服排行榜 玩家裝備,卡牌,信息 ??
? ? ? ? 代碼流程:client-> 發(fā)送被查詢玩家UUID->Gate->GameServer? 查找內(nèi)存里頭有沒有該玩家->DataServer 首先查詢r(jià)edis,然后查詢mysql
? ? ? ? ? ? ? ? ? ? ? ?? 如果都查不到數(shù)據(jù),gameserver通知中心服務(wù)器控制器,查詢玩家。并返回?cái)?shù)據(jù)
? ? ? ? 問題原因:因?yàn)槎鄠€(gè)DataServer共用一個(gè)redis,而 redis緩存是如此設(shè)計(jì)的 ?get player:uuid ? set player:uuid , 假如1服A玩家查詢2服B玩家,
1服和2服在同一個(gè)物理機(jī)上,共用同一個(gè)redis, 1服的A玩家是能夠通GameServer 1服查詢并加載 B玩家,從而導(dǎo)致? B玩家數(shù)據(jù)存在于多個(gè)GameServer中,當(dāng)GameServer 定時(shí)刷新時(shí),清除長時(shí)間不上線的玩家時(shí),就會(huì)把數(shù)據(jù)寫回redis, 這個(gè)時(shí)候redis里頭的B玩家數(shù)據(jù),就不一定是來自于2服的新數(shù)據(jù),很可能 新數(shù)據(jù)被? 1服里頭的B玩家老數(shù)據(jù)頂替掉。定時(shí)存mysql啟動(dòng)線程執(zhí)行時(shí),這份老數(shù)據(jù)就會(huì)被寫入mysql從而導(dǎo)致玩家回檔。
? ?? 修改方案:get player:uuid ? set player:uuid ? 改成?get player:serverid:uuid ? set player:serverid:uuid
總結(jié):
? ? 在使用 redis時(shí),由其是同一程序,多個(gè)進(jìn)程啟動(dòng)時(shí),設(shè)計(jì)key時(shí)最好帶上進(jìn)程唯一標(biāo)識碼,而且在數(shù)量級大,還能減少查詢時(shí)間復(fù)雜度