通知一下_左手MongoDB,右手Redis_宠文网
首页

通知一下

关灯 护眼    字体:

上一章 章节列表 下一页




Redis有着丰富的数据结构,一些功能天然就适合使用Redis来开发。本章将介绍Redis中几个比较高级的数据结构和应用。



9.1 哈希表的功能和应用


哈希表(Hash Table)是一种数据结构,它实现了“键-值”(Key-Value)的映射。根据Key就能快速找到 Value。并且,无论有多少个键值对,查询时间始终不变。Python 的字典就是基于哈希表实现的。

在Redis中也有一个数据结构叫作哈希表。

在Redis中,使用哈希表可以保存大量数据,且无论有多少数据,查询时间始终保持不变。Redis的一个哈希表里面可以储存232 ─1(约等于43亿)个键值对。



9.1.1 实例31:使用Redis记录用户在线状态


现在,一些论坛网站能够显示用户当前是在线状态还是离线状态。那这个功能是怎么实现的呢?其中一种实现方法就是基于Redis来实现。

实例描述

分别使用字符串和哈希表记录用户的在线信息,并比较在这个场景下哈希表相对于字符串有什么优势。

1.使用字符串记录用户的在线状态

程序的逻辑非常简单,包括以下几个步骤:

(1)用户登录时,在Redis中添加一个字符串,Key为用户账号,Value为1。

(2)用户退出网站时,从Redis中删除账号名对应的Key。

(3)查询时,程序尝试从Redis中获取用户账号对应的字符串:如果值为1,则表示“在线”;如果值为None,则表示“不在线”。

完整的查询代码如下:

代码9-1 使用Redis字符串记录用户在线信息

其中,主要代码说明如下。

● 第3行代码:连接本地的Redis。

● 第12行代码:使用用户帐号作为Key,在Redis中设置字符串。

● 第21行代码:从Redis中删除Key为用户账号的字符串。

● 第30行代码:从Redis中获取Key为用户帐号的字符串的值。如果这个字符串存在,则返回里面的值(在第12行设置的1);如果Redis没有这个Key,则返回None。

● 第31~33行代码:根据返回的值进行判断。如果返回1,则说明用户现在在线;否则说明用户现在不在线。

整个逻辑过程非常简单而直观。功能也正常,看起来没有什么问题。

2.使用字符串保存在线状态的弊端

现在有10个账号同时在线,当对 Redis 执行列出所有 Key 的操作以后,看到的结果如图9-1所示。

如果有1000个用户同时在线,则Redis列出所有Key后的结果如图9-2所示。

图9-1 10个用户同时在线时的Redis Key

图9-2 1000个用户同时在线的Redis Key

有多少个用户在线,就有多少个Key。

现在,网站又加入了一个积分机制。每个用户都有一个积分数据,由于这个数据需要经常查询和修改,因此也使用Redis来保存。显然,如果使用用户账号作为Key,积分作为Value,现在Redis看起来也没有什么问题。

那问题来了,在线信息使用用户账号作为 Key,积分信息也使用账号作为 Key,这不就冲突了吗?

于是有人给不同的 Key 加上了后缀。例如,记录用户是否在线,使用的 Key 为“账号:online”。如果用户账号为10032,那他的在线状态Key就是“10032:online”。记录用户积分的Key为“账号:score”,例如用户10032对应的积分Key为“10032:score”。

提示:

在Redis中,Key中的冒号就是普通的字符,用来分割前缀和后缀,没有什么特殊意义。写成“10032_online”或者“10032-score”效果完全一样。

这样一来,假如有一万个用户同时在线,可能会在Redis中出现2万个Key,或者更多。

3.使用哈希表记录用户在线状态

使用哈希表来记录用户在线状态,只需要1个Key。若要记录用户的积分信息,则再加一个Key。原来用字符串时需要2万个Key实现的功能,现在使用哈希表只需要两个Key就能解决。

提示:

哈希表与字符串的不同之处——哈希表在Key里面还有“字段”的概念。“字段”下面才是“值”。即一个哈希表的Key里面可以设置成百上千个键值对。

查询用户在线状态的小程序,如果使用哈希表来重构,则代码如下:

