問(wèn)題現(xiàn)象
表面現(xiàn)象是系統(tǒng)登錄突然失效,排查原因發(fā)現(xiàn),使用redis查詢(xún)用戶(hù)信息異常,從而定位到redis問(wèn)題
if (PassWord.equals(dbPassWord)) {
map.put("rtn", 1);
map.put("value", validUser);
session.setAttribute("username", user.getUsername());
redisWarehouseControlUtil.addObjectData(user.getUsername(),user.getUsername(),30);
}
排查原因
我的redis使用的是華為云的redis分布式緩存服務(wù),所以在問(wèn)題排查方面,我們可以結(jié)合華為云提供的豐富的分析診斷工具來(lái)輔助排查解決問(wèn)題。
1、問(wèn)題定位到redis上,登陸redis服務(wù)器,發(fā)現(xiàn)服務(wù)器內(nèi)存使用率100%。
2、使用華為云的性能監(jiān)控功能,查詢(xún)指定時(shí)段的內(nèi)存使用率信息。發(fā)現(xiàn)“內(nèi)存利用率”指標(biāo)持續(xù)接近100%。查詢(xún)內(nèi)存使用率超過(guò)95%的時(shí)間段內(nèi),“已逐出的鍵數(shù)量”和“命令最大時(shí)延”,均呈現(xiàn)顯著上升趨勢(shì),表明存在內(nèi)存不足的問(wèn)題。
當(dāng)內(nèi)存不足時(shí),可能導(dǎo)致Key頻繁被逐出、響應(yīng)時(shí)間上升、QPS(每秒訪問(wèn)次數(shù))不穩(wěn)定等問(wèn)題,基本上redis服務(wù)已經(jīng)癱瘓。
3、先使用實(shí)例診斷功能,大體分析一下可能得問(wèn)題原因:主要還是內(nèi)存占用過(guò)高問(wèn)題。
4、使用華為云的緩存分析功能,執(zhí)行大Key掃描,發(fā)現(xiàn)另一個(gè)項(xiàng)目的ErrorMeterData?key,他是一個(gè)list隊(duì)列,竟然存儲(chǔ)數(shù)據(jù)占了2.6G,還有兩個(gè)key存儲(chǔ)數(shù)據(jù)占用了幾百M(fèi),就是這三個(gè)key把服務(wù)器的內(nèi)存占滿(mǎn)了。
5、分析查找原因:去代碼中查找ErrorMeterData?key對(duì)應(yīng)的功能,找到了問(wèn)題所在,這個(gè)key存儲(chǔ)的是解析出現(xiàn)異常的數(shù)據(jù)隊(duì)列,但問(wèn)題是,開(kāi)發(fā)這個(gè)功能的同事,并沒(méi)有給這個(gè)key設(shè)置過(guò)期時(shí)間,也沒(méi)有對(duì)這個(gè)異常數(shù)據(jù)隊(duì)列的數(shù)據(jù)進(jìn)行其他處理,就一直存在這個(gè)隊(duì)列中,隨著時(shí)間的增長(zhǎng),以及異常數(shù)據(jù)的日復(fù)一日的不斷累加,會(huì)導(dǎo)致存儲(chǔ)數(shù)據(jù)太多,終于內(nèi)存被占滿(mǎn)。這是一個(gè)非常嚴(yán)重的bug。
問(wèn)題就出在:redisMeterDataUtil.AddErrorMeterDataList(baseMessage);這一步
private void MeterDataExtractProcess() {
boolean rtn = false;
while (!needClose) {
// 普通表數(shù)據(jù)的解析
try {
BaseMessage baseMessage = redisMeterDataUtil.getMeterData();
if (baseMessage != null) {
if (!baseMessage.getFunctionCode().equals("") && baseMessage.getFunctionCode() != null) {
switch (baseMessage.getFunctionCode()) {
// 溫控面板解析 --主動(dòng)上傳 批量
case FunctionCode.UploadTcStateData:
UploadTcStateDataMessage tcStateDataMessage =
new UploadTcStateDataMessage(baseMessage);
rtn = tcStateDataService.addUploadTcStateDataDataMessage(tcStateDataMessage);
break;
// //根據(jù)表號(hào)讀取,單條 溫控面板解析
case FunctionCode.getTcStateData:
ReplyTcStateDataMessage replyTcStateDataMessage =
new ReplyTcStateDataMessage(baseMessage);
rtn = tcStateDataService.addTcStateDataMessage(replyTcStateDataMessage);
break;
default:
break;
}
if (rtn == false) {
// 解析方法存儲(chǔ)失敗,將數(shù)據(jù)添加到錯(cuò)誤隊(duì)列
redisMeterDataUtil.AddErrorMeterDataList(baseMessage);
}
} else {
// 若隊(duì)列數(shù)據(jù)為空,則線(xiàn)程休眠1s后繼續(xù)執(zhí)行
ThreadSleep(1000);
continue;
}
}
} catch (Exception e) {
logger.error("MeterDataExtractServer--表數(shù)據(jù)redis解析出錯(cuò)"+e.getMessage());
ThreadSleep(1000);
}
public void AddErrorMeterDataList(BaseMessage baseMessage) {
addData(ErrorMeterDataListSign, baseMessage);
}
private void addData(String type, Object data) {
String key = type;
redisTemplate.opsForList().leftPush(key, data);
}
6、如果你沒(méi)有使用華為云或者阿里云的專(zhuān)門(mén)的redis服務(wù),而是自己在服務(wù)器搭建的Redis服務(wù)。那么排查問(wèn)題的步驟和方法,大體可以分為幾步:
- 查詢(xún)?cè)\斷服務(wù)的CPU、內(nèi)存、硬盤(pán)、網(wǎng)絡(luò)等是否正常
- 查看日志分析異常問(wèn)題
- 如果是內(nèi)存占滿(mǎn)問(wèn)題,則可以在Redis-cli客戶(hù)端連接實(shí)例后,執(zhí)行大key掃描命令或者執(zhí)行過(guò)期key掃描(過(guò)期key掃描會(huì)對(duì)鍵空間進(jìn)行Redis的scan掃描,釋放內(nèi)存中已過(guò)期但是由于惰性刪除機(jī)制而沒(méi)有釋放的內(nèi)存空間),并查看key的內(nèi)存占用情況。并對(duì)內(nèi)存占用過(guò)大的key進(jìn)行處理。
如果你想掃描Redis實(shí)例中的大key,你可以使用
SCAN
命令結(jié)合TYPE
命令來(lái)獲取每個(gè)鍵的類(lèi)型,并根據(jù)鍵的類(lèi)型獲取其大小。以下是一個(gè)示例的命令:
bash復(fù)制代碼
redis-cli SCAN 0 MATCH * COUNT 1000 | while read key; do type=$(redis-cli TYPE $key); size=$(redis-cli -c GET $key | wc -c); echo "$key: $type, Size: $size"; done
這個(gè)命令將使用
SCAN
命令迭代整個(gè)數(shù)據(jù)庫(kù),并對(duì)每個(gè)鍵執(zhí)行TYPE
命令來(lái)獲取鍵的類(lèi)型。然后,對(duì)于字符串類(lèi)型的鍵,使用GET
命令獲取其值,并使用wc -c
命令計(jì)算其長(zhǎng)度。最后,將鍵、類(lèi)型和大小輸出到終端。另外,如果你想查看Redis實(shí)例的output buffer占用情況,你可以使用
CONFIG GET output-buffer-limit
命令來(lái)獲取output buffer的配置信息。該命令將返回output buffer的配置參數(shù),包括類(lèi)型、大小和閾值。請(qǐng)注意,上述命令中的
redis-cli -c GET $key
是用于獲取字符串類(lèi)型的鍵的大小。對(duì)于其他類(lèi)型的鍵,你可能需要使用其他命令或方法來(lái)獲取其大小。
處理措施
1、為內(nèi)存占用過(guò)大的key設(shè)置過(guò)期時(shí)間,這樣數(shù)據(jù)就不會(huì)一直存儲(chǔ)在隊(duì)列中
(1)比較緊急想要恢復(fù)redis,且隊(duì)列中的數(shù)據(jù)不重要,則可以直接鏈接redis,執(zhí)行命令
EXPIRE key seconds
:設(shè)置鍵的過(guò)期時(shí)間(以秒為單位),過(guò)期后鍵將被自動(dòng)刪除。或者
DEL key
:刪除指定鍵
(2)在代碼中為key設(shè)置過(guò)期時(shí)間
/**
* 設(shè)置設(shè)備緩存過(guò)期時(shí)間(分鐘)
* @param type 設(shè)備分類(lèi)
*/
private void setExpireTime(String type,int cacheTime) {
String key = type;
redisTemplate.expire(key,cacheTime,TimeUnit.MINUTES);
}
/**
* 設(shè)置表數(shù)據(jù)緩存失效時(shí)間list集合
*/
public void setMeterInfoExpire() {
setExpireTime(MeterDataListSign,deviceCacheTime);
}
2、業(yè)務(wù)邏輯上將這個(gè)異常數(shù)據(jù)隊(duì)列的數(shù)據(jù),重新返回處理隊(duì)列,設(shè)置返回次數(shù),如果超過(guò)三次以上,還是沒(méi)有被正常隊(duì)列處理掉,則將異常數(shù)據(jù)持久化,并刪除redis中的該異常數(shù)據(jù)。
我的實(shí)際業(yè)務(wù)中,異常數(shù)據(jù)沒(méi)有重回隊(duì)列處理的必要了,所以我的業(yè)務(wù)代碼中,直接不在用redis隊(duì)列存儲(chǔ)異常數(shù)據(jù),而是直接將異常數(shù)據(jù)持久化存儲(chǔ)到mongodb中。
if (rtn == false) {
// 解析方法存儲(chǔ)失敗,將數(shù)據(jù)添加到錯(cuò)誤隊(duì)列----不再存在redis,直接持久化存儲(chǔ)到mongodb
//redisMeterDataUtil.AddErrorMeterDataList(baseMessage);
tcErrorMessageHistoryUtil.addMessage(baseMessage);
}
3、設(shè)置key的過(guò)期時(shí)間后,過(guò)了一段時(shí)間內(nèi)存恢復(fù)正常
文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-771558.html
總結(jié)
在使用redis的對(duì)象或者list隊(duì)列等實(shí)例時(shí),要記得給key設(shè)置過(guò)期時(shí)間,避免數(shù)據(jù)一直堆積無(wú)法釋放。對(duì)于重要的異常數(shù)據(jù)隊(duì)列的數(shù)據(jù),要進(jìn)行業(yè)務(wù)處理:重回隊(duì)列或數(shù)據(jù)持久化。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-771558.html
到了這里,關(guān)于Redis內(nèi)存使用率高,內(nèi)存不足問(wèn)題排查和解決的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!