Redis 数据的基本操作

Scrapy 框架本身没有分布式功能,但是配合 Redis 服务后就能实现分布式,于是就诞生了 Scrapy-Redis 这样的框架。它在 Scrapy 的基础上配合 Redis 进行了一些改造,实现了分布式的功能。本节会介绍 Redis 服务的相关操作,为后续使用和学习 Scrapy-Redis 打好基础。

1. Redis 介绍与安装

首先我们在 CentOS7.8 的系统上源码编译安装 redis-5.0 的最新版。按照下列步骤进行:

安装 gcc 等编译工具,下载 redis 5 的最新源码并解压:

[root@server2 shen]# wget http://download.redis.io/releases/redis-5.0.9.tar.gz
--2020-06-27 13:10:23--  http://download.redis.io/releases/redis-5.0.9.tar.gz
Resolving download.redis.io (download.redis.io)... 109.74.203.151
Connecting to download.redis.io (download.redis.io)|109.74.203.151|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1986574 (1.9M) [application/x-gzip]
Saving to: ‘redis-5.0.9.tar.gz’

100%[=========================================================================================================================================>] 1,986,574   12.9KB/s   in 2m 11s 

2020-06-27 13:12:35 (14.8 KB/s) - ‘redis-5.0.9.tar.gz’ saved [1986574/1986574]
[root@server2 shen]# tar xzf redis-5.0.9.tar.gz

进入 redis 源码目录,直接安装:

[root@server2 shen]# cd redis-5.0.9
[root@server2 redis-5.0.9]# make MALLOC=libc PREFIX=/usr/local/redis install

添加 redis 命令路径:

[root@server2 redis-5.0.9]# cat /etc/profile
...
# 添加下面两行内容
REDIS_HOME=/usr/local/redis
export PATH=$REDIS_HOME/bin:$PATH

[root@server2 redis-5.0.9]# source /etc/profile
[root@server2 redis-5.0.9]# which redis-cli
/usr/local/redis/bin/redis-cli

添加并修改 redis.conf 配置文件:

[root@server2 redis-5.0.9]# mkdir /etc/redis
[root@server2 redis-5.0.9]# cp redis.conf /etc/redis
[root@server2 redis-5.0.9]# cat /etc/redis
# ...
# 修改第一处,改为后台守护进程方式启动
daemonize yes
# ...

# 修改端口,比如改为6777
port 6777
# ...

# 需要密码
# requirepass foobared
requirepass spyinx
# ...

# 允许通过公网访问该redis
# bind 127.0.0.1
bind 0.0.0.0

加入 systemd 服务,统一管理:

[root@server2 redis-5.0.9]# cat /etc/systemd/system/redis.service
[Unit]
Description=Redis
After=network.target
 
[Service]
Type=forking
ExecStart=/usr/local/redis/bin/redis-server /etc/redis/redis.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/usr/local/redis/bin/redis-cli -p 6777 shutdown
PrivateTmp=true
 
[Install]
WantedBy=multi-user.target

[root@server2 redis-5.0.9]# systemctl start redis
[root@server2 redis-5.0.9]# systemctl status redis
● redis.service - Redis
   Loaded: loaded (/etc/systemd/system/redis.service; disabled; vendor preset: disabled)
   Active: active (running) since Sat 2020-06-27 14:08:44 CST; 3s ago
  Process: 7080 ExecStart=/usr/local/redis/bin/redis-server /etc/redis/redis.conf (code=exited, status=0/SUCCESS)
 Main PID: 7081 (redis-server)
   CGroup: /system.slice/redis.service
           └─7081 /usr/local/redis/bin/redis-server 0.0.0.0:6777

Jun 27 14:08:44 server2 systemd[1]: Starting Redis...
Jun 27 14:08:44 server2 redis-server[7080]: 7080:C 27 Jun 2020 14:08:44.938 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
Jun 27 14:08:44 server2 redis-server[7080]: 7080:C 27 Jun 2020 14:08:44.938 # Redis version=5.0.9, bits=64, commit=00000000, ...started
Jun 27 14:08:44 server2 redis-server[7080]: 7080:C 27 Jun 2020 14:08:44.938 # Configuration loaded
Jun 27 14:08:44 server2 systemd[1]: Started Redis.
Hint: Some lines were ellipsized, use -l to show in full.