代码9-2 使用哈希表记录用户在线状态



其中,主要代码说明如下。

● 第12行代码:向Redis中名为user_online_status的哈希表中添加一个字段,字段名为用户账号,值为1。如果不存在名为user_online_status的哈希表,则自动创建一个。

● 第21行代码:从Redis中名为user_online_status的哈希表中删除一个字段,字段名为用户账号。

● 第30行代码:检查名为user_online_status的哈希表中是否有某个特定的字段,如果没有这个字段就返回False,如果有这个字段就返回True。

使用哈希表来保存1000个用户在线状态,运行效果如图9-3所示。

列出Redis中的所有Key,可以看到1000个用户在线状态都储存在名为user_online_status这个Key里面。

列出这个Key中的所有键值,可以看到,在Redis命令行交互界面里面,输出结果是按照“Key-Value-Key-Value”的间隔顺序输出的。

图9-3 使用哈希表保存1000个用户在线状态

使用哈希表不仅可以减少Redis的个数,还能优化储存空间。Redis官方就特别说明,哈希表对存储结构进行过特殊的优化,储存相同的内容,占用的内存比字符串要小很多。

提示:

著名图片社区Instagram在官方博客中发布过一篇文章,详细介绍了把一百万个键值对从Redis字符串迁移到哈希表的过程。

https://instagram-engineering/storing-hundreds-of-millions-of-simple-key-value-pairs-inredis-1091ae80f74c

使用字符串保存一百万个键值对需要21GB的存储空间,而改为哈希表以后,只需要5GB的存储空间。



9.1.2 实例32:使用Python向哈希表中添加数据


哈希表一共有15个操作命令,对应到Python中就是15个方法。9.1.2和9.1.3小节将介绍其中最常用的几个方法。

向哈希表中添加数据,使用的方法名为hset或者hmset。

● hset一次只能添加一个键值对。

● hmset一次可以添加多个键值对。

代码格式为:

client.hset('Key', ’字段名’, ’值’)

client.hmset('Key', {’字段名1': ’值1', ’字段名2': ’值2', ’字段名n': ’值n'})

实例描述

向Redis中添加一个哈希表用来记录用户信息,Key为“people_info”,字段名为“姓名”,值为用户详细信息对应的JSON字符串。

代码如下:

代码9-3 向哈希表中逐条添加数据和批量添加数据

其中,主要代码说明如下。

● 第5行代码:向名为“people_info”的哈希表中添加一个字段,字段名为“张小二”,值为一个JSON字符串。

● 第7~12行代码:创建一个用户信息字典,字典的Key是不同的人名,值为每个人信息的JSON字符串。

● 第14行代码:批量插入多人信息到名为“people_info”的哈希表中。

运行效果如图9-4所示。图中的中文被 Redis 转码了,但从英文和数字可以看出信息添加成功。

图9-4 使用Python向哈希表中添加数据



9.1.3 实例33:使用Python从哈希表中读取数据


实例描述

分别使用4个不同的命令(hkeys、hget、hmget和hgetall)从哈希表中读取数据,并对比这四个命令的不同。

1.hkeys

hkeys用于获取所有字段的字段名,返回的数据是包含bytes型数据的列表。

使用格式为:

field_names = client.hkeys(’哈希表名’)

例如:

代码9-4 读取哈希表的字段名



其中,主要代码说明如下。

● 第5行代码:使用hkeys方法,获取people_info哈希表中的所有字段名,结果为一个列表。

● 第6、7行代码:展开field_names列表,并将结果解码为字符串后打印出来。

运行效果如图9-5所示。

图9-5 获取哈希表所有字段名

2.hget、hmget、hgetall

● hget:获取一个字段的值。

● hmget:一次性获取多个字段的值。

● hgetall:获取一个哈希表中的所有字段名和值。

它们的使用格式为:

client.hget(’哈希表名’, ’字段名’)

