SORT key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern …]] [ASC | DESC] [ALPHA] [STORE destination]

可用版本: >= 1.0.0
时间复杂度: O(N+M*log(M)), N 为要排序的列表或集合内的元素数量, M 为要返回的元素数量。如果只是使用 SORT 命令的 GET 选项获取数据而没有进行排序,时间复杂度 O(N)。

返回或保存给定列表、集合、有序集合 key 中经过排序的元素。

排序默认以数字作为对象,值被解释为双精度浮点数,然后进行比较。

一般 SORT 用法

最简单的 SORT 使用方法是 SORT keySORT key DESC

  • SORT key 返回键值从小到大排序的结果。

  • SORT key DESC 返回键值从大到小排序的结果。

假设 today_cost 列表保存了今日的开销金额,那么可以用 SORT 命令对它进行排序:

  1. # 开销金额列表
  2.  
  3. redis> LPUSH today_cost 30 1.5 10 8
  4. (integer) 4
  5.  
  6. # 排序
  7.  
  8. redis> SORT today_cost
  9. 1) "1.5"
  10. 2) "8"
  11. 3) "10"
  12. 4) "30"
  13.  
  14. # 逆序排序
  15.  
  16. redis 127.0.0.1:6379> SORT today_cost DESC
  17. 1) "30"
  18. 2) "10"
  19. 3) "8"
  20. 4) "1.5"

使用 ALPHA 修饰符对字符串进行排序

因为 SORT 命令默认排序对象为数字,当需要对字符串进行排序时,需要显式地在 SORT 命令之后添加 ALPHA 修饰符:

  1. # 网址
  2.  
  3. redis> LPUSH website "www.reddit.com"
  4. (integer) 1
  5.  
  6. redis> LPUSH website "www.slashdot.com"
  7. (integer) 2
  8.  
  9. redis> LPUSH website "www.infoq.com"
  10. (integer) 3
  11.  
  12. # 默认(按数字)排序
  13.  
  14. redis> SORT website
  15. 1) "www.infoq.com"
  16. 2) "www.slashdot.com"
  17. 3) "www.reddit.com"
  18.  
  19. # 按字符排序
  20.  
  21. redis> SORT website ALPHA
  22. 1) "www.infoq.com"
  23. 2) "www.reddit.com"
  24. 3) "www.slashdot.com"

如果系统正确地设置了 LC_COLLATE 环境变量的话,Redis能识别 UTF-8 编码。

使用 LIMIT 修饰符限制返回结果

排序之后返回元素的数量可以通过 LIMIT 修饰符进行限制,修饰符接受 offsetcount 两个参数:

  • offset 指定要跳过的元素数量。

  • count 指定跳过 offset 个指定的元素之后,要返回多少个对象。

以下例子返回排序结果的前 5 个对象( offset0 表示没有元素被跳过)。

  1. # 添加测试数据,列表值为 1 指 10
  2.  
  3. redis 127.0.0.1:6379> RPUSH rank 1 3 5 7 9
  4. (integer) 5
  5.  
  6. redis 127.0.0.1:6379> RPUSH rank 2 4 6 8 10
  7. (integer) 10
  8.  
  9. # 返回列表中最小的 5 个值
  10.  
  11. redis 127.0.0.1:6379> SORT rank LIMIT 0 5
  12. 1) "1"
  13. 2) "2"
  14. 3) "3"
  15. 4) "4"
  16. 5) "5"

可以组合使用多个修饰符。以下例子返回从大到小排序的前 5 个对象。

  1. redis 127.0.0.1:6379> SORT rank LIMIT 0 5 DESC
  2. 1) "10"
  3. 2) "9"
  4. 3) "8"
  5. 4) "7"
  6. 5) "6"

使用外部 key 进行排序

可以使用外部 key 的数据作为权重,代替默认的直接对比键值的方式来进行排序。

假设现在有用户数据如下:

以下代码将数据输入到 Redis 中:

  1. # admin
  2.  
  3. redis 127.0.0.1:6379> LPUSH uid 1
  4. (integer) 1
  5.  
  6. redis 127.0.0.1:6379> SET user_name_1 admin
  7. OK
  8.  
  9. redis 127.0.0.1:6379> SET user_level_1 9999
  10. OK
  11.  
  12. # jack
  13.  
  14. redis 127.0.0.1:6379> LPUSH uid 2
  15. (integer) 2
  16.  
  17. redis 127.0.0.1:6379> SET user_name_2 jack
  18. OK
  19.  
  20. redis 127.0.0.1:6379> SET user_level_2 10
  21. OK
  22.  
  23. # peter
  24.  
  25. redis 127.0.0.1:6379> LPUSH uid 3
  26. (integer) 3
  27.  
  28. redis 127.0.0.1:6379> SET user_name_3 peter
  29. OK
  30.  
  31. redis 127.0.0.1:6379> SET user_level_3 25
  32. OK
  33.  
  34. # mary
  35.  
  36. redis 127.0.0.1:6379> LPUSH uid 4
  37. (integer) 4
  38.  
  39. redis 127.0.0.1:6379> SET user_name_4 mary
  40. OK
  41.  
  42. redis 127.0.0.1:6379> SET user_level_4 70
  43. OK

BY 选项

默认情况下, SORT uid 直接按 uid 中的值排序:

  1. redis 127.0.0.1:6379> SORT uid
  2. 1) "1" # admin
  3. 2) "2" # jack
  4. 3) "3" # peter
  5. 4) "4" # mary

通过使用 BY 选项,可以让 uid 按其他键的元素来排序。

比如说,以下代码让 uid 键按照 userlevel{uid} 的大小来排序:

  1. redis 127.0.0.1:6379> SORT uid BY user_level_*
  2. 1) "2" # jack , level = 10
  3. 2) "3" # peter, level = 25
  4. 3) "4" # mary, level = 70
  5. 4) "1" # admin, level = 9999

userlevel* 是一个占位符,它先取出 uid 中的值,然后再用这个值来查找相应的键。

比如在对 uid 列表进行排序时,程序就会先取出 uid 的值 1234 ,然后使用 user_level_1user_level_2user_level_3user_level_4 的值作为排序 uid 的权重。

GET 选项

使用 GET 选项,可以根据排序的结果来取出相应的键值。

比如说,以下代码先排序 uid ,再取出键 username{uid} 的值:

  1. redis 127.0.0.1:6379> SORT uid GET user_name_*
  2. 1) "admin"
  3. 2) "jack"
  4. 3) "peter"
  5. 4) "mary"

组合使用 BY 和 GET

通过组合使用 BYGET ,可以让排序结果以更直观的方式显示出来。

比如说,以下代码先按 userlevel{uid} 来排序 uid 列表,再取出相应的 username{uid} 的值:

  1. redis 127.0.0.1:6379> SORT uid BY user_level_* GET user_name_*
  2. 1) "jack" # level = 10
  3. 2) "peter" # level = 25
  4. 3) "mary" # level = 70
  5. 4) "admin" # level = 9999

现在的排序结果要比只使用 SORT uid BY userlevel* 要直观得多。

获取多个外部键

可以同时使用多个 GET 选项,获取多个外部键的值。

以下代码就按 uid 分别获取 userlevel{uid}username{uid}

  1. redis 127.0.0.1:6379> SORT uid GET user_level_* GET user_name_*
  2. 1) "9999" # level
  3. 2) "admin" # name
  4. 3) "10"
  5. 4) "jack"
  6. 5) "25"
  7. 6) "peter"
  8. 7) "70"
  9. 8) "mary"

GET 有一个额外的参数规则,那就是 —— 可以用 # 获取被排序键的值。

以下代码就将 uid 的值、及其相应的 userlevelusername 都返回为结果:

  1. redis 127.0.0.1:6379> SORT uid GET # GET user_level_* GET user_name_*
  2. 1) "1" # uid
  3. 2) "9999" # level
  4. 3) "admin" # name
  5. 4) "2"
  6. 5) "10"
  7. 6) "jack"
  8. 7) "3"
  9. 8) "25"
  10. 9) "peter"
  11. 10) "4"
  12. 11) "70"
  13. 12) "mary"

获取外部键,但不进行排序

通过将一个不存在的键作为参数传给 BY 选项,可以让 SORT 跳过排序操作,直接返回结果:

  1. redis 127.0.0.1:6379> SORT uid BY not-exists-key
  2. 1) "4"
  3. 2) "3"
  4. 3) "2"
  5. 4) "1"