# 设置开机启动
[root@server2 redis-5.0.9]# systemctl enable redis
Created symlink from /etc/systemd/system/multi-user.target.wants/redis.service to /etc/systemd/system/redis.service.

最后测试下 redis 服务是否能正常工作:

[root@server2 redis-5.0.9]# redis-cli -p 6777
127.0.0.1:6777> auth spyinx
OK
127.0.0.1:6777> ping "hello, world"
"hello, world"
127.0.0.1:6777> set hello world
OK
127.0.0.1:6777> get hello
"world"
127.0.0.1:6777> 

完成 redis 服务搭建后我们就可以开始 redis 服务的使用了。

2. Redis 的基本操作

我们会分成5个部分来介绍 redis 的基本数据类型,并进行实战演练。限于篇幅,这部分内容不会比较详细,许多重要的细节和知识点会被忽略。请重点参考 <<Redis使用手册>> 一书去深入学习和掌握 redis 的使用。

2.1 字符串

字符串是 Redis 最基本的键值对类型。它在数据库中将一个单独的键和单独的一个值关联起来。和 Python 字典类型中的 key-value 类似。常用的字符串操作命令有:

指令 含义
set 为字符串键设置值
get 获取字符串键的值
getset 获取旧值设置新值
mset 一次设置多个字符串键的值
mget 一次获取多个字符串键的值
msetnx 只在键不存在的情况下,一次为多个字符串键设置值
strlen 获取字符串值的字节长度
getrange 获取字符串值指定索引范围的值
setrange 对字符串值的指定索引范围进行设置
incrby/decrby 对指定值执行加法操作和减法操作
incrbyfloat 对整数值执行浮点数加法操作

接下来我们就使用上面的指令在 redis 的命令行中进行测试,请仔细查看每一步操作并动手实践:

首先是 set 和 get 指令,非常简单:

127.0.0.1:6777> set hello world
OK
127.0.0.1:6777> get hello
"world"
127.0.0.1:6777>

接着是 getset 指令,getset 返回的是旧值,同时会设置新的值

# 
127.0.0.1:6777> getset hello "new world"
"world"
127.0.0.1:6777> get hello
"new world"
127.0.0.1:6777>

接着演示 mset 和 mget 指令,都是 set 和 get 的批量版,set 后面跟的是 key1 value1 key2 value2 … 这样的形式

# 
127.0.0.1:6777> mset key1 value1 key2 value2 key3 value3
OK
127.0.0.1:6777> mget key1 key2 key3
1) "value1"
2) "value2"
3) "value3"
127.0.0.1:6777>

msetnx 只能对不存在的键进行批量设置值,对于存在的键进行设置时会报错。返回0表示失败

# 
127.0.0.1:6777> msetnx key3 value3 key4 value4
(integer) 0
127.0.0.1:6777> get key4
(nil)

类似于 python 中的 len(key1) 语句

# 
127.0.0.1:6777> strlen key1
(integer) 6

getrange 和 setrange 指令非常类似于 python 中的切片操作

# 
127.0.0.1:6777> get key1
"value1"
127.0.0.1:6777> getrange key1 0 3
"valu"
127.0.0.1:6777> getrange key1 -3 -1
"ue1"
127.0.0.1:6777> setrange key1 2 xxx
(integer) 6
127.0.0.1:6777> get key1
"vaxxx1"
127.0.0.1:6777> setrange key1 10 ", this is so pretty!"
(integer) 30

可以看到,对于超出范围的,会用 x00 填充

# 
127.0.0.1:6777> get key1
"vaxxx1x00x00x00x00, this is so pretty!"
127.0.0.1:6777> 

incrby、decrby 和 incrbyfloat 指令的实例

127.0.0.1:6777> set number 1
OK
127.0.0.1:6777> incrby number 100
(integer) 101
127.0.0.1:6777> get number
"101"
127.0.0.1:6777> decrby number 50
(integer) 51
127.0.0.1:6777> get number
"51"

加浮点数必须使用专门的指令,不能再使用 incrby 指令:

# 
127.0.0.1:6777> incrby number 12.2
(error) ERR value is not an integer or out of range
127.0.0.1:6777> incrbyfloat number 12.2
"63.2"

redis 字符串类型的操作是不是比较简单?接下来还有更复杂的数据类型,请继续往下学习。

2.2 散列

Redis 的散列键会将一个键一个散列在数据库中关联起来,用户可以在散列中对多个键设置值,这里会和前面字符串的操作一样。

