您的位置:首页 > 数据库 > Redis

2.bgrewriteaof问题

2016-03-12 21:37 459 查看
 一、背景

1. AOF:

    Redis的AOF机制有点类似于Mysql binlog,是Redis的提供的一种持久化方式(另一种是RDB),它会将所有的写命令按照一定频率(no, always, every seconds)写入到日志文件中,当Redis停机重启后恢复数据库。

     


 

2. AOF重写:

     (1) 随着AOF文件越来越大,里面会有大部分是重复命令或者可以合并的命令(100次incr = set key 100)

     (2) 重写的好处:减少AOF日志尺寸,减少内存占用,加快数据库恢复时间。

    


 

 

 

二、单机多实例可能存在Swap和OOM的隐患:

    由于Redis的单线程模型,理论上每个redis实例只会用到一个CPU, 也就是说可以在一台多核的服务器上部署多个实例(实际就是这么做的)。但是Redis的AOF重写是通过fork出一个Redis进程来实现的,所以有经验的Redis开发和运维人员会告诉你,在一台服务器上要预留一半的内存(防止出现AOF重写集中发生,出现swap和OOM)。

    


 

 

 

三、最佳实践

1. meta信息:作为一个redis云系统,需要记录各个维度的数据,比如:业务组、机器、实例、应用、负责人多个维度的数据,相信每个Redis的运维人员都应该有这样的持久化数据(例如Mysql),一般来说还有一些运维界面,为自动化和运维提供依据

    例如如下:



 



 



    

 

2. AOF的管理方式:

 (1) 自动:让每个redis决定是否做AOF重写操作(根据auto-aof-rewrite-percentage和auto-aof-rewrite-min-size两个参数):

  


  

 (2) crontab: 定时任务,可能仍然会出现多个redis实例,属于一种折中方案。

 

 (3) remote集中式:

       最终目标是一台机器一个时刻,只有一个redis实例进行AOF重写。

       具体做法其实很简单,以机器为单位,轮询每个机器的实例,如果满足条件就运行(比如currentSize和baseSize满足什么关系)bgrewriteaof命令。

       期间可以监控发生时间、耗时、频率、尺寸的前后变化            



策略优点缺点
自动无需开发1. 有可能出现(无法预知)上面提到的Swap和OOM

2. 出了问题,处理起来其实更费时间。
AOF控制中心(remote集中式)1. 防止上面提到Swap和OOM。

2. 能够收集更多的数据(aof重写的发生时间、耗时、频率、尺寸的前后变化),更加有利于运维和定位问题(是否有些机器的实例需要拆分)。
控制中心需要开发。
 

一台机器轮询执行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) {  

        // 某台机器和机器下所有redis实例  

        final String host = MapUtils.getString(paramMap, InspectParamEnum.SPLIT_KEY);  

        List<InstanceInfo> list = (List<InstanceInfo>) paramMap.get(InspectParamEnum.INSTANCE_LIST);  

        // 遍历所有的redis实例  

        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 {  

                    // 从redis info中索取持久化信息  

                    Map<String, String> persistenceMap = parseMap(jedis);  

                    if (persistenceMap.isEmpty()) {  

                        logger.error("{}:{} get persistenceMap failed", host, port);  

                        continue;  

                    }  

                    // 如果正在进行aof就不做任何操作,理论上要等待它完毕,否则  

                    if (!isAofEnabled(persistenceMap)) {  

                        continue;  

                    }  

                    // 上一次aof重写后的尺寸和当前aof的尺寸  

                    long aofCurrentSize = MapUtils.getLongValue(persistenceMap, "aof_current_size");  

                    long aofBaseSize = MapUtils.getLongValue(persistenceMap, "aof_base_size");  

                    // 阀值大于60%  

                    long aofThresholdSize = (long) (aofBaseSize * 1.6);  

                    double percentage = getPercentage(aofCurrentSize, aofBaseSize);  

                    // 大于60%且超过60M  

                    if (aofCurrentSize >= aofThresholdSize && aofCurrentSize > (64 * 1024 * 1024)) {  

                        // bgRewriteAof 异步操作。  

                        boolean isInvoke = invokeBgRewriteAof(jedis);  

                        if (!isInvoke) {  

                            logger.error("{}:{} invokeBgRewriteAof failed", host, port);  

                            continue;  

                        } else {  

                            logger.warn("{}:{} invokeBgRewriteAof started percentage={}", host, port, percentage);  

                        }  

                        // 等待Aof重写成功(bgRewriteAof是异步操作)  

                        while (true) {  

                            try {  

                                // before wait 1s  

                                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) {  

                                    // bgrewriteaof Done  

                                    logger.warn("{}:{} bgrewriteaof Done lastSize:{}Mb,currentSize:{}Mb", host, port,  

                                            getMb(aofCurrentSize),  

                                            getMb(MapUtils.getLongValue(loopMap, "aof_current_size")));  

                                    break;  

                                } else {  

                                    // wait 1s  

                                    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