在Redis中寻找高内存使用率的扩展

在Redis中寻找高内存使用率的扩展,redis,jedis,Redis,Jedis,我们在Redis中存储了大量数据。事实上,我们在Redis中存储了大量的键以及与每个键相关的少量数据。键长8字节,数据长8字节(长值)。有10亿把钥匙(是的,10亿) 给定Redis存储的结构,据我所知(并且)给定8个字节的密钥,头的开销为8个字节,密钥末尾的null为1个字节。这是17个字节。假设这四舍五入到至少24个字节,加上8个字节的长值将得到32个字节 十亿个密钥将是32GB。测量的使用量为158GB。当然,有间接费用,但5:1的比率似乎很大。有人能解释一下这一点,或者指出减少内存使用的

我们在Redis中存储了大量数据。事实上,我们在Redis中存储了大量的键以及与每个键相关的少量数据。键长8字节,数据长8字节(长值)。有10亿把钥匙(是的,10亿)

给定Redis存储的结构,据我所知(并且)给定8个字节的密钥,头的开销为8个字节,密钥末尾的null为1个字节。这是17个字节。假设这四舍五入到至少24个字节,加上8个字节的长值将得到32个字节

十亿个密钥将是32GB。测量的使用量为158GB。当然,有间接费用,但5:1的比率似乎很大。有人能解释一下这一点,或者指出减少内存使用的方法吗

我已经包括了基于绝地武士的测试计划

import java.security.SecureRandom;
import java.text.DecimalFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.exceptions.JedisClusterMaxRedirectionsException;

public class Test8byteKeys {
    protected static JedisCluster cluster = null;
    protected static final ExecutorService executor;

    protected static volatile boolean shuttingDown = false;

    private static final int AVAILABLE_PROCESSORS = Runtime.getRuntime().availableProcessors();

    static {
        final int cores = Math.max(4, (AVAILABLE_PROCESSORS * 3) / 4);
        executor = new ThreadPoolExecutor(cores, cores, //
                15, TimeUnit.SECONDS, //
                new LinkedBlockingQueue<>(cores), //
                new ThreadPoolExecutor.CallerRunsPolicy());

        System.out.println("Running with " + cores + " threads");
    }

    static private GenericObjectPoolConfig getPoolConfiguration() {
        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();

        poolConfig.setLifo(true);
        poolConfig.setTestOnBorrow(true);
        poolConfig.setTestOnReturn(false);
        poolConfig.setBlockWhenExhausted(true);
        poolConfig.setMinIdle(1);
        poolConfig.setMaxTotal(101);
        poolConfig.setTestWhileIdle(false);
        poolConfig.setSoftMinEvictableIdleTimeMillis(3000L);
        poolConfig.setNumTestsPerEvictionRun(5);
        poolConfig.setTimeBetweenEvictionRunsMillis(5000L);
        poolConfig.setJmxEnabled(true);

        return poolConfig;
    }

    private static void connectToCluster() {
        try {
            Set<HostAndPort> nodes = new HashSet<>();
            String hap /* host and port */ = System.getProperty("hap", null);
            if (hap == null) {
                System.err.println("You must supply the host and port of a master in the cluster on the command line");
                System.err.println("java -Dhap=<host:port> -jar <jar> ");
                System.exit(1);
            }

            String[] parts = hap.split(":"); // assume ipv4 address
            nodes.add(new HostAndPort(parts[0].trim(), Integer.valueOf(parts[1].trim())));

            System.out.println("Connecting to " + hap);
            cluster = new JedisCluster(nodes, getPoolConfiguration());
        }
        catch (Exception e) {
            System.err.println("Could not connect to redis -- " + e.getMessage());
            System.exit(1);
        }
    }