这种用法在单独使用时,没什么实际用处。

不过,通过将这种用法和 GET 选项配合,就可以在不排序的情况下,获取多个外部键,相当于执行一个整合的获取操作(类似于 SQL 数据库的 join 关键字)。

以下代码演示了,如何在不引起排序的情况下,使用 SORTBYGET 获取多个外部键:

  1. redis 127.0.0.1:6379> SORT uid BY not-exists-key GET # GET user_level_* GET user_name_*
  2. 1) "4" # id
  3. 2) "70" # level
  4. 3) "mary" # name
  5. 4) "3"
  6. 5) "25"
  7. 6) "peter"
  8. 7) "2"
  9. 8) "10"
  10. 9) "jack"
  11. 10) "1"
  12. 11) "9999"
  13. 12) "admin"

将哈希表作为 GET 或 BY 的参数

除了可以将字符串键之外,哈希表也可以作为 GETBY 选项的参数来使用。

比如说,对于前面给出的用户信息表:

我们可以不将用户的名字和级别保存在 username{uid}userlevel{uid} 两个字符串键中,而是用一个带有 name 域和 level 域的哈希表 userinfo{uid} 来保存用户的名字和级别信息:

  1. redis 127.0.0.1:6379> HMSET user_info_1 name admin level 9999
  2. OK
  3.  
  4. redis 127.0.0.1:6379> HMSET user_info_2 name jack level 10
  5. OK
  6.  
  7. redis 127.0.0.1:6379> HMSET user_info_3 name peter level 25
  8. OK
  9.  
  10. redis 127.0.0.1:6379> HMSET user_info_4 name mary level 70
  11. OK

之后, BYGET 选项都可以用 key->field 的格式来获取哈希表中的域的值,其中 key 表示哈希表键,而 field 则表示哈希表的域:

  1. redis 127.0.0.1:6379> SORT uid BY user_info_*->level
  2. 1) "2"
  3. 2) "3"
  4. 3) "4"
  5. 4) "1"
  6.  
  7. redis 127.0.0.1:6379> SORT uid BY user_info_*->level GET user_info_*->name
  8. 1) "jack"
  9. 2) "peter"
  10. 3) "mary"
  11. 4) "admin"

保存排序结果

默认情况下, SORT 操作只是简单地返回排序结果,并不进行任何保存操作。

通过给 STORE 选项指定一个 key 参数,可以将排序结果保存到给定的键上。

如果被指定的 key 已存在,那么原有的值将被排序结果覆盖。

  1. # 测试数据
  2.  
  3. redis 127.0.0.1:6379> RPUSH numbers 1 3 5 7 9
  4. (integer) 5
  5.  
  6. redis 127.0.0.1:6379> RPUSH numbers 2 4 6 8 10
  7. (integer) 10
  8.  
  9. redis 127.0.0.1:6379> LRANGE numbers 0 -1
  10. 1) "1"
  11. 2) "3"
  12. 3) "5"
  13. 4) "7"
  14. 5) "9"
  15. 6) "2"
  16. 7) "4"
  17. 8) "6"
  18. 9) "8"
  19. 10) "10"
  20.  
  21. redis 127.0.0.1:6379> SORT numbers STORE sorted-numbers
  22. (integer) 10
  23.  
  24. # 排序后的结果
  25.  
  26. redis 127.0.0.1:6379> LRANGE sorted-numbers 0 -1
  27. 1) "1"
  28. 2) "2"
  29. 3) "3"
  30. 4) "4"
  31. 5) "5"
  32. 6) "6"
  33. 7) "7"
  34. 8) "8"
  35. 9) "9"
  36. 10) "10"

可以通过将 SORT 命令的执行结果保存,并用 EXPIRE key seconds 为结果设置生存时间,以此来产生一个 SORT 操作的结果缓存。

这样就可以避免对 SORT 操作的频繁调用:只有当结果集过期时,才需要再调用一次 SORT 操作。

另外,为了正确实现这一用法,你可能需要加锁以避免多个客户端同时进行缓存重建(也就是多个客户端,同一时间进行 SORT 操作,并保存为结果集),具体参见 SETNX key value 命令。

返回值

没有使用 STORE 参数,返回列表形式的排序结果。使用 STORE 参数,返回排序结果的元素数量。