Skip to content

哈希

几乎所有的编程语言都提供了哈希(hash)类型,它们的叫法可能是哈希、字典、关联数组。
在 Redis 中,哈希类型是指键值本身又是一个键值对结构,形如 value = {{field1, value1}, ... {fieldN, valueN}}

命令

设置值

hset key field value

下面为 user:1 添加一对 field-value:

1
2
127.0.0.1:6379> hset user:1 name tom
(integer) 1

如果设置成功会返回 1,反之会返回 0.
此外 Redis 提供了 hsetnx,它们的关系就像 set 和 setnx 命令一样,只不过作用域由键变为 field

获取值

hget key field

例如,下面操作获取 user:1 的 name 域(属性)对应的值:

1
2
127.0.0.1:6379> hget user:1 name
"tom"

如果键或 field 不存在,会返回 nil:

1
2
3
4
127.0.0.1:6379> hget user:2 name
(nil)
127.0.0.1:6379> hget user:1 age
(nil)

删除field

hdel key field [field ...]

hdel 会删除一个或多个 field,返回结果为成功删除 field 的个数,例如:

1
2
3
4
127.0.0.1:6379> hdel user:1 name
(integer) 1
127.0.0.1:6379> hdel user:1 age
(integer) 0

计算 field 个数

hlen key

例如 user:1 有 3 个 field:

1
2
3
4
5
6
7
8
127.0.0.1:6379> hset user:1 name tom
(integer) 1
127.0.0.1:6379> hset user:1 age 23
(integer) 1
127.0.0.1:6379> hset user:1 city tianjin
(integer) 1
127.0.0.1:6379> hlen user:1
(integer) 3

批量设置或获取 field-value

hmget key field [field ...]
hmset key field value [field value ...]

hmset 和 hmget 分别是批量设置和获取 field-value,hmset 需要的参数是 key 和多对 field-value,hmget 需要的参数是 key 和多个 field。

1
2
3
4
5
127.0.0.1:6379> hmset user:1 name mike age 12 city tianjin
OK
127.0.0.1:6379> hmget user:1 name city
1) "mike"
2) "tianjin"

判断 field 是否存在

hexists key field

例如,user:1 包含 name 域,所以返回结果为 1,不包含时返回 0:

1
2
127.0.0.1:6379> hexists user:1 name
(integer) 1

获取所有 field

hkeys key

hkeys 命令应该叫 hfields 更为恰当,它返回指定哈希键所有的 field:

1
2
3
4
127.0.0.1:6379> hkeys user:1
1) "name"
2) "age"
3) "city"

获取所有 value

hvals key

1
2
3
4
127.0.0.1:6379> hvals user:1
1) "mike"
2) "12"
3) "tianjin"

获取所有的 field-value

hgetall key

下面操作获取 user:1 所有的 field-value:

1
2
3
4
5
6
7
127.0.0.1:6379> hgetall user:1
1) "name"
2) "mike"
3) "age"
4) "12"
5) "city"
6) "tianjin"

开发提示

在使用 hgetall 时,如果哈希元素个数比较多,会存在阻塞 Redis 的可能。
如果开发人员只需要获取部分 field,可以使用 hmget,如果一定要获取全部 field-value,可以使用 hscan 命令,该命令会渐进式遍历哈希类型

hincrby hincrbyfloat

1
2
hincrby key field
hincrbyfloat key field

hincrby 和 hincrbyfloat,就像 incrby 和 incrbyfloat 命令一样,但是它们的作用域是 field

计算 value 的字符串长度

hstrlen key field

例如 hget user:1 name 的 value 是 tom,那么 hstrlen 的返回结果是 3:

1
2
127.0.0.1:6379> hstrlen user:1 name
(integer) 3

内部编码

哈希类型的内部编码有两种:

ziplist(压缩列表)

当哈希类型元素个数小于 hash-max-ziplist-entries 配置(默认 512 哥)、同时所有值都小于 hash-max-ziplist-value 配置(默认 64 字节)时,Redis 会使用 ziplist 作为哈希的内部实现,ziplist 使用更加紧凑的结构实现多个元素的连续存储,所以在节省内存方面比 hashtable 更加优秀

hashtable(哈希表)

当哈希类型无法满足 ziplist 的条件时,Redis 会使用 hashtable 作为哈希的内部实现,因为此时 ziplist 的读写效率会下降,而 hashtable 的读写时间复杂度为 O(1)

py 使用

方法 作用 示例 示例结果
hset(name, key, value) 向键名为name的散列表中添加映射 hset('price', 'cake', 5) 1, 即添加的映射个数
hsetnx(name, key, value) 如果映射键名不存在,则向键名为name的散列表中添加映射 hsetnx('price', 'book', 6) 1,即添加的映射个数
hget(name, key) 返回键名为name的散列表中key对应的值 redis.hget('price', 'cake') 5
1
2
3
4
5
6
from redis import StrictRedis

redis = StrictRedis(host="127.0.0.1", port=6379, db=0)
redis.hset('price', 'cake', 5)
print(redis.hget('price', 'cake'))
# b'5'
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from redis import StrictRedis

redis_cli = StrictRedis("127.0.0.1", 6379)
redis_cli.hset('price', 'cake', 5)
redis_cli.hset('price', 'mongo', 666)
dic = redis_cli.hgetall('price')
print(dic)
for k, v in dic.items():
    print(type(k), type(v), k, v)
    print(type(k.decode()), type(v.decode()))
    print(k.decode(), v.decode())

输出结果:

1
2
3
4
5
6
7
{b'cake': b'5', b'mongo': b'666'}
<class 'bytes'> <class 'bytes'> b'cake' b'5'
<class 'str'> <class 'str'>
cake 5
<class 'bytes'> <class 'bytes'> b'mongo' b'666'
<class 'str'> <class 'str'>
mongo 666
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
from redis import StrictRedis
import json

redis_cli = StrictRedis("127.0.0.1", 6379)
redis_cli.delete('price')
redis_cli.hset('price', json.dumps('cake'), json.dumps(5))
redis_cli.hset('price', json.dumps('mongo'), json.dumps(666))
dic = redis_cli.hgetall('price')
print(dic)
for k, v in dic.items():
    print(type(k), type(v), k, v)
    print(type(k.decode()), type(v.decode()))
    print(k.decode(), v.decode())
    k = json.loads(k.decode())
    v = json.loads(v.decode())
    print(k, v, type(k), type(v))

输出结果:

1
2
3
4
5
6
7
8
9
{b'"cake"': b'5', b'"mongo"': b'666'}
<class 'bytes'> <class 'bytes'> b'"cake"' b'5'
<class 'str'> <class 'str'>
"cake" 5
cake 5 <class 'str'> <class 'int'>
<class 'bytes'> <class 'bytes'> b'"mongo"' b'666'
<class 'str'> <class 'str'>
"mongo" 666
mongo 666 <class 'str'> <class 'int'>