    private static final Thread shutdown = new Thread(new Runnable() {
        // Clean up at exit
        @Override
        public void run() {
            shuttingDown = true;

            System.out.println((new Date()).toString() + "\t" + "Executor shutdown in progress");

            try {
                executor.shutdown();
                executor.awaitTermination(10L, TimeUnit.SECONDS);
            }
            catch (Exception e) {
                // ignore
            }
            finally {
                try {
                    if (!executor.isShutdown()) {
                        executor.shutdownNow();
                    }
                }
                catch (Exception e) {
                    //ignore
                }
            }

            try {
                cluster.close();
            }
            catch (Exception e) {
                System.err.println("cluster disconnection failure: " + e);
            }
            finally {
                //
            }

            System.out.println((new Date()).toString() + "\t" + "shutdown complete.");
        }
    });

    final static char[] CHARACTERS = { //
            '0', '1', '2', '3', '4', '5', //
            '6', '7', '8', '9', 'a', 'b', //
            'c', 'd', 'e', 'f', 'g', 'h', //
            'i', 'j', 'k', 'l', 'm', 'n', //
            'o', 'p', 'q', 'r', 's', 't', //
            'u', 'v', 'w', 'x', 'y', 'z', //
            'A', 'B', 'C', 'D', 'E', 'F', //
            'G', 'H', 'I', 'J', 'K', 'L', //
            'M', 'N', 'O', 'P', 'Q', 'R', //
            'S', 'T', 'U', 'V', 'W', 'X', //
            'Y', 'Z', '#', '@' //
    };

    protected final static byte[] KEY_EXISTS_MARKER = { '1' };

    static class Runner implements Runnable {
        private byte[] key = null;

        public Runner(byte[] key) {
            this.key = key;
        }

        @Override
        public void run() {
            if (!shuttingDown) {
                try {
                    cluster.set(key, KEY_EXISTS_MARKER);
                    cluster.expire(key, 60 * 60 * 48); // for test purposes, only keep around for 2 days
                }
                catch (JedisClusterMaxRedirectionsException e) {
                    System.err.println(
                            (new Date()).toString() + "\tIGNORING\t" + e + "\t" + "For key " + new String(key));
                }
                catch (Exception e) {
                    System.err.println((new Date()).toString() + "\t" + e + "\t" + "For key " + new String(key));
                    e.printStackTrace();
                    System.exit(1);
                }
            }
        }
    }

