关灯
护眼 字体:大
中
小
上一章
目录
下一章
列表(Lists)是Redis中的另一种基本数据结构。
列表就像是一根平放的水管:可以从左边往里塞入数据,也可以从右边往里塞入数据;可以从左边读取数据,也可以从右边读取数据。
Redis中的列表,与Python的列表在行为上有不少相似之处,可以对比着学习。列表有17条不同的操作命令,本书将会介绍其中的7条常用命令。
5.3.1 使用redis-cli实现
1.插入数据
列表分左右两个方向,所以可以从左右两侧插入。“插入(Insert)”可以理解为“推入(Push)”。又由于“左(Left)”的首字母为“L”,“右(Right)”的首字母为“R”,所以,从列表左侧插入数据的命令为“LPush”,从列表右侧插入数据的命令为“RPush”。
Redis的命令是不区分大小写的,所以一般用小写“lpush”和“rpush”便于辨认。
向列表中插入数据的命令为:
lpush key value1 value2 value3
rpush key value1 value2 value3
其中,Key的命名要求与字符串一样,可以是数字、字母下划线和中文,但不建议使用中文。
Value可以有1个或者多个。如有多个value,应使用空格将它们隔开。如果一个value内部本身就有空格,那么就使用引号包起来。
(1)从列表左侧插入数据,代码实例如下:
lpush example_list hello
lpush example_list how are you
lpush example_list "are you ok?" fine thank you
用“lpush”插入数据的流程如图5-28所示。
图5-28 lpush示意图
(2)用“rpush”插入数据的写法与“lpush”完全相同:
用“rpush”插入数据的流程如图5-29所示。
图5-29 rpush示意图
列表可以同时从左边和右边插入数据,见下方代码:
lpush example_left_right 123
rpush example_left_right 456
lpush example_left_right 789
添加完数据后,Redis当前Key的结构如图5-30所示。
图5-30 添加3个列表后的Key
列表里可以有成千上万上百万的数据,所以,使用列表控制 Key 的规模是一种比较好的选择。
2.查看数据
(1)查看列表的长度。
由于一个列表里面可以存放非常多的数据,因此,可以使用命令“llen”来查看列表的长度。命令格式为:
llen key
运行效果如图5-31所示。
图5-31 查看列表里面有多少个数据
(2)根据索引查看数据。
与Python的列表一样,Redis的列表也是有“索引”的。可以使用命令“lrange”来根据索引查看数据。
索引从最左边开始编号,从0到“列表长度-1”。例如,左边第1个数据索引为0,第2个数据索引为1……第7个元素的索引为6,第8个元素的索引为7。
根据索引查看数据的命令格式为:
lrange 0 开始索引 结束索引
例如,查看索引为6的数据:
lrange example_list 6 6
查看索引从2(包括2)到5(包括5)的数据:
lrange example_list 2 5
运行效果如图5-32所示。
图5-32 使用索引查看列表数据
(3)查看列表的所有数据。
Redis的列表也支持“负索引”,索引“-1”表示最右边的数据,“-2”表示右数第2个数据,以此类推。
因此,如果要查看列表的所有数据,可以使用命令:
lrange key 0 -1
运行效果如图5-33所示。
图5-33 查看列表所有数据
提示:
由于一个列表可以储存几百万条数据,所以,绝对不要冒然列出一个列表里面的所有数据,否则可能导致大量数据输出而瞬间耗尽系统的I/O资源。
应该是:先查看列表的长度,如确定数据量很小,则列出所有的值;如果数据量很大,则可以使用索引查看头几条数据与末尾几条数据。
查看列表的右边5条数据:
lrange key -5 -1
(4)弹出数据。
除了读取数据外,还能从列表里面“弹出(Pop)”数据,弹出也分为左右两个方向。从左边弹出数据使用的命令为“lpop”,从右边弹出数据使用的命令为“rpop”。命令格式为:
lpop key
rpop key
需要注意的是,在弹出数据的同时,被弹出的这个数据也会被从列表中删除,如图5-34所示。
从图5-34可以看出,列表“example_list”原有8条数据,首先从左侧弹出“you”,列表还剩7条数据,再从右侧弹出“hello”,最后列表还剩6条数据。
图5-34 从列表的左右侧弹出数据
3.修改数据
Redis的列表,可以根据数据的索引修改数据,使用的命令是“lset”,命令格式为:
lset key index 新的值
运行效果如图5-35所示。
图5-35 使用lset修改列表里面的数据
5.3.2 使用Python实现
使用Python操作Redis列表,用到的关键字与redis-cli中的命令一模一样。唯一的微小不同是——参数的传入方式。
1.插入数据
通过Python向Redis列表的左右两侧插入数据的关键字是“lpush”和“rpush”,写法如下:
代码5-4 在Python中向Redis列表左侧和右侧添加数据
插入数据后,程序会自动返回当前列表的长度,所以:
● 当第一次向“example_list_python”插入“python”时,程序返回“1”,表示添加了这个“python”后列表里有一条数据了。
● 当插入“life is short”后,加上前面的“python”,列表中有两条数据,因此返回2。插入这两条数据后,再来看一下这个Key下面的数据,如图5-36所示。
图5-36 插入两条数据后的列表
由于“life is short”是从右侧插入列表的,所以数据在下面。那如果要插入多条数据该怎么办呢?有两种办法。
方法一:将多条数据直接作为参数依次放入“lpush”或者“rpush”中。
具体代码如下:
代码5-5 在Python中,向Redis列表左侧或右侧批量添加数据
代码运行效果如图5-37所示。
图5-37 插入数据后的效果
其中,编号“1)”对应的是中文“第四条”,编号“10)”对应的是中文“六”。使用这种方式,理论上想添加多少个参数就可以添加多少个参数。但是,如果数据量比较大,添加起来会非常麻烦。对于数据量大的情况,就需要使用方法二插入多条数据。
方法二:把多条数据使用一个列表保存,然后把这个列表“左侧加上星号”后作为参数加入到“lpush”或“rpush”的参数中。
什么叫作“左侧加上星号”?请看下面的代码:
代码5-6 在Python中,使用列表向Redis列表中批量添加数据
运行效果如图5-38所示。
图5-38 第二种批量插入数据的运行效果
提示:
请注意观察图5-37与图5-38,无论使用两种方法中的哪一种批量插入数据,体会“左侧插入”与“右侧插入”的含意。
假设,使用lpush插入的数据为['one', 'two', 'three', 'four'],那么,首先把“one”插入列表,接着把“two”插入到“one”的左边,把“three”插入到“two”的左边,把“four”再插入到“three”的左边。这样的位置关系体现在redis-cli中就是:越左边的数据,编号越小。
同理,请试着分析:用“rpush”命令插入数据时数据是怎么进去的,先进去与后进去的数据的位置关系。
2.读取数据
(1)查看列表长度。
查看列表长度,Python使用的关键字依然是“llen”,其用法如下:
client.llen(key)
例如:
>>> import redis
>>> client = redis.Redis()
>>> print(client.llen('example_list_python'))
10
运行效果如图5-39所示,列表example_list_python中一共有10条数据。
图5-39 在Python中查看列表长度
(2)获取列表中一定索引范围的数据。
获取列表中一定索引范围的数据,使用的关键字也是“lrange”,格式如下:
client.lrange(key, 开始索引,结束索引)
例如:
代码5-7 在Python中获取Redis列表的多个数据
运行效果如图5-40所示。
图5-40 lrange查看索引范围
(3)使用for循环把数据展开。
lrange返回的数据是一个列表,列表里面的数据是“bytes”型的数据。可以使用for循环把这些数据展开,再转为人可以读懂的字符串。
代码5-8 Python获取Redis列表的值,并转为人可以读懂的字符串
运行效果如图5-41所示。
图5-41 使lrange返回的数据容易阅读
(4)从左右侧弹出数据。
从左右侧弹出数据的关键字依然是“lpop”和“rpop”,代码格式如下:
client.lpop(key)
client.rop(key)
例如以下代码。
代码5-9 使用Python从Redis列表中弹出数据
运行效果如图5-42所示。
图5-42 从列表左右侧弹出数据
从图5-42可以看出,被弹出的数据类型也是bytes。如果bytes型的数据是中文,则需要把它解码为字符串型数据人才能阅读。弹出数据后,Redis也会把被弹出的数据删除。
提示:
注意图5-42中,“第四条”两侧没有引号,但是“六”两侧有单引号。这是Python交互环境的显示机制,因为“六”是一个字符串,当在Python的交互环境中直接显示字符串时,它就会被引号包起来,提示开发者这个数据是Python的字符串。
而如果使用“print”函数把一个字符串打印出来,那么Python交互环境就会去掉引号,只显示内容本身。
所以,这里有无引号仅仅取决于Python的交互环境,和Redis没有任何关系。
3.修改数据
在Python中,根据索引修改Redis列表的数据使用的关键字是“lset”,代码格式如下:
client.lset(key, index, value)
例如:
代码5-10 用Python从Redis列表中删除数据
运行效果如图5-43所示。
图5-43 修改列表中的一条数据
5.3.3 列表的应用
在工程上,Redis列表一般用来作为一个队列,存放一批可以使用相同逻辑处理的数据。
假设,有一个互联网公司,需要在除夕给10万个注册用户发送祝福短信。简化起见,假设一台服务器1秒钟可以发送一条短信,现有10台服务器,需要2.7小时来完成任务。为了保证不漏掉一个用户,也不能给一个用户发多条短信,还要实现发送短信失败后进行重试,一个用户最多重试3次,那么就可以使用Redis的列表来实现。
10台服务器上面跑同一套程序,这些程序不停地从Redis中获取用户手机号,然后调用发送接口。简化版代码如下:
代码5-11 简易分布式短信发送程序
其中,主要代码说明如下。
● 第4行代码:如果程序需要连接远程的Redis,则需要指定IP,这一点在第9章将会讲到。
● 第8行代码:对一个空的列表执行“lpop”,会返回 None,说明所有的短信都已经发送完毕。
● 第12行代码:列表中的数据是一段JSON格式的字符串,从Redis中读取出来后数据类型为bytes,由于Python自带的json模块可以处理bytes型的数据,因此不需要将其转为普通字符串。
● 第13行代码:在初始状态下,phone_info中的数据形如:{'phone_number': 12345678},因此使用字典的“get”方法可以防止报错。只有在至少重试一次后,phone_info 中才会出现“retry_times”这个Key。
● 第15行代码:调用“send_sms”接口发送短信,发送成功则返回“True”,发送失败则返回“False”。
● 第24行代码:如果发送失败并且重试次数不足3次,那么就把数据重新放进Redis中“phone_queue”这个列表的右侧。在放进去之前,“retry_times”需要加1。
10台服务器同时从1台公共的Redis列表左侧读取数据。由于Redis是一个单线程、单进程的数据库,因此10台服务器即使同时对列表执行“lpop”操作,Redis也会自动让它们排队,一个一个地弹出最左侧的数据。因此,服务器不会读取到相同的数据,这样就可以实现服务器之间分工协作而不用担心重复发送短信的问题。
另外,即使一个手机号发送失败了,把它重新塞入列表,那么不久后另一台服务器就可能又拿到这个手机号并重新发送短信。
在这个例子中,只需要让服务器获取手机号发送短信就可以了,不需要关心具体哪一台服务器给哪一个手机号发送短信,所以可以使用列表来实现。所有服务器都从列表里面取手机号,取到以后用相同的发短信逻辑发送短信即可。Redis的列表在这个应用中就是一个队列的角色,数据就在里面,谁想要谁就来取。
上一章
目录
下一章