生活随笔
收集整理的這篇文章主要介紹了
美团在Redis上踩过的一些坑-2.bgrewriteaof问题
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
轉載請注明出處哈:http://carlosfu.iteye.com/blog/2254154
?? ?
?一、背景
1. AOF:
? ? Redis的AOF機制有點類似于Mysql binlog,是Redis的提供的一種持久化方式(另一種是RDB),它會將所有的寫命令按照一定頻率(no, always, every seconds)寫入到日志文件中,當Redis停機重啟后恢復數(shù)據(jù)庫。
? ? ?
?
2. AOF重寫:
? ? ?(1) 隨著AOF文件越來越大,里面會有大部分是重復命令或者可以合并的命令(100次incr = set key 100)
? ? ?(2) 重寫的好處:減少AOF日志尺寸,減少內存占用,加快數(shù)據(jù)庫恢復時間。
? ??
?
?
?
二、單機多實例可能存在Swap和OOM的隱患:
? ? 由于Redis的單線程模型,理論上每個redis實例只會用到一個CPU, 也就是說可以在一臺多核的服務器上部署多個實例(實際就是這么做的)。但是Redis的AOF重寫是通過fork出一個Redis進程來實現(xiàn)的,所以有經(jīng)驗的Redis開發(fā)和運維人員會告訴你,在一臺服務器上要預留一半的內存(防止出現(xiàn)AOF重寫集中發(fā)生,出現(xiàn)swap和OOM)。
? ??
?
?
?
三、最佳實踐
1. meta信息:作為一個redis云系統(tǒng),需要記錄各個維度的數(shù)據(jù),比如:業(yè)務組、機器、實例、應用、負責人多個維度的數(shù)據(jù),相信每個Redis的運維人員都應該有這樣的持久化數(shù)據(jù)(例如Mysql),一般來說還有一些運維界面,為自動化和運維提供依據(jù)
? ? 例如如下:
?
?
? ??
?
2. AOF的管理方式:
?(1) 自動:讓每個redis決定是否做AOF重寫操作(根據(jù)auto-aof-rewrite-percentage和auto-aof-rewrite-min-size兩個參數(shù)):
??
??
?(2) crontab: 定時任務,可能仍然會出現(xiàn)多個redis實例,屬于一種折中方案。
?
?(3) remote集中式:
? ? ? ?最終目標是一臺機器一個時刻,只有一個redis實例進行AOF重寫。
? ? ? ?具體做法其實很簡單,以機器為單位,輪詢每個機器的實例,如果滿足條件就運行(比如currentSize和baseSize滿足什么關系)bgrewriteaof命令。
? ? ? ?期間可以監(jiān)控發(fā)生時間、耗時、頻率、尺寸的前后變化? ? ? ? ? ??
| 策略 | 優(yōu)點 | 缺點 |
| 自動 | 無需開發(fā) | 1. 有可能出現(xiàn)(無法預知)上面提到的Swap和OOM 2. 出了問題,處理起來其實更費時間。 |
| AOF控制中心(remote集中式) | 1. 防止上面提到Swap和OOM。 2. 能夠收集更多的數(shù)據(jù)(aof重寫的發(fā)生時間、耗時、頻率、尺寸的前后變化),更加有利于運維和定位問題(是否有些機器的實例需要拆分)。 | 控制中心需要開發(fā)。 |
?
一臺機器輪詢執(zhí)行bgRewriteAof代碼示例:
Java代碼??
package?com.sohu.cache.inspect.impl;?? ?? import?com.sohu.cache.alert.impl.BaseAlertService;?? import?com.sohu.cache.entity.InstanceInfo;?? import?com.sohu.cache.inspect.InspectParamEnum;?? import?com.sohu.cache.inspect.Inspector;?? import?com.sohu.cache.util.IdempotentConfirmer;?? import?com.sohu.cache.util.TypeUtil;?? import?org.apache.commons.collections.MapUtils;?? import?org.apache.commons.lang.StringUtils;?? import?redis.clients.jedis.Jedis;?? ?? import?java.util.Collections;?? import?java.util.LinkedHashMap;?? import?java.util.List;?? import?java.util.Map;?? import?java.util.concurrent.TimeUnit;?? ?? ?? public?class?RedisIsolationPersistenceInspector?extends?BaseAlertService?implements?Inspector?{?? ?? ????public?static?final?int?REDIS_DEFAULT_TIME?=?5000;?? ?? ????@Override?? ????public?boolean?inspect(Map<InspectParamEnum,?Object>?paramMap)?{?? ?????????? ????????final?String?host?=?MapUtils.getString(paramMap,?InspectParamEnum.SPLIT_KEY);?? ????????List<InstanceInfo>?list?=?(List<InstanceInfo>)?paramMap.get(InspectParamEnum.INSTANCE_LIST);?? ?????????? ????????for?(InstanceInfo?info?:?list)?{?? ????????????final?int?port?=?info.getPort();?? ????????????final?int?type?=?info.getType();?? ????????????int?status?=?info.getStatus();?? ?????????????? ????????????if?(status?!=?1)?{?? ????????????????continue;?? ????????????}?? ????????????if?(TypeUtil.isRedisDataType(type))?{?? ????????????????Jedis?jedis?=?new?Jedis(host,?port,?REDIS_DEFAULT_TIME);?? ????????????????try?{?? ?????????????????????? ????????????????????Map<String,?String>?persistenceMap?=?parseMap(jedis);?? ????????????????????if?(persistenceMap.isEmpty())?{?? ????????????????????????logger.error("{}:{}?get?persistenceMap?failed",?host,?port);?? ????????????????????????continue;?? ????????????????????}?? ?????????????????????? ????????????????????if?(!isAofEnabled(persistenceMap))?{?? ????????????????????????continue;?? ????????????????????}?? ?????????????????????? ????????????????????long?aofCurrentSize?=?MapUtils.getLongValue(persistenceMap,?"aof_current_size");?? ????????????????????long?aofBaseSize?=?MapUtils.getLongValue(persistenceMap,?"aof_base_size");?? ?????????????????????? ????????????????????long?aofThresholdSize?=?(long)?(aofBaseSize?*?1.6);?? ????????????????????double?percentage?=?getPercentage(aofCurrentSize,?aofBaseSize);?? ?????????????????????? ????????????????????if?(aofCurrentSize?>=?aofThresholdSize?&&?aofCurrentSize?>?(64?*?1024?*?1024))?{?? ?????????????????????????? ????????????????????????boolean?isInvoke?=?invokeBgRewriteAof(jedis);?? ????????????????????????if?(!isInvoke)?{?? ????????????????????????????logger.error("{}:{}?invokeBgRewriteAof?failed",?host,?port);?? ????????????????????????????continue;?? ????????????????????????}?else?{?? ????????????????????????????logger.warn("{}:{}?invokeBgRewriteAof?started?percentage={}",?host,?port,?percentage);?? ????????????????????????}?? ?????????????????????????? ????????????????????????while?(true)?{?? ????????????????????????????try?{?? ?????????????????????????????????? ????????????????????????????????TimeUnit.SECONDS.sleep(1);?? ????????????????????????????????Map<String,?String>?loopMap?=?parseMap(jedis);?? ????????????????????????????????Integer?aofRewriteInProgress?=?MapUtils.getInteger(loopMap,?"aof_rewrite_in_progress",?null);?? ????????????????????????????????if?(aofRewriteInProgress?==?null)?{?? ????????????????????????????????????logger.error("loop?watch:{}:{}?return?failed",?host,?port);?? ????????????????????????????????????break;?? ????????????????????????????????}?else?if?(aofRewriteInProgress?<=?0)?{?? ?????????????????????????????????????? ????????????????????????????????????logger.warn("{}:{}?bgrewriteaof?Done?lastSize:{}Mb,currentSize:{}Mb",?host,?port,?? ????????????????????????????????????????????getMb(aofCurrentSize),?? ????????????????????????????????????????????getMb(MapUtils.getLongValue(loopMap,?"aof_current_size")));?? ????????????????????????????????????break;?? ????????????????????????????????}?else?{?? ?????????????????????????????????????? ????????????????????????????????????TimeUnit.SECONDS.sleep(1);?? ????????????????????????????????}?? ????????????????????????????}?catch?(Exception?e)?{?? ????????????????????????????????logger.error(e.getMessage(),?e);?? ????????????????????????????}?? ????????????????????????}?? ????????????????????}?else?{?? ????????????????????????if?(percentage?>?50D)?{?? ????????????????????????????long?currentSize?=?getMb(aofCurrentSize);?? ????????????????????????????logger.info("checked?{}:{}?aof?increase?percentage:{}%?currentSize:{}Mb",?host,?port,?? ????????????????????????????????????percentage,?currentSize?>?0???currentSize?:?"<1");?? ????????????????????????}?? ????????????????????}?? ????????????????}?finally?{?? ????????????????????jedis.close();?? ????????????????}?? ????????????}?? ????????}?? ????????return?true;?? ????}?? ?? ????private?long?getMb(long?bytes)?{?? ????????return?(long)?(bytes?/?1024?/?1024);?? ????}?? ?? ????private?boolean?isAofEnabled(Map<String,?String>?infoMap)?{?? ????????Integer?aofEnabled?=?MapUtils.getInteger(infoMap,?"aof_enabled",?null);?? ????????return?aofEnabled?!=?null?&&?aofEnabled?==?1;?? ????}?? ?? ????private?double?getPercentage(long?aofCurrentSize,?long?aofBaseSize)?{?? ????????if?(aofBaseSize?==?0)?{?? ????????????return?0.0D;?? ????????}?? ????????String?format?=?String.format("%.2f",?(Double.valueOf(aofCurrentSize?-?aofBaseSize)?*?100?/?aofBaseSize));?? ????????return?Double.parseDouble(format);?? ????}?? ?? ????private?Map<String,?String>?parseMap(final?Jedis?jedis)?{?? ????????final?StringBuilder?builder?=?new?StringBuilder();?? ????????boolean?isInfo?=?new?IdempotentConfirmer()?{?? ????????????@Override?? ????????????public?boolean?execute()?{?? ????????????????String?persistenceInfo?=?null;?? ????????????????try?{?? ????????????????????persistenceInfo?=?jedis.info("Persistence");?? ????????????????}?catch?(Exception?e)?{?? ????????????????????logger.warn(e.getMessage()?+?"-{}:{}",?jedis.getClient().getHost(),?jedis.getClient().getPort(),?? ????????????????????????????e.getMessage());?? ????????????????}?? ????????????????boolean?isOk?=?StringUtils.isNotBlank(persistenceInfo);?? ????????????????if?(isOk)?{?? ????????????????????builder.append(persistenceInfo);?? ????????????????}?? ????????????????return?isOk;?? ????????????}?? ????????}.run();?? ????????if?(!isInfo)?{?? ????????????logger.error("{}:{}?info?Persistence?failed",?jedis.getClient().getHost(),?jedis.getClient().getPort());?? ????????????return?Collections.emptyMap();?? ????????}?? ????????String?persistenceInfo?=?builder.toString();?? ????????if?(StringUtils.isBlank(persistenceInfo))?{?? ????????????return?Collections.emptyMap();?? ????????}?? ????????Map<String,?String>?map?=?new?LinkedHashMap<String,?String>();?? ????????String[]?array?=?persistenceInfo.split("\r\n");?? ????????for?(String?line?:?array)?{?? ????????????String[]?cells?=?line.split(":");?? ????????????if?(cells.length?>?1)?{?? ????????????????map.put(cells[0],?cells[1]);?? ????????????}?? ????????}?? ?? ????????return?map;?? ????}?? ?? ????public?boolean?invokeBgRewriteAof(final?Jedis?jedis)?{?? ????????return?new?IdempotentConfirmer()?{?? ????????????@Override?? ????????????public?boolean?execute()?{?? ????????????????try?{?? ????????????????????String?response?=?jedis.bgrewriteaof();?? ????????????????????if?(response?!=?null?&&?response.contains("rewriting?started"))?{?? ????????????????????????return?true;?? ????????????????????}?? ????????????????}?catch?(Exception?e)?{?? ????????????????????String?message?=?e.getMessage();?? ????????????????????if?(message.contains("rewriting?already"))?{?? ????????????????????????return?true;?? ????????????????????}?? ????????????????????logger.error(message,?e);?? ????????????????}?? ????????????????return?false;?? ????????????}?? ????????}.run();?? ????}?? }??
?
?
?
?
附圖一張:
?
?
總結
以上是生活随笔為你收集整理的美团在Redis上踩过的一些坑-2.bgrewriteaof问题的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。