    public static void main(String[] args) {
        SecureRandom random = new SecureRandom();
        DecimalFormat decimal = new DecimalFormat("#,##0");
        final byte[] randomBytes = new byte[8];

        connectToCluster();

        Runtime.getRuntime().addShutdownHook(shutdown);

        System.out.println((new Date()) + " Starting test");

        for (int i = 0; i < 1000000000; i++) {
            random.nextBytes(randomBytes);
            final byte[] key = new byte[8];
            for (int j = 0; j < randomBytes.length; j++)
                key[j] = (byte) (CHARACTERS[((randomBytes[j] & 0xFF)) % CHARACTERS.length] & 0xFF);

            try {
                if (shuttingDown) {
                    System.err.println((new Date()).toString() + "\t" + "Main loop terminating due to shutdown");
                    break;
                }

                if (i % 1000000 == 0)
                    System.out.println((new Date()).toString() + "\t" + decimal.format(i));

                try {
                    executor.submit(new Runner(key));
                }
                catch (Exception e) {
                    System.err.println((new Date()).toString() + "\t" + e);
                }
            }
            catch (Exception e) {
                System.err.println("Failed to set key " + new String(key) + " -- " + e);
            }
        }

        if (!shuttingDown) {
            System.out.println((new Date()) + " Done");
            System.exit(0);
        }
    }
}
导入java.security.SecureRandom;
导入java.text.DecimalFormat;
导入java.util.Date;
导入java.util.HashSet;
导入java.util.Set;
导入java.util.concurrent.ExecutorService;
导入java.util.concurrent.LinkedBlockingQueue;
导入java.util.concurrent.ThreadPoolExecutor;
导入java.util.concurrent.TimeUnit;
导入org.apache.commons.pool2.impl.GenericObjectPoolConfig;
导入redis.clients.jedis.HostAndPort;
导入redis.clients.jedis.JedisCluster;
导入redis.clients.jedis.exceptions.JedisClusterMaxRedirectionsException;
公共类Test8Byte密钥{
受保护的静态JedisCluster群集=null;
受保护的静态最终执行器服务执行器;
受保护的静态易失性布尔关闭=false;
私有静态final int AVAILABLE_PROCESSORS=Runtime.getRuntime().availableProcessors();
静止的{
最终整数核=数学最大值(4(可用处理器*3)/4);
executor=新线程池executor(核心、核心、//
15,时间单位。秒//
新的LinkedBlockingQueue(核心)//
新的ThreadPoolExecutor.CallerRunPolicy());
System.out.println(“使用“+内核+线程运行”);
}
静态专用GenericObjectPoolConfig getPoolConfiguration()的{
GenericObjectPoolConfig-poolConfig=新的GenericObjectPoolConfig();
poolConfig.setLifo(true);
poolConfig.settstonbrow(true);
poolConfig.settstonReturn(false);
poolConfig.setBlockWhenExhausted(true);
poolConfig.setMinIdle(1);
poolConfig.setMaxTotal(101);
poolConfig.setTestWhileIdle(false);
poolConfig.SetSoftmineVictobleIDletimillis(3000L);
boolconfig.setNumtestsPervictionRun(5);
poolConfig.SetTimeBetweenVictionRunsmillis(5000L);
poolConfig.setJmxEnabled(true);
返回池配置;
}
私有静态void connectToCluster(){
试一试{
Set nodes=new HashSet();
字符串hap/*主机和端口*/=System.getProperty(“hap”,null);
如果(hap==null){
System.err.println(“必须在命令行上提供集群中主机的主机和端口”);
System.err.println(“java-Dhap=-jar”);
系统出口(1);
}
String[]parts=hap.split(“:”)//假定为ipv4地址
添加(新主机端口(部件[0].trim(),Integer.valueOf(部件[1].trim()));
System.out.println(“连接到”+hap);
cluster=新的JedisCluster(节点,getPoolConfiguration());
}
捕获(例外e){
System.err.println(“无法连接到redis--”+e.getMessage());
系统出口(1);
}
}
私有静态最终线程关闭=新线程(new Runnable(){
//出口清理
@凌驾
公开募捐{
关机=真;
System.out.println((新日期()).toString()+“\t”+“执行器正在关闭”);
试一试{
executor.shutdown();
执行器等待终止(10L,时间单位秒);
}
捕获(例外e){
//忽略
}
最后{
试一试{
如果(!executor.isShutdown()){
执行者。关机现在();
}
}
捕获(例外e){
//忽略
}
}
试一试{
cluster.close();
}
捕获(例外e){
System.err.println(“集群断开故障:+e”);
}
最后{
//
}
System.out.println((新日期()).toString()+“\t”+“关机完成”);
}
});
最终静态字符[]字符={//
'0', '1', '2', '3', '4', '5', //
‘6’、‘7’、‘8’、‘9’、‘a’、‘b’//
‘c’、‘d’、‘e’、‘f’、‘g’、‘h’//
‘i’、‘j’、‘k’、‘l’、‘m’、‘n’//
‘o’、‘p’、‘q’、‘r’、‘s’、‘t’//
‘u’、‘v’、‘w’、‘x’、‘y’、‘z’//
‘A’、‘B’、‘C’、‘D’、‘E’、‘F’//
‘G’、‘H’、‘I’、‘J’、‘K’、‘L’//
‘M’、‘N’、‘O’、‘P’、‘Q’、‘R’//
‘S’、‘T’、‘U’、‘V’、‘W’、‘X’//
“Y”、“Z”、“#”和“@”//
};
受保护的最终静态字节[]键\u存在\u标记={'1'};
静态类运行器实现可运行{
私有字节[]密钥=null;
公共运行程序(字节[]键){
this.key=key;
}
@凌驾
公开募捐{
如果(!关机){
试一试{
cluster.set(key,key\u存在\u标记);
cluster.expire(键,60*60*48);//出于测试目的,仅保留2天
}
捕获(JedisclusterMaxRedirectionsE异常){
System.err.println(
(新日期()).toString()+“\tIGNORING\t”+e+