首页 » Redis » Redis学习03之 HASH

Redis学习03之 HASH

前一篇学习了String类型, 由于Redis的数据保存在内存中,查询方式非常块,像String类型可以存储浏览量,投票,文章点击等小量级的数据记录中,如果数据量超过百万级别使用简单的string映射关系会浪费大量的内存,此时Redis推荐使用另一种数据结构:HASH.  存储相同量级的数据Hash 消耗内存约String的四分之一,使用一种压缩存储,同时查询速度也并不差。 如果Redisk中有大量的keys, 使用key *会导致Redis hang,所以不清楚时不建议使用该命令。下面简单测试HASH的使用。

HASH 实例了KEY-VALUE的映射,和字符串类似,但Value是键值对, 根据Key可以快速找到Value, 无论有多少个键值对,查询时间是一样的,Python中的DICT就是HASH TABLE的实现。 Redis中的Hash可以存储2的32次方-1(约43亿)个键值对。

hash是一个string类型的field和value的映射表.一个key可对应多个field,一个field对应一个value。将一个对象存储为hash类型,较于每个字段都存储成string类型更能节省内存。新建一个hash对象时开始是用zipmap(又称为small hash)来存储的。这个zipmap其实并不是hash table,但是zipmap相比正常的hash实现可以节省不少hash本身需要的一些元数据存储开销。尽管zipmap的添加,删除,查找都是O(n)。

这里是因为Redis 的hash 对象有两种编码方式
1. ziplist(2.6之前是zipmap)
2. hashtable

当哈希对象可以同时满足以下两个条件时, 哈希对象使用 ziplist 编码:
哈希对象保存的所有键值对的键和值的字符串长度都小于 64 字节;
哈希对象保存的键值对数量小于 512 个;

不能满足这两个条件的哈希对象需要使用 hashtable 编码。在Redis 内存优化时强烈建议使用HASH。

哈希类型中的命令及使用
配置值
HSET、HMSET 、HSETX
HSET – 设置值
HGET – 获取指定字段值

取值
HGET HMGET HGETALL
HGET – 获取指定字段值
HMGET – 返回多个字段值
HGETALL – 获取所有字段及值

自增
HINCRBY – 为指定字段值增加
HINCRBYFLOAT – 为指定字段值增加浮点数

其它
HDEL – 字段删除
HEXISTS – 判断字段是否存在
HKEYS – 返回所有字段
HLEN – 返回字段数量
HVALS – 返回所有字段值

127.0.0.1:6379> hset stu1 name Jack age 30  sex F
(integer) 3
127.0.0.1:6379> hget stu1 name
"Jack"
127.0.0.1:6379> hget stu1 age
"30"
127.0.0.1:6379> hset stu1 id 1
(integer) 1

127.0.0.1:6379> hmget stu1 name sex
1) "Jack"
2) "F"

127.0.0.1:6379> hgetall stu1
1) "name"
2) "Jack"
3) "age"
4) "30"
5) "sex"
6) "F"
7) "id"
8) "1"

127.0.0.1:6379> hdel stu1 sex
(integer) 1
127.0.0.1:6379> hmset stu2 name Tome id 2
OK

127.0.0.1:6379> hgetall stu1
1) "name"
2) "Jack"
3) "age"
4) "30"
5) "id"
6) "1"
127.0.0.1:6379> hgetall stu2
1) "name"
2) "Tome"
3) "id"
4) "2"

127.0.0.1:6379> hkeys stu1
1) "name"
2) "age"
3) "id"
127.0.0.1:6379> hkeys stu2
1) "name"
2) "id"

127.0.0.1:6379> hlen stu1
(integer) 3


127.0.0.1:6379> hvals stu1
1) "Jack"
2) "30"
3) "1"

127.0.0.1:6379> hincrby stu1 age 2
(integer) 32
127.0.0.1:6379> hget stu1 age
"32"


案例

模拟像WEIBO,Twitter 上发布的链接一样,会自动转为短链接显示。这里有几个python文件

[root@MiWiFi-R2100-srv hash]# find . -name "*.py" -print -exec cat {} \;
./base36.py
def base10_to_base36(number):
    alphabets = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    result = ""

    while number != 0 :
        number, i = divmod(number, 36)
        result = (alphabets[i] + result)

    return result or alphabets[0]
