Redis 利用 Hash 存储节约内存

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis 利用 Hash 存储节约内存相关的知识,希望对你有一定的参考价值。

参考技术A 刚开始用 String 结构来做一个 key-value 存储
但这样单个简单的 key 存储的 value 很大

优化方案是使用 Hash 结构,由于 Hash 结构会在单个 Hash 元素在不足一定数量时进行压缩存储,所以可以大量节约内存。这一点 String 结构里是不存在的

省内存的原因是新建一个 hash 对象时开始是用 ziplist(又称为 small hash)来存储的。这个 ziplist 其实并不是 hash table,但是 ziplist 相比正常的 hash 实现可以节省不少 hash 本身需要的一些元数据存储开销。 尽管 ziplist 的添加,删除,查找都是 O(n),但是由于一般对象的 field 数量都不太多。 所以使用 ziplist 也是很快的,也就是说添加删除平均还是 O(1) 。如果 field 或者 value 的大小超出一定限制后,redis 会在内部自动将 ziplist 替换成正常的 hash 实现,这个限制可以在配置文件中指定 hash-zipmap-max-entries 参数来控制。将 hash-zipmap-max-entries 设置为 1000 时,性能比较好,超过 1000 后 HSET 命令就会导致 CPU 消耗变得非常大

原存储方式:

将数据分段,每一段使用一个 Hash 结构存储,保证了每个 Hash 内部只包含 3 位的 key,也就是 1000 个。如:

这样公共的前缀只存了一次,也节约了一部分内存

总的 2 个优化:

Redis系列-存储hash主要操作命令

Redis系列-存储篇hash主要操作函数小结

hash是一些列key value(field value)的映射表。常常用其存储一些对象实例。相对于把一个对象的各个字段存储为string,存储为hash会占用更少的内存。为什么会更省内存呢?需要搞清楚两个配置(hash-max-zipmap-entries和hash-max-zipmap-value)的含义,配置的详细介绍,我打算放在最后的配置优化环节讲。

1)新增

a)hset

语法:hset key field value
解释:设置hash表key中的field的值。如果hash表不存在,则创建,并执行设置field的值,如果hash表存在,值field的值覆盖或新增
[root@localhost ~]# redis-cli 
redis 127.0.0.1:6379> hset user.1 name lisi    #设置key user.1 name域 的值
(integer) 1
redis 127.0.0.1:6379> hset user.1 age 45 #设置age域
(integer) 1
redis 127.0.0.1:6379> hset user.1 tech lisi
(integer) 1

b)hmset

语法:hash key field value[key value]
解释:批量设置hash表key的域
redis 127.0.0.1:6379> hmset user.2 name niuer age 34 #同时设置name 和age域
OK

c)hsetnx

语法:hsetnx key field value
解释:仅仅当field域不存在时,设置hash表field的值
redis 127.0.0.1:6379> hsetnx user.1 name lisi  #由于name域已经设置过,所以返回0
(integer) 0
redis 127.0.0.1:6379> hsetnx user.1 fri 5  
(integer) 1   #fri域没有设置过,所以hset并返回1

2)查询

a)hget

语法:hget key field

解释:获取哈希表key的field值

redis 127.0.0.1:6379> hget user.1 name  #存在的hash表及域
"lisi"
redis 127.0.0.1:6379> hget user.3 name  #不存在的hash表
(nil)
redis 127.0.0.1:6379> hget user.1 bb  #不存在的域
(nil)

b)hmget

语法:hmget key field[field]

解释:批量获取hash表的filed

redis 127.0.0.1:6379> hmget user.1 name age fri tech
1) "lisi"
2) "45"
3) "5"
4) "lisi"
redis 127.0.0.1:6379> hmget user.1 name age fri tech nofield #存在hash表中包含不存在的域nofield
1) "lisi"
2) "45"
3) "5"
4) "lisi"
5) (nil)
redis 127.0.0.1:6379> hmget user.3 name age fri #不存在的hash表
1) (nil)
2) (nil)
3) (nil)

c)hgetall

语法:hgetall key

解释:获取hash表的所有域值

redis 127.0.0.1:6379> hgetall user.2  #存在的hash表
1) "name"   #域
2) "niuer"  #域name的值
3) "age"    #域
4) "34"     #域age的值
redis 127.0.0.1:6379> hgetall user.3  #不存在的hansh表
(empty list or set)

d)hexists

语法:hexists key field

解释:判断hash表中是否存在某个域

redis 127.0.0.1:6379> hexists user.1 name  #存在
(integer) 1
redis 127.0.0.1:6379> hexists user.1 nofield  #不存在
(integer) 0
redis 127.0.0.1:6379> hexists use1 nofield #hash表不存在
(integer) 0

e)hkeys

语法:hkeys key

解释:获取hash表的所有域

redis 127.0.0.1:6379> hkeys user.1  #存在的hash表
1) "name"
2) "age"
3) "tech"
4) "fri"
redis 127.0.0.1:6379> hkeys user.4 #不存在的hash
(empty list or set)

f)hvals

语法:hvals key

解释:获取hash表的所有域值

redis 127.0.0.1:6379> hvals user.1  #存在hash
1) "lisi"
2) "45"
3) "lisi"
4) "5" 
redis 127.0.0.1:6379> hvals user.4 #不存在
(empty list or set)

3)修改

语法:hincrby key field increment

解释:hash表field域的数值增加步长increment,如果increment是负值,则是递减。如果域不存在,初始值视为0

redis 127.0.0.1:6379> hincrby user.1 age 2  #增加2
(integer) 47
redis 127.0.0.1:6379> hincrby user.1 age -3  #减少3
(integer) 44
redis 127.0.0.1:6379> hincrby user.1 age2 -3 #域不能存在,初始值是0
(integer) -3

4)删除

语法:hdel key field[field]

解释:删除hash的域,如果指定多个field,则删除多个

redis 127.0.0.1:6379> hkeys user.1
1) "name"
2) "age"
3) "tech"
4) "fri"
5) "age2"
redis 127.0.0.1:6379> hdel user.1 age2  #删除一个域
(integer) 1
redis 127.0.0.1:6379> hkeys user.1
1) "name"
2) "age"
3) "tech"
4) "fri"
redis 127.0.0.1:6379> hdel user.1 fri tech #删除2个域
(integer) 2
redis 127.0.0.1:6379> hkeys user.1
1) "name"
2) "age"
redis 127.0.0.1:6379> hdel user.1 bb #删除一个不存在的域 
(integer) 0 #返回0

5)其他

语法:hlen key

解释:获取hash的域数量

redis 127.0.0.1:6379> hkeys user.1
1) "name"
2) "age"
redis 127.0.0.1:6379> hlen user.1 #存在2个域
(integer) 2
redis 127.0.0.1:6379> hlen user.4  #不存在的hash
(integer) 0

关于hash的更多详细用法,请参阅:http://redis.io/commands#hash

以上是关于Redis 利用 Hash 存储节约内存的主要内容,如果未能解决你的问题,请参考以下文章

使用Redis的Hash存储Java对象

redis内存优化方法

Redis数据结构之压缩列表-ziplist

redis之Hash存储与String存储内存消耗对照

Session服务器之Redis

Redis学习笔记jedis(JedisCluster)操作Redis集群 redis-cluster