图片描述

散列结构显示

来看看 Redis 中用于操作散列数据类型的命令:

指令 含义
hset/hsetnx 为字段赋值/当键不存在时为字段赋值
hget 获取字段值
hincrby 对字段存储的整数值执行加法和减法动作
hincrbyfloat 对字段存储的数值执行浮点的加法和减法动作
hstrlen 获取字段值的字节长度
hexists 检查字段是否存在
hdel 删除字段
hlen 获取散列表中字段数
hmset 批量设置多个字段的值
hmget 批量获取多个字段的值
hkeys/hvals/hgetall 获取所有字段/所有值和所有字段和值

下面我们就来使用这些指令,进行实战演练。请仔细阅读下面的操作以及输出结果:

# hset/hsetnx/hget最基本的三个操作
127.0.0.1:6777> hset books:1000 article 'life of article'
(integer) 1
# 返回0就是失败,1表示成功
127.0.0.1:6777> hsetnx books:1000 article 'life of article'
(integer) 0
127.0.0.1:6777> hsetnx books:1000 history 'war of liberation'
(integer) 1
127.0.0.1:6777> hget books:1000 article
"life of article"
127.0.0.1:6777> hget books:1000 history
"war of liberation"
127.0.0.1:6777> 

实战 hincrby 和 hincrbyfloat 指令:

# 
127.0.0.1:6777> hset books:1000 number 100
(integer) 1
# 实现加法
127.0.0.1:6777> hincrby books:1000 number 10
(integer) 110
# 实现减法
127.0.0.1:6777> hincrby books:1000 number -10
(integer) 100
127.0.0.1:6777> hget books:1000 number
"100"
127.0.0.1:6777> hincrbyfloat books:1000 number -20.1
"79.9"

测试 hstrlen、hexists 和 hdel 指令:

# 
127.0.0.1:6777> hget books:1000 article
"life of article"
127.0.0.1:6777> hstrlen books:1000 article
(integer) 15
127.0.0.1:6777> hget books:1000 number
"79.9"
# 返回1表示存在,0表示不存在
127.0.0.1:6777> hexists books:1000 number
(integer) 1
# 删掉number字段
127.0.0.1:6777> hdel books:1000 number
(integer) 1
127.0.0.1:6777> hexists books:1000 number
(integer) 0

测试 hlen/hmset/hmget 命令:

# 
127.0.0.1:6777> hlen books:1000
(integer) 2
127.0.0.1:6777> hmset books:1000 math "differential geometry" physics "quantum mechanics"
OK
127.0.0.1:6777> hlen books:1000
(integer) 4
127.0.0.1:6777> hmget books:1000 article history math physics
1) "life of article"
2) "war of liberation"
3) "differential geometry"
4) "quantum mechanics"

最后测试 hkeys/hvals/hgetall 指令:

# 
127.0.0.1:6777> hkeys books:1000
1) "article"
2) "history"
3) "math"
4) "physics"
127.0.0.1:6777> hvals books:1000
1) "life of article"
2) "war of liberation"
3) "differential geometry"
4) "quantum mechanics"
127.0.0.1:6777> hgetall books:1000
1) "article"
2) "life of article"
3) "history"
4) "war of liberation"
5) "math"
6) "differential geometry"
7) "physics"
8) "quantum mechanics"

看看上面的操作,是不是十分简单和明了?Redis 就是这么好用!

2.3 列表

Redis 中的列表和 Python 中的列表都是类似的。Redis 对于列表的数据类型也提供了很多操作指令,非常有意思。我们还是和上面一样,先给出部分常用的操作指令,然后逐个进行实战演示。来看看 Redis 中操作列表数据的指令:

指令 含义
lpush 将元素推入列表左端
rpush 将元素推入列表右端
lpushx/rpushx 只对已存在的列表执行推入操作
lpop 弹出最左端的元素
rpop 弹出列表最右端的元素
rpoplpush 将右边弹出的元素推入左边
llen 获取列表的长度
lindex 获取指定索引上的元素
lrange 获取指定索引范围的元素
lset 为指定索引设置新元素
linsert 将元素插入列表
ltrim 修建列表。接收一个列表和一个索引范围作为参数,并移除列表中位于给定索引范围之外的元素,只保留给定范围之内的元素
lrem 移除列表中指定元素
blpop 带有阻塞功能的左弹出操作
brpop 阻塞式右弹出操作