client.hmget(’哈希表名’, [’字段名1', ’字段名2', ’字段名n'])

client.hgetall(’哈希表名’)

例如:分别实现从哈希表中读取一个、多个和全部字段的值,见代码9-5。

代码9-5 从哈希表中读取数据



其中,主要代码说明如下。

● 第5行代码:从名为people_info的哈希表中获取字段名为“张小二”的值。

● 第6行代码:由于从Redis获取的值是bytes型数据,所以要将其解码为字符串后再打印。

● 第9行代码:从名为“people_info”的哈希表中同时获取,名为“王小二”和“刘小五”这两个字段的值。

● 第10、11行代码:由于hmget返回的结果是列表,所以用for循环展开。

● 第14行代码:获取“people_info”哈希表中的所有字段名和值。返回的结果是一个字典,但是字典的Key和Value全都是bytes型的数据。

运行效果如图9-6所示。

图9-6 分别使用hget、hmget和hgetall获取数据

需要注意以下几点:

● 使用hget方法时,无论是哈希表名不存在或者字段名不存在,都会返回None。

● 使用hmget时,如果哈希表名不存在,则返回的列表所有元素都是None;如果哈希表中部分字段存在,部分字段不存在,则返回值列表中不存在的字段值表示为None。

● 在hgetall方法返回的字典中,Key和Value都是bytes型的数据,因此如果要查询里面的结果,也需要使用bytes型的数据,例如:

代码9-6 对哈希表中的中文进行解码以显示



9.1.4 实例34:使用Python判断哈希表中是否存在某字段,并获取字段数量


实例描述

判断一个哈希表中是否存在某个字段,并获取哈希表字段的个数。

1.判断一个哈希表中是否有某个字段。

如果要判断一个哈希表中是否有某个字段,有两个方法:

(1)获取这个字段的值,如果值为None,则这个字段就是不存在的。

(2)使用hexists方法。hexists方法的格式如下:

client.hexists(’哈希表名’, ’字段名’)

如果字段存在,则返回True;如果字段不存在,则返回False。例如:

代码9-7 判断哈希表中是否有某个字段

提示:

如果哈希表名不存在,则hexists的第2个参数无论是什么都会返回False。

2.查看一个哈希表中有多少个字段。

如果需要知道一个哈希表中有多少个字段,则可以使用hlen方法。

hlen方法的格式如下:

client.hlen(’哈希表名’)

如果哈希表名存在,则返回字段数;如果哈希表不存在,则返回0。例如:

field_num = client.hlen('people_info')

print(f'people_info哈希表中一个有{field_num}个字段’)



9.1.5 实例35:在Redis交互环境redis-cli中读/写哈希表


Redis命令行交互环境对哈希表的显示不太直观,因此只做简单介绍。

实例描述

在redis-cli中,分别实现以下功能:

(1)向哈希表中添加内容。

(2)从哈希表中读取数据。

(3)判断字段是否存在。

(4)查看字段个数。

1.向哈希表中添加内容

在redis-cli中,向哈希表中添加数据使用的命令是“hset”和“hmset”,它们的格式如下:

hset 哈希表名 字段名 值

hmset 哈希表名 字段名1 值1 字段名2 值2 字段名n 值n

例如:

hset people_info 赵老六 ’{"age": 100, "salary": 10, "address": "北京"}'

hmset book_info 论语 32 中庸 48 大学 50

运行效果如图9-7所示。

图9-7 在redis-cli中添加数据

2.从哈希表中读取数据

从哈希表中读取数据,分别对应的命令为“hkeys”“hget”“hmget”“hgetall”。它们的格式如下:

hkeys 哈希表名

hget 哈希表名 字段名

hmget 哈希表名 字段名1 字段名2 字段名3

hgetall 哈希表名

例如:

hkeys book_info

hget book_info 论语

hmget book_info 论语 大学

hgetall book_info

运行效果如图9-8所示。

图9-8 在redis-cli中获取哈希表数据

3.判断字段是否存在和获取字段数量

判断字段是否存在使用的命令为“hexists”,获取字段数量使用的关键字为“hlen”。它们的格式如下:

hexists 哈希表名 字段名

hlen 哈希表名

执行hexists时,如果字段存在,则返回1;如果字段不存在,则返回0。

例如:

hexists book_info 论语

hlen book_info

运行效果如图9-9所示。

图9-9 在redis-cli中判断字段是否存在并获取字段数


    

上一章 章节列表 下一页