./shorty_url.py
from base36 import base10_to_base36

ID_COUNTER = "ShortyUrl::id_counter"
URL_HASH = "ShortyUrl::url_hash"

class ShortyUrl:

    def __init__(self, client):
        self.client = client

    def shorten(self, target_url):
        new_id = self.client.incr(ID_COUNTER)
        short_id = base10_to_base36(new_id)
        self.client.hset(URL_HASH, short_id, target_url)
        return short_id

    def restore(self, short_id):
        return self.client.hget(URL_HASH, short_id)
./gethkeys.py
import redis

client = redis.Redis()

fields = client.hkeys('ShortyUrl::url_hash')

for name in fields:
   print(name.decode())

./gethvals.py
import redis

client = redis.Redis()

fields = client.hvals('ShortyUrl::url_hash')

for name in fields:
   print(name.decode())

下面使用python CL命令行及调用py测试

[root@MiWiFi-R2100-srv hash]# python
Python 2.7.5 (default, Nov 20 2015, 02:00:19)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from shorty_url import ShortyUrl
>>> from redis import Redis
>>> client = Redis(decode_responses=True)
>>> shorty_url = ShortyUrl(client)
>>> shorty_url.shorten('anbob.com')
'1'
>>> shorty_url.shorten('www.google.com/bolabolabola.html')
'2'
>>> shorty_url.restore("1")
u'anbob.com'
>>> shorty_url.restore("2")
u'www.google.com/bolabolabola.html'

>>> for i in range(30):
...   shorty_url.shorten('www.anbob.com/arch/'+str(i)+'.html')
...
'Y'
'Z'
'10'
'11'
'12'
'13'
'14'
'15'
...
...
'1M'
'1N'
'1O'
'1P'
'1Q'
'1R'


127.0.0.1:6379> hgetall ShortyUrl::url_hash

 70) "www.anbob.com/arch/1.html"
 71) "10"
 72) "www.anbob.com/arch/2.html"
 73) "11"
 74) "www.anbob.com/arch/3.html"
 75) "12"
 76) "www.anbob.com/arch/4.html"
 77) "13"
 78) "www.anbob.com/arch/5.html"
 79) "14"
 80) "www.anbob.com/arch/6.html"
...

114) "www.anbob.com/arch/23.html"
115) "1M"
116) "www.anbob.com/arch/24.html"
117) "1N"
118) "www.anbob.com/arch/25.html"

[root@MiWiFi-R2100-srv hash]# python gethkeys.py
1
2
3
4
5
6
7
...

[root@MiWiFi-R2100-srv hash]# python gethvals.py
anbob.com
www.google.com/bolabolabola.html
...
www.anbob.com/arch/0.html
www.anbob.com/arch/1.html
www.anbob.com/arch/2.html
www.anbob.com/arch/3.html
www.anbob.com/arch/4.html
www.anbob.com/arch/5.html
www.anbob.com/arch/6.html
www.anbob.com/arch/7.html
www.anbob.com/arch/8.html
...

也可用于存储json

>>> import json
>>> import redis
>>> client = redis.Redis()
>>> client.hset('users','weejar', json.dumps({'id':1,'CNname':'张维照', 'blog':'www.anbob.com'}))
1L
>>> client.hset('users','admin', json.dumps({'id':2,'CNname':'ADMIN', 'blog':'www.anbob.com/login'}))
1L
>>> names = client.hkeys('users')

>>> for p in names:
...   print(p)
...
admin
weejar

>>> for p in names:
...   print(client.hget('users',p).decode())
...
{"blog": "www.anbob.com/login", "CNname": "ADMIN", "id": 2}
{"blog": "www.anbob.com", "CNname": "\u5f20\u7ef4\u7167", "id": 1}

>>> user_cnt=client.hlen('users')

>>> print('there are {%s} users ' % user_cnt )
there are {2} users

>>> if client.hexists('users','weejar'):
...    client.hdel('users','weejar')
...
1
>>> client.hkeys('users')
['admin']

— over —

打赏

对不起,这篇文章暂时关闭评论。