@Chiang
2021-08-08T10:45:23.000000Z
字数 2774
阅读 396
Redis操作
2021-08
Redis 是由意大利开发者 Salvatore Sanfilippo(antirez)通过 C 语言开发的、基于内存的、可持久化的开源键值对存储数据库(英文全称是 REmote DIctionary Server,中文译作远程字典服务器),由于其简单易用、高性能、支持丰富的数据结构和原子操作,已逐渐成为目前互联网最流行的存储中间件解决方案,被广泛应用于缓存
、NoSQL
、消息队列
等技术领域。
网上关于 Redis 的简介和安装教程很多,这里不过多介绍了。如果是在 Mac 本地安装的话,可以使用 Homebrew 包管理器进行快速安装:
brew install redis
然后通过 redis-server
即可启动 Redis 服务器。
不过,不管是服务器还是本地,学院君更推荐使用 Docker 进行快速安装和部署,相关的教程网上也很多,我本地使用的是 Laradock 这个 PHP Docker 集成开发环境,通过如下命令即可启动 Redis 容器:
docker-compose up -d redis
启动成功后,就可以通过 Redis Docker 容器提供的客户端命令连接到在该容器中启动的 Redis 服务器了:
docker-compose exec redis redis-cli
你可以通过如下基准测试命令简单测试下部署在本地 Docker 容器中的 Redis 服务器同时处理 10 万个并发请求的性能:
docker-compose exec redis redis-benchmark -n 100000 -q
Redis 目前支持的数据结构包含以下五种:
- String:字符串
- List:列表
- Set:集合
- SortedSet:有序集合
- Hash:哈希字典
首先来看最简单的字符串。
我们可以通过 SET
指令设置指定字符串的值:
set website `xueyuanjun.com`
然后通过 GET 指令获取该字符串的值:
get website
非常简单,我们比较熟知的应用缓存功能通常都是基于这种字符串键值对实现的,当然了,很多缓存键存在有效期的概念,我们在设置缓存键值的时候,可以通过额外的 EX
参数指定有效期(单位:秒):
set username `xueyuanjun` ex 3
这里我们设置 username
有效期是 3 秒,过了 3 秒后就取不到对应的键值了,因为过期后该键值对会自动销毁,免除了我们自己去维护的麻烦,非常方便。
除了字符串格式的值外,还支持数字格式的值,我们可以利用这个特性实现计数器功能,比如浏览数、购买数、点赞数等,这个时候,我们可以使用 INCR 命令来初始化对应的键值:
incr site_visits
获取还是通过 GET
指令,默认步长是 1,即每次调用 INCR
指令会将对应的键值 +1,此外,还可以通过 INCRBY
显式设置步长,比如我们在前面的基础上将步长设置为 5,可以这么做:
incrby site_visits 5
与 INCR
/INCRBY
相对的是 DECR
/DECRBY
指令,即对给定键值做 -1 操作或者做给定步长的减少操作(对应取消点赞之类的操作)。
当然,字符串键值还支持很多其他指令,这里就不一一列出了,你可以在 Redis 官网 Commands 页面进行查阅(在 Filter by group 下拉框选择对应的数据格式即可查看该数据格式支持的所有指令).
接下来,我们来看另一个常见的数据结构 —— 列表。
列表类似于我们前面数据结构中介绍的链表,我们可以将元素添加到列表(支持从头部添加也支持从尾部添加),也可以从列表中移除并获取某个元素(支持从头部移除也支持从尾部移除),还可以读取整个列表的元素。
显然,我们可以基于 Redis 的列表实现类似栈和队列的数据结构,基于 Redis 的消息队列也正是基于这个数据结构实现的(后面介绍消息队列实现时我们会详细介绍)。
以队列为例,我们从尾部添加数据,从头部读取数据。
先通过 RPUSH
指令推送数据到队列末尾(通过键名 skills
命名):
rpush skills `php`
推送成功后,你可以通过 LRANGE
指令获取列表中的所有元素(0 表示起始位置索引、-1 表示结束位置索引,你也可以通过其他索引值获取给定区间元素):
lrange skills 0 -1
然后通过 LPOP
指令从队列头部移除并获取元素:
lpop skills
上述数据添加和获取符合「先入先出」规则,所以是一个标准的队列结构。
当然了,还有与之相对的 RPOP
和 LPUSH
指令,你可以基于 LPUSH
和 LPOP
实现栈这种数据结构,这里就不单独演示了。
更多 Redis 列表支持的指令可以在 Redis 官网 Commands 页面查阅:
学过高中数学的同学对集合这个数学概念都不陌生,集合拥有确定性、互异性和无序性,对应到 Redis 里面的集合数据结构也是一样。
注:与集合不同,列表可以包含重复元素,列表内的元素顺序也和添加时的顺序一致。
确定性很好理解,你必须将一个确定的元素值添加到 Redis 集合,而不能把一个不确定的变量添加进去。
Redis 集合中所有元素都是互异的,即任意一个元素都是唯一的,当我们尝试向集合中添加相同元素时,会忽略后续添加的值,比如我们通过 SADD
指令尝试向 skills
集合添加两个相同的 PHP
元素:
sadd skills `php`
第二次添加返回值是 0,表示添加失败,通过 SMEMBERS 列举集合时,也只有一个元素,表明 Redis 集合确实会自动帮我们做去重处理:
smembers skills
另外,Redis 集合具备无序性,所以当你向 skills
集合添加多个元素时,返回的结果和添加时的排序并不一致:
这当然是为了降低底层数据维护成本从而提升性能考虑。因此,我们如果尝试从 Redis 集合获取元素时,返回的结果值也是随机的,并不能确保给定位置的值是添加序列时的值,你可以看到 Redis Set 指令集中的 SPOP 返回值也确实是随机的:
当业务场景需要对集合数据做去重处理而又不需要确保数据顺序时,Redis 集合是个不错的选择。
但有些时候我们的业务场景既要去重,又要确保排序,比如一些热门数据排行榜的实现,因此,Redis 还支持另一种数据结构 —— 有序集合。
顾名思义,有序集合就是在集合的基础上让内部元素有确定的排序,不过这个排序不是按照添加时的顺序,而是通过额外的排序字段值指定,还是以上面的例子为例。
我们将有序集合命名为 sorted_skills
,然后通过 ZADD
指令向这个有序集合添加元素:
zadd sorted_skills 0 `php`
和 SADD
指令不同,在元素值之前插入了一个数值(score),在返回有序集合时,默认会根据这个数值进行升序排序:
参考资料:
高性能Redis