下面我们继续在我们的 redis 客户端命令行中进行操作,实践这些指令:

首先测试推入元素到列表的指令:lpush/rpush/lpushx/rpushx:

# 
127.0.0.1:6777> lpush companies baidu alibaba tencent
(integer) 3
# lrange用于列表显示,先不用管它。看到一直左插入元素的结果如下
127.0.0.1:6777> lrange companies 0 -1
1) "tencent"
2) "alibaba"
3) "baidu"
# 右边插入,需要把下面的列表横过来看
127.0.0.1:6777> rpush companies bytedance
(integer) 4
127.0.0.1:6777> lrange companies 0 -1
1) "tencent"
2) "alibaba"
3) "baidu"
4) "bytedance"
127.0.0.1:6777> lpushx companies chinatelecom 
(integer) 5
127.0.0.1:6777> lrange companies 0 -1
1) "chinatelecom"
2) "tencent"
3) "alibaba"
4) "baidu"
5) "bytedance"
#元素推进不存在的列表,失败,返回为0
127.0.0.1:6777> lpushx not-exist chinatelecom 
(integer) 0

这里测试下列表元素的弹出功能,涉及的指令有 lpop/rpop/rpoplpush:

# 
127.0.0.1:6777> lrange companies 0 -1
1) "chinatelecom"
2) "tencent"
3) "alibaba"
4) "baidu"
5) "bytedance"
# 左端弹出操作,返回结果为弹出元素
127.0.0.1:6777> lpop companies
"chinatelecom"
127.0.0.1:6777> lrange companies 0 -1
1) "tencent"
2) "alibaba"
3) "baidu"
4) "bytedance"
# 右端弹出操作
127.0.0.1:6777> rpop companies
"bytedance"
127.0.0.1:6777> lrange companies 0 -1
1) "tencent"
2) "alibaba"
3) "baidu"
# 右端弹出左端推入
127.0.0.1:6777> lpush list1 a1 a2 a3
(integer) 3
127.0.0.1:6777> lpush list2 b1 b2 b3
(integer) 3
127.0.0.1:6777> lrange list1 0 -1
1) "a3"
2) "a2"
3) "a1"
127.0.0.1:6777> lrange list2 0 -1
1) "b3"
2) "b2"
3) "b1"
127.0.0.1:6777> rpoplpush list1 list2
"a1"
# 从list1的右端弹出,推入到list2的左端
127.0.0.1:6777> lrange list1 0 -1
1) "a3"
2) "a2"
127.0.0.1:6777> lrange list2 0 -1
1) "a1"
2) "b3"
3) "b2"
4) "b1"

llen、lrange 和 lindex 指令:

# 
127.0.0.1:6777> llen companies
(integer) 3
127.0.0.1:6777> lrange companies 0 -1
1) "tencent"
2) "alibaba"
3) "baidu"
127.0.0.1:6777> lrange companies 0 1
1) "tencent"
2) "alibaba"
# 超出列表索引范围,返回nil
127.0.0.1:6777> lindex companies 3
(nil)
127.0.0.1:6777> lindex companies 2
"baidu"
127.0.0.1:6777> lindex companies 0
"tencent"
# 支持负数,倒着数
127.0.0.1:6777> lindex companies -2
"alibaba"

测试 lset/linsert/ltrim/lrem 指令:

# 
127.0.0.1:6777> lset companies 2 meituan
OK
# 设置第三个元素为美团
127.0.0.1:6777> lrange companies 0 -1
1) "tencent"
2) "alibaba"
3) "meituan"
127.0.0.1:6777> linsert companies after meituan xiaomi
(integer) 4
127.0.0.1:6777> lrange companies 0 -1
1) "tencent"
2) "alibaba"
3) "meituan"
4) "xiaomi"
# 插入指令:insert list before|after 目标元素 插入元素
127.0.0.1:6777> linsert companies before xiaomi jingdong
(integer) 5
127.0.0.1:6777> lrange companies 0 -1
1) "tencent"
2) "alibaba"
3) "meituan"
4) "jingdong"
5) "xiaomi"
# 只保留1-3的值,其余全部去掉
127.0.0.1:6777> ltrim companies 1 3
OK
127.0.0.1:6777> lrange companies 0 -1
1) "alibaba"
2) "meituan"
3) "jingdong"
127.0.0.1:6777> lpush test_rem a a a b a c a a a
(integer) 9
# 0 表示的是删除所有a元素
127.0.0.1:6777> lrem test_rem 0 a
(integer) 7
127.0.0.1:6777> lrange test_rem 0 -1
1) "c"
2) "b"
127.0.0.1:6777> rpop test_rem
"b"
127.0.0.1:6777> rpop test_rem
"c"
# 重新赋值
127.0.0.1:6777> lpush test_rem a a a b a c a a a
(integer) 9
# 正数3表示从左向右,删除3个a
127.0.0.1:6777> lrem test_rem 3 a
(integer) 3
127.0.0.1:6777> lrange test_rem 0 -1
1) "c"
2) "a"
3) "b"
4) "a"
5) "a"
6) "a"
# 负数3表示从右向左,删除3个a
127.0.0.1:6777> lrem test_rem -3 a
(integer) 3
127.0.0.1:6777> lrange test_rem 0 -1
1) "c"
2) "a"
3) "b"

这种命令行式的操作是不是非常简单?我们通过几次实验就能大致理解和掌握相关的指令含义。

2.4 集合

这个类型和 Python 的集合类型非常类似,Redis 也提供了丰富的指令对该类型的数据进行操作。首先来看常用的集合相关的指令:

指令 含义
sadd 将元素添加到集合
srem 从集合中移除元素
smove 将元素从一个集合移动到另一个集合中
smembers 获取集合包含的所有元素
scard 获取集合包含的元素数量
sismember 检查给定元素是否存在于集合中
srandmember 从集合中随机获取一个元素
spop 随机地从集合中移除指定数量的元素
sinter/sinterstore 对集合执行交集计算
sunion/sunionstore 对集合执行并集计算
sdiff/sdiffstore 对集合执行差集计算

下面继续开启 Redis 的实战,请仔细阅读下面的指令操作以及相关的结果输出,重要的地方我会做好注释。

sadd 指令添加集合数据:

# 
127.0.0.1:6777> sadd databases 'mysql' 'oracle' 'redis' 'mongodb' 'xxxx'
(integer) 5
# srem指令移除xxxx元素
127.0.0.1:6777> srem databases xxxx
(integer) 1
# smembers指令查看集合内的所有元素
127.0.0.1:6777> smembers databases
1) "mongodb"
2) "redis"
3) "mysql"
4) "oracle"

smove 指令,将 databases 集合中的 redis 移动到 nosql 集合中:

127.0.0.1:6777> sadd nosql cassandra hbase
(integer) 2
# 
127.0.0.1:6777> smove databases nosql redis
(integer) 1
127.0.0.1:6777> smembers databases
1) "mysql"
2) "mongodb"
3) "oracle"
127.0.0.1:6777> smembers nosql
1) "cassandra"
2) "hbase"
3) "redis"

scard 指令查看集合内元素个数:

# 
127.0.0.1:6777> scard nosql
(integer) 3
# sismember判断元素是否在集合内,1表示在,0表示不在
127.0.0.1:6777> sismember nosql redis
(integer) 1
127.0.0.1:6777> sismember databases redis
(integer) 0

srandmember 指令返回集合内随机的元素,但是元素不会被移除:

# 
127.0.0.1:6777> srandmember databases
"oracle"
127.0.0.1:6777> srandmember databases
"mongodb"
127.0.0.1:6777> srandmember databases
"mysql"
127.0.0.1:6777> srandmember databases
"mysql"
127.0.0.1:6777> smembers databases
1) "mysql"
2) "mongodb"
3) "oracle"
# 给集合databases添加两个元素
127.0.0.1:6777> sadd databases 'test1' 'no'
(integer) 2
# 随机从databases集合中弹出一个元素
127.0.0.1:6777> spop databases 
"mysql"
# 随机从databases集合中弹出两个元素
127.0.0.1:6777> spop databases 2
1) "test1"
2) "mongodb"
# spop指令会使得集合移除相应的弹出元素
127.0.0.1:6777> smembers databases
1) "no"
2) "oracle"

计算集合的交并查:

# 
127.0.0.1:6777> sadd test2 a1 a2 b1 b2 b3
(integer) 5
127.0.0.1:6777> sinter test1 test2
1) "a1"
2) "a2"
# sinterstore指令会将集合test1和test2的交集保存到集合store_inter中
127.0.0.1:6777> sinterstore store_inter test1 test2
(integer) 2
127.0.0.1:6777> smembers store_inter 
1) "a1"
2) "a2"
# sunion指令求两个集合的并集
127.0.0.1:6777> sunion test1 test2
1) "b2"
2) "b3"
3) "b1"
4) "a3"
5) "a1"
6) "a2"
# sunionstore指令和sinterstore指令类似,将结果保存到另一个集合中
127.0.0.1:6777> sunionstore store_union test1 test2
(integer) 6
127.0.0.1:6777> smembers store_union
1) "b2"
2) "b3"
3) "b1"
4) "a3"
5) "a1"
6) "a2"
# sdiff指令表示的是集合test1有而集合test2中没有的元素
127.0.0.1:6777> sdiff test1 test2
1) "a3"
127.0.0.1:6777> sdiffstore store_diff test1 test2
(integer) 1
127.0.0.1:6777> smembers store_diff
1) "a3"

经过上面一系列实战之后,是不是对 Redis 有了初步的认识?Redis 的使用是不是非常简单?当然 Redis 服务提供的指令还有很多,涉及许多方面。接下来我们将会介绍 Redis 5中新增的一个重要的数据结构:流 (stream)

2.5 流

流 (stream) 是Redis 5.0 中新增的数据结构,我们来看看 Redis 为该数据结构提供的相关指令:

指令 含义
xadd 追加新元素到流的末尾
xtrim 对流进行裁剪
xdel 移除指定元素
xlen 获取流包含的元素数量
xrange/xrevrange 访问流中元素
xread 以阻塞或非阻塞方式获取流元素
xgroup 创建消费者组
xreadgroup 读取消费者组
xack 确认消费消息
xpending 显示待处理消息的相关信息
xclaim 转移消息的归属权
xinfo 查看流和消费者组的相关信息

stream 指令的后半部分关于消费者组的,需要提前了解一些基本的消息队列知识才能上手实践。这里我简单实战下前半部分的指令,理解流的基本使用,和前面的列表非常类似。

xadd指令给一个流添加新元素,xrange可以查看流中的元素,这里指定流元素的 id=1000,流元素就是 {k1:value1,k2:value2}:

# 
127.0.0.1:6777> xadd s1 100000 k1 value1 k2 value2
"100000-0"
127.0.0.1:6777> xrange s1 - +
1) 1) "100000-0"
   2) 1) "k1"
      2) "value1"
      3) "k2"
      4) "value2"
# 用*代替id,这样会根据当前时间戳自动生成一个id,{k3:value3}就是流元素
127.0.0.1:6777> xadd s1 * k3 value3 
"1593436935752-0"
# 下一个插入的流元素id一定要大于流最后的元素id
127.0.0.1:6777> xadd s1 1000 k4 value4 
(error) ERR The ID specified in XADD is equal or smaller than the target stream top item
127.0.0.1:6777> 
# 都这样了,就只好使用默认生成
127.0.0.1:6777> xadd s1 * k4 value4 
"1593437129285-0"

# xrange指令用于显示指定范围的流元素,用id来作为范围。-表示最小的值,+表示最大的id值
127.0.0.1:6777> xrange s1 - +
1) 1) "100000-0"
   2) 1) "k1"
      2) "value1"
      3) "k2"
      4) "value2"
2) 1) "1593436935752-0"
   2) 1) "k3"
      2) "value3"
3) 1) "1593437129285-0"
   2) 1) "k4"
      2) "value4"
# 使用count限制返回流元素的个数
127.0.0.1:6777> xrange s1 - + count 2
1) 1) "100000-0"
   2) 1) "k1"
      2) "value1"
      3) "k2"
      4) "value2"
2) 1) "1593436935752-0"
   2) 1) "k3"
      2) "value3"

下面演示 xtrim/xdel/xlen 指令的使用:

# 
127.0.0.1:6777> xlen s1
(integer) 3
# 为了测试方便,再添加两个流元素
127.0.0.1:6777> xadd s1 * k5 value5 
"1593437712761-0"
127.0.0.1:6777> xadd s1 * k6 value6 
"1593437717879-0"
127.0.0.1:6777> xlen s1
(integer) 5
# 修改s1流,将元素裁剪到4个,这样就会删除最开头的元素
127.0.0.1:6777> xtrim s1 maxlen 4
(integer) 1
127.0.0.1:6777> xrange s1 - +
1) 1) "1593436935752-0"
   2) 1) "k3"
      2) "value3"
2) 1) "1593437129285-0"
   2) 1) "k4"
      2) "value4"
3) 1) "1593437712761-0"
   2) 1) "k5"
      2) "value5"
4) 1) "1593437717879-0"
   2) 1) "k6"
      2) "value6"
# 删除流id为1593437129285-0的元素
127.0.0.1:6777> xdel s1 1593437129285-0
(integer) 1
127.0.0.1:6777> xrange s1 - +
1) 1) "1593436935752-0"
   2) 1) "k3"
      2) "value3"
2) 1) "1593437712761-0"
   2) 1) "k5"
      2) "value5"
3) 1) "1593437717879-0"
   2) 1) "k6"
      2) "value6"
      
# xread 指令从流 s1 中获取流 id>1593436935752-0 的元素,最多2个
127.0.0.1:6777> xread count 2 streams s1 1593436935752-0
1) 1) "s1"
   2) 1) 1) "1593437712761-0"
         2) 1) "k5"
            2) "value5"
      2) 1) "1593437717879-0"
         2) 1) "k6"
            2) "value6"

涉及更多的指令细节以及消费者组的指令使用,请参考 <<Redis使用手册>> 一书。

2.6 其他类型

限于篇幅,这里没有介绍到 Redis 的有序集合类型、位图和地理坐标等相关指令。读者可以自行参考文献2,里面详细介绍了 Redis 的大部分指令并对指令操作进行了绘图说明。

3. Python 实战 redis

Python 操作 redis 数据库也是非常简单的,我们有现成的第三方模块-redis,它实现了绝大部分官方命令并使用官方的语法和命令,使用起来和我们在命令行上操作 redis 数据库一致。首先来安装 Python 的 redis 模块:

[root@server2 ~]# pip3 install redis
WARNING: Running pip install with root privileges is generally not a good idea. Try `pip3 install --user` instead.
Collecting redis
  Downloading http://mirrors.cloud.aliyuncs.com/pypi/packages/a7/7c/24fb0511df653cf1a5d938d8f5d19802a88cef255706fdda242ff97e91b7/redis-3.5.3-py2.py3-none-any.whl (72kB)
    100% |████████████████████████████████| 81kB 17.0MB/s 
Installing collected packages: redis
Successfully installed redis-3.5.3

接下来我们在 Python 的命令行中演示前面介绍的 Redis 中的字符串命令,请看完后认真实践下面的代码:

连接 redis 数据库,和 redis 服务在同一台机上:

# 
Python 3.6.8 (default, Apr  2 2020, 13:34:55) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import redis
>>> r = redis.Redis(host='localhost', port=6777, password='spyinx', db=0)
>>> r.set('hello', 'world')
True
>>> r.get('hello')
b'world'

实践 getset/mset/mget/strlen 指令:

# 
>>> r.getset('hello', 'new world')
b'world'
>>> r.get('hello')
b'new world'
>>> r.mset({'key1':'value1', 'key2':'value2', 'key3':'value3'})
True
>>> r.mget(['key1', 'key2', 'key3'])
[b'value1', b'value2', b'value3']
>>> r.strlen('key1')
6

实践前面的 getrange/setrange 指令:

# 
>>> r.getrange("key1", 0, 3)
b'valu'
>>> r.getrange("key1", -3, -1)
b'ue1'
>>> r.setrange("key1", 2, 'xxx')
6
>>> r.get("key1")
b'vaxxx1'

实践 incrby/decrby/incrbyfloat 指令:

# 
>>> r.set("number", 100)
True
>>> r.get("number")
b'100'
>>> r.incrby("number", 1)
101
>>> r.decrby("number", 50)
51
>>> r.get("number")
b'51'
>>> r.incrbyfloat("number", 22.1)
73.1
>>> r.incrbyfloat("number", -20.8)
52.3

上面这些基础的 api 接口是不是和 redis-cli 命令行使用起来一模一样?所以,熟练掌握了 redis-cli 的指令用法,Python 操作 redis 也不在话下。

4. 小结

本小节我们主要介绍了 redis 相关的基础指令以及如何使用 Python 操作 redis,实现缓存数据的增删改查。这些是后续学习和使用分布式爬虫框架的基础,一定要牢牢掌握。

图片描述