本文将介绍两种降低Redis内存占用的方法——使用短结构存储数据和对数据进行分片.
降低Redis内存占用有助于减少创建快照和加载快照所需的时间、提升载入AOF文件和重写AOF文件时的效率、缩短从服务器同步所需的时间,并能让Redis存储更多的数据.
Redis为列表、集合、散列和有序集合提供了一组配置选项(配置文件中),这些选项可以让Redis以更加节约空间的方式存储长度较短的结构(即短结构).
在列表、散列和有序集合的长度较短或者体积较小的时候,Redis可以选择使用一种名为压缩列表(ziplist)的紧凑存储方式来存储这些结构.压缩列表会以序列化的方式存储数据,这些序列化数据每次被读取的时候都要就行解码,每次被写入的时候都要进行局部的重新编码,并且可能需要对内存里的数据进行移动.所以呢读写一个长度较大的压缩列表可能会给性能带来负面的影响.
来看一下不同结构使用压缩列表表示的配置选项:
list-max-ziplist-entries 512 list-max-ziplist-value 64 当列表的元素长度都小于64字节并且列表元素数量小于512时,使用压缩列表,反之使用linkedlist. hash-max-ziplist-entries 512 hash-max-ziplist-value 64 当散列的元素的键和值都小于64字节并且键值对的数量小于512时,使用压缩列表,反之使用hashtable zset-max-ziplist-entries 128 zset-max-ziplist-value 64 当有序集合的元素都小于64字节并且元素数量小于128个的时候,使用压缩列表,反之使用skiplist
-max-ziplist-entries:表示列表、散列和有序集合在被编码为压缩列表的情况下,允许包含的最大元素数量.
-max-ziplist-value:表示压缩列表的每个节点的最大体积是多少?.
列表、散列和有序集合的基本配置选项很相似,它们都由*-max-ziplist-entries和*-max-ziplist-value组成.当这些选项设置的限制条件中的任意一个被打破的时候,Redis就会将相应的列表、散列或者有序集合从压缩列表编码转换为其他结构,而内存的占用也会所以呢增加.下面给出一个例子:
跟列表、散列和有序集合一样,体积较小的集合也有自己的短结构:如果集合包含的所有成员都可以被解释为十进制整数,而这些整数又处于平台的有符号整数范围之内,并且集合成员的数量满足配置文件中的限制条件,那么Redis就会以有序整数数组的方式存储集合.这种存储方式又被称为整数集合(intset).
我们先来看一下集合使用整数集合表示的限制条件:
set-max-intset-entries 512 --只要集合存储的整数数量没有超过512,Redis就会使用整数集合表示以减少数据的体积.
再来看一下操作实例:?
为了方便测试,我将set-max-intset-entries设置为10,首先向集合testset中加入10个整数元素,testset的存储结构为intset(整数集合),当再向testset中增加一个整数元素时,此时集合元素数量超过了set-max-intset-entries的设置,此时testset的存储结构将从整数集合(intset)转换为散列表(hashtable).
前面提到过,读写一个长度较大的压缩列表可能会给性能带来负面的影响,同样,操作一个元素数量较多的整数集合时也可能会给性能带来负面影响,所以Redis才会在压缩列表和整数集合突破限制条件时,将其转换为更底层的结构类型.
分片(sharding)是一种广为人知的技术,很多数据库都使用这种技术来扩展存储空间并提高自己所能处理的负载量.分片本质上就是基于某些简单的规则,将数据划分为更小的部分,然后根据数据所属的部分来决定将数据分配到哪或者从哪获取数据.而Redis分片就是将数据拆分到多个Redis服务器的过程,这样每个Redis服务器将只存储原数据的子集.
Redis分片的作用:
允许使用很多电脑的内存总和来支持更大的数据库.没有分片,你就被局限于单机能支持的内存容量.
允许伸缩计算能力到多核或多服务器,伸缩网络带宽到多服务器或多网络适配器.
Redis分片的方式:
Redis分片的缺点:
涉及多个键的操作通常不支持.例如,你不能对映射在两个不同Redis服务器上的键执行交集.
涉及多个键的事务不能使用.
数据处理变得更复杂,例如,你需要处理多个RDB/AOF文件,备份数据时你需要聚合多个实例和主机的持久化文件.
用Java实现Redis分片:
在对数据结构进行分片的时候,我们既可以实现结构的所有功能,也可以只实现结构的部分功能.这里的例子中,实现了散列结构的分片函数以及分片散列的HSET和HGET功能.
根据基础键和散列键计算出分片键的函数:
//在调用shardKey函数时,用户需要给定基础散列的名字、将要被存储到分片散列里面的元素的键、预计的元素总数量以及每个分片存储的元素数量 public String shardKey(String base, String key, long totalElements, int shardSize) { long shardId = 0; if (isDigit(key)) { //如果key是一个整数,那么它将被直接用于计算分片ID shardId = Integer.parseInt(key, 10) / shardSize; }else{ CRC32 crc = new CRC32(); crc.update(key.getBytes()); long shards = 2 * totalElements / shardSize; //计算分片的总数量 shardId = Math.abs(((int)crc.getValue()) % shards); //计算分片ID } return base ◆ ':' ◆ shardId; //把基础键和分片ID组合在一起,得到分片键 } //判断字符串是否为一个整数字符串 private boolean isDigit(String string) { for(char c : string.toCharArray()) { if (!Character.isDigit(c)){ return false; } } return true; }
分片散列的HSET和HGET函数:
public Long shardHset( Jedis conn, String base, String key, String value, long totalElements, int shardSize) { String shard = shardKey(base, key, totalElements, shardSize); //计算出应该由哪个分片来存储 return conn.hset(shard, key, value); //将元素存储到分片里面 } public String shardHget( Jedis conn, String base, String key, int totalElements, int shardSize) { String shard = shardKey(base, key, totalElements, shardSize); //计算出元素应该被存储到了哪个分片 return conn.hget(shard, key); //获取存储在分片里面的元素 }
分片散列的设置和获取操作并不复杂,其它的操作如HDEL、HINCRBY等也可以用类似的方式实现.
参考:
转载请注明出处
以上就是土嘎嘎小编为大家整理的Redis短结构与分片相关主题介绍,如果您觉得小编更新的文章只要能对粉丝们有用,就是我们最大的鼓励和动力,不要忘记讲本站分享给您身边的朋友哦!!