遇到这个问题,我上来就回答可以利用数据结构的特性,比如Zset的自带排序。(当时没有想到redis也是自带sort功能,对无序数据结构进行后续排序的)
先看AI的表现:
问题:Redis排序知道吗?如果我的业务场景中需要引入Redis缓存,且需要维护一定的顺序性,Redis可以通过哪些方式实现?
回答:
是的,Redis可以通过一些方式来实现有序集合的排序功能。
1. 使用有序集合
Redis中的有序集合(SortedSet)是一个键值对集合,其中每个元素都有一个分数(score),可以根据分数进行排序。
使用ZADD命令添加元素时,指定分数即可。使用ZRANGEBYSCORE命令可以按照分数区间获取元素,使用ZREVRANGEBYSCORE命令可以按照分数区间逆序获取元素。
2. 使用列表
Redis中的列表(List)可以通过LPUSH、RPUSH、LINSERT等命令添加元素。使用LINDEX命令可以获取指定位置的元素,使用LLEN命令可以获取列表长度。
可以通过设置列表元素的值来实现排序功能。例如,将列表元素设置为JSON字符串,其中包含一个需要排序的字段。然后可以使用SORT命令按照该字段进行排序。
3. 使用有序列表
Redis中的有序列表(Sorted List)是通过列表和有序集合实现的。列表中存储元素,有序集合中存储元素对应的分数。使用ZADD命令向有序集合中添加元素时,同时在列表中插入一个元素。使用ZRANGEBYSCORE命令按照分数区间获取元素时,同时获取对应的列表元素。
有序列表可以实现高效的排序功能,因为它既可以按照分数进行排序,又可以按照插入顺序进行排序。
以上是Redis实现排序的三种方式,可以根据具体业务场景选择合适的方式来实现。
我们自己在服务器上进行实践
首先开启服务器
centos上执行: redis-server
但是如果我们有自定义配置的话,一定要后接自己的配置文件地址
redis-server /usr/local/redis-5.0.8/redis.conf
再开一个客户端,执行redis-cli进入客户端。
SORT命令可以对列表类型、集合类型和有序集合类型键进行排序,并且可以完成与关系数据库中的连接查询相类似的任务。
127.0.0.1:6379> lpush mylist 4 2 5 1 3 7
(integer) 6
127.0.0.1:6379> sort mylist
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "7"
127.0.0.1:6379>
127.0.0.1:6379> sadd myset 2 8 55 5 6 6 9
(integer) 6
127.0.0.1:6379> sort myset
1) "2"
2) "5"
3) "6"
4) "8"
5) "9"
6) "55"
注意,对有序集合类型排序时会忽略元素的分数,只针对元素自身的值进行排序
127.0.0.1:6379> zadd myzset 50 2 40 3 20 1 60 6
(integer) 4
127.0.0.1:6379> zrange myzset 0 -1 withscores
1) "1"
2) "20"
3) "3"
4) "40"
5) "2"
6) "50"
7) "6"
8) "60"
127.0.0.1:6379> sort myzset
1) "1"
2) "2"
3) "3"
4) "6"
Redis中的ZRANGE指令用于获取有序集合(Sorted Set)中指定范围内的元素,按照元素的分值(score)从小到大排序。
语法如下:
ZRANGE key start stop [WITHSCORES]
参数说明:
举个例子,如果有一个有序集合scores,其中包含以下元素:
redis> ZADD scores 10 Tom
redis> ZADD scores 20 Jack
redis> ZADD scores 30 Alice
redis> ZADD scores 40 Bob
使用ZRANGE scores 0 -1指令,则会返回整个有序集合,按照分值从小到大排序:
1) "Tom"
2) "Jack"
3) "Alice"
4) "Bob"
如果使用ZRANGE scores 1 2命令,则会返回排名为1和2的元素,即"Jack"和"Alice":
1) "Jack"
2) "Alice"
如果使用ZRANGE scores 0 -1 WITHSCORES命令,则会返回整个有序集合,包含每个元素的分值:
1) "Tom"
2) "10"
3) "Jack"
4) "20"
5) "Alice"
6) "30"
7) "Bob"
8) "40"
使用sort命令配合DESC参数可以实现倒序排序。
127.0.0.1:6379> lpush mylist 1 3 5 7 1 -1 233 4
(integer) 8
127.0.0.1:6379> sort mylist desc
1) "233"
2) "7"
3) "5"
4) "4"
5) "3"
6) "1"
7) "1"
8) "-1"
使用limit可以分页显示结果。Limist offet count:我们从第offset+1元素开始输出,取count个元素
127.0.0.1:6379> sort mylist desc limit 0 2
1) "233"
2) "7"
假定有一张表的数据如下:
uid | user_name_{uid} | user_level_{uid} |
---|---|---|
1 | admin | 9999 |
2 | jack | 10 |
3 | lucky | 256 |
4 | mary | 70 |
对于上表中的数据,很多情况下在Redis 中的存储方式是:使用列表存储 uid,再分别用字符串存储 user_name_ {uid} 和 user_level_ {uid}
我们根据上述传统模式,在redis中添加数据
首先添加admin
127.0.0.1:6379> lpush uid 1
(integer) 1
127.0.0.1:6379> set user_name_1 admin
OK
127.0.0.1:6379> set user_level_1 9999
OK
127.0.0.1:6379>
再添加jack的信息
127.0.0.1:6379> lpush uid 2
(integer) 2
127.0.0.1:6379> set user_name_2 jack
OK
127.0.0.1:6379> set user_level_2 10
OK
我们分别用sort uid和sort uid by user_level_* 查看效果:
127.0.0.1:6379> sort uid
1) "1"
2) "2"
3) "3"
127.0.0.1:6379> sort uid by user_level_*
1) "2"
2) "3"
3) "1"
127.0.0.1:6379>
对by pattern的解释:
user_level_* 是一个占位符, 它先取出 uid 中的值, 然后再用这个值来查找相应的键。
比如在对 uid 列表进行排序时, 程序就会先取出 uid 的值 1 、 2 、 3 、 4 , 然后使用 user_level_1 、 user_level_2 、 user_level_3 和 user_level_4 的值作为排序 uid 的权重。
对于上述根据pattern进行排序,我们还想知道具体的键值,那么可以引入GET选项
比如,以下代码先排序uyid,然后,再取出对应键user_name_{uid}的值:
127.0.0.1:6379> sort uid
1) "1"
2) "2"
3) "3"
127.0.0.1:6379> sort uid get user_name_*
1) "admin"
2) "jack"
3) "lucky"
我们一条语句也可以跟上多个GET参数
比如说, 以下代码就按 uid 分别获取 user_level_{uid} 和 user_name_{uid} :
127.0.0.1:6379> sort uid get user_level_* GET user_name_*
1) "9999"
2) "admin"
3) "10"
4) "jack"
5) "256"
6) "lucky"
GET 有一个额外的参数规则,那就是 —— 可以用 # 获取被排序键的值。
以下代码就将 uid 的值、及其相应的 user_level_* 和 user_name_* 都返回为结果:
127.0.0.1:6379> sort uid GET # GET user_level_* get user_name_*
1) "1"
2) "9999"
3) "admin"
4) "2"
5) "10"
6) "jack"
7) "3"
8) "256"
9) "lucky"
首先我们用一个列表来缓存结果
默认情况下, SORT 操作只是简单地返回排序结果,并不进行任何保存操作。
通过给 STORE 选项指定一个 key 参数,可以将排序结果保存到给定的键上。
如果被指定的 key 已存在,那么原有的值将被排序结果覆盖。
127.0.0.1:6379> rpush mynum 1 3 5
(integer) 3
127.0.0.1:6379> lrange mynum 0 -1
1) "1"
2) "3"
3) "5"
127.0.0.1:6379> rpush mynum 2 4 6
(integer) 6
127.0.0.1:6379> lrange mynum 0 -1
1) "1"
2) "3"
3) "5"
4) "2"
5) "4"
6) "6"
127.0.0.1:6379> sort mynum store mysortednum
(integer) 6
127.0.0.1:6379> lrange mysortednum 0 -1
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
小节:
SORT是Redis中最强大最复杂的命令之一,如果使用不好很容易成为性能瓶颈。
SORT命 令的时间复杂度是O(n+mlog(m)),其中n表示要排序的列表(集合或有序集合)中的元素个 数,m表示要返回的元素个数。
所以开发中使用SORT命令时需要注意以下几点。
(1)尽可能减少待排序键中元素的数量(使N尽可能小)。
(2)使用LIMIT参数只获取需要的数据(使M尽可能小)。
(3)如果要排序的数据数量较大,尽可能使用STORE参数将结果缓存。
参考资料:
Redis 排序功能 -- SORT_51CTO博客_redis sort