Redis存储优化--小对象压缩

小对象压缩
  • Redis是一种内存数据库,内存是计算机中一种比较宝贵的资源,如果我们不注意节约,Redis很可能出现内存不足,最终导致崩溃。Redis为了优化数据结构的内存占用,增加了非常多的优化点,这些优化也是牺牲代码的可读性为代价,但是对于解决的内存问题来说是非常值得的,尤其是对于Redis这种内存数据库
32bit VS 64bit
  • Redis如果使用32bit版本,内部存储所有数据结构使用的指针空间占用会少一半,如果Redis使用内存不会超过2^32 = 4GB,可以考虑32bit进行编译,能节约大量内存。4GB容量对小站点来说戳戳由于。不足可以增加节点
    • 指针是寻址,存储的是地址信息,32bit系统能表示的总大小是2^32 位,所以指针能表示的寻址最大值也就是 2 ^32,同理64bit 系统占用2 ^ 64
小对象压缩
  • 如果Redis内部管理的集合数据结构很小,他会使用紧凑型存储形式压缩存储,只有当集合数据达到一定量级的时候,Redis才会将数据存储称为正常我们熟知的状态。
  • 例如HashMap本来是二维结构,如果内部元素少,使用数组+链表的形式反而浪费空间,因为指针占用空间比数据还要多,还不如使用一维数组进行存储,查找时候元素少,遍历也快,只不过代码略复杂而已。以下案例用一维数组存储HashMap的CUD
public class ArrayMap<K, V> {
    private List<K> keys = new ArrayList<>();
    private List<V> values = new ArrayList<>();

    public V put(K k, V v){
        for (int i = 0; i < keys.size(); i++) {
            if(keys.get(i).equals(k)){
                V oldv = values.get(i);
                values.set(i, v);
                return oldv;
            }
        }
        keys.add(k);
        values.add(v);
        return null;
    }

    public V get(K k){
        for (int i = 0; i < keys.size(); i++) {
            if(keys.get(i).equals(k)){
                return values.get(i);
            }
        }
        return null;
    }

    public V delete(K k){
        for (int i = 0; i < keys.size(); i++) {
            if(keys.get(i).equals(k)){
                keys.remove(k);
                return values.remove(i);
            }
        }
        return null;
    }
}
  • 同样Redis的ziplist是一个紧凑型的字节数组结构,如下图中所示,每个元素都是紧密相连,中间没有指针用来寻址操作,通过三个全局指针来完成逻辑处理,zlbytes, zltail和zlend。如下:
    在这里插入图片描述
  • 如果如上结果存储的是hash结构,那么key和value作为两个entry,被相邻的两个entry存储,通过如下方式验证,输出类型是ziplist:
新docker-redis:0>hset hello a 1
"1"
新docker-redis:0>hset hello b 2
"1"
新docker-redis:0>object encoding hello
"ziplist"
  • 如果存储的类型是zset,那么value和score会作为两个entry被相邻存储
新docker-redis:0>zadd world 1 a
"1"
新docker-redis:0>zadd world 2 b
"1"
新docker-redis:0>object encoding world
"ziplist"
  • 整数的存储Redis有一个叫intset的整数数组结构,用于存放元素都是整数且元素个数较少的set集合。
  • 如果整数可以用uint16标识,那么intset的元素就是16位的数组,如果新加入的整数超过uint16,那么久用uint32,还不够则用uint64,Redis支持set集合动态从unit16升级到unit32,在到unit64

在这里插入图片描述

  • 如下代码,我们在set集合中存储的数字,他的类型就是intset
新docker-redis:0>sadd hello 1 2
"2"
新docker-redis:0>object encoding hello
"intset"
  • 如果set存储的字符串,那么sadd会升级为hashtable结构,
新docker-redis:0>sadd hello yes no
"2"
新docker-redis:0>object encoding hello
"hashtable"
总结
hash - max-z 工pl 工 st entries 512 结构存储  			# hash 的元素个数超过 512 就必须用标准
hash- max-ziplist-value 64 超过 64 就必须用标准结构存储  	# hash 的任意元素的 key/value 的长度超过64 就必须用标准结构存储
list-max-ziplist-entries 512 结构存储  					# list 的元素个数超过 512 就必须用标准结构存储
list-max-z 工plist - value 64 用标准结构存储  			# list 的任意元素的长度超过 64 就必须用标准结构存储
zset-max-ziplist-entries 128 结构存储  					# zset 的元素个数超过 128 就必须用标准结构存储
zset-max- ziplist-value 64 用标准结构存储  				# zset 的任意元素的长度超过 64 就必须用标准结构存储
set-max-intset-entries 512 标准结构存储  					# set 的整数元素个数超过 512 就必须用标准结构存储
  • 如下测试代码:
public static void main(String[] args) {
        Jedis jedis = JedisPoolTools.getJedis();
        jedis.del("books");
        for (int i = 0; i < 512; i++) {
            jedis.hset("books", String.valueOf(i), String.valueOf(i));
        }
        System.out.println(jedis.objectEncoding("books"));
        jedis.hset("books", "hello", "512");
        System.out.println(jedis.objectEncoding("books"));
    }
//输出如下:
ziplist
hashtable
  • 当Hash结构元素超过512 ,存储结构发生变化其他的也都同理,我们通过如上总结数据看出,当hash结构的任意entry的value超过64 ,存储结构就升级成标准结构。
内存回收机制
  • Redis并不总是将空闲内存理解归还操作系统
  • 例如Redis内存10GB,当删除1GBkey后,观察内存,并不会太大变化,因为操作系统是以页为单位回收内存,这个页内只要还有一个key,就不会被回收。Redis虽然删除了1GBkey但是这些key是分散到各个page中,每个page都还有其他key,这就导致内不会被立刻回收。
  • 我们执行flushdb,观察内存,他就会重新使用那些尚未回收的空闲内存。就比如一个page中只有一个key,次数set另一个key,他会存储在这个page的空闲内存中。
内存分配算法
  • 内存分配是比较复杂的,需要是的划分内存页,考虑内存碎片,需要平衡性能与效率
  • Redis为保证自身结构简单,目前没有自己做内存分配策略,由第三方库完成此部分内容,
    • Redis可以用jemalloc,facebook的库来管理内存
    • Redis也可以切换到tcmalloc,google的库来管理
  • jemalloc性能优于tcmalloc,Redis默认使用jemalloc
新docker-redis:0>info memory
"# Memory
used_memory:5110711968
.....
mem_fragmentation_ratio:1.33
mem_allocator:jemalloc-4.0.3
active_defrag_running:0
lazyfree_pending_objects:0
  • 通过info memory指令可以看到Redis的 mem_allocator:jemalloc-4.0.3使用的jemalloc-4.0.3

上一篇:Redis–事务理解
下一篇:Redis高可用基石–主从同步

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值