redis排序
排序命令
redis支持对list
,set
和sorted set
元素的排序。排序命令是sort
完整的命令格式如下:
SORT [key] [BY pattern] [LIMIT start count] [GET pattern] [ASC|DESC] [ALPHA] [STORE dstkey]
默认排序
这个是最简单的情况,没有任何选项就是简单的对集合自身元素排序并返回排序结果。下面给个例子:
lpush ml 12 -- (integer) 1 lpush ml 11 -- (integer) 2 lpush ml 23 -- (integer) 3 lpush ml 13 -- (integer) 4 sort ml -- 只指明对哪个[key]排序 -- 1. "11" -- 2. "12" -- 3. "13" -- 4. "23"
升序与降序:[asc|desc] [alpha]
-
sort默认的排序方式
asc
是从小到大排的。 -
逆序可以加上
desc
选项, -
想按字母顺序排可以加
alpha
选项
lpush mylist baidu -- (integer) 1 lpush mylist hello -- (integer) 2 lpush mylist xhan -- (integer) 3 lpush mylist soso -- (integer) 4 sort mylist -- 某些情况下禁用,非double必须用 alpha 选项 -- 1. "soso" -- 2. "xhan" -- 3. "hello" -- 4. "baidu" sort mylist alpha -- 1. "baidu" -- 2. "hello" -- 3. "soso" -- 4. "xhan" sort mylist desc alpha -- 1. "xhan" -- 2. "soso" -- 3. "hello" -- 4. "baidu"
组合模式:[by pattern]
除了可以按集合元素自身值排序外,还可以将集合元素内容按照给定
[by pattern]
组合成新的key,并按照新key中对应的内容进行排序。
LRANGE [keyname] 0 -1 -- 1. "11" -- 2. "12" -- 3. "13" -- 4. "23" set name11 nihao set name12 wo set name13 shi set name23 lala sort ml by name* alpha -- 1. "13" -- 2. "23" -- 3. "11" -- 4. "12"
*
代表了ml
中的元素值,所以这个排序是按照name12 name13 name23 name23
这四个
key对应值排序的,当然返回的还是排序后ml
集合中的元素
sort ml by name* get name* get #
为什么会按照shi lala nihao wo
的顺序排下来,
这个跟单纯的排序name*
和name * alpha
的结果都不一样。
这个问题要从redis的实现逻辑上来分析了
-
list在插入后默认是按照时间的先后反序排列的
13 23 11 12
。 -
sort m1 by name*
确定是会按照name*
的值进行排序的。 但当name*
对应的值不是数值型并且没有设置alpha
的时候会导致排序分值都是相同的。 因为程序将把name*
对应的值尝试转换为数值型。 -
这就会导致
sort ml by name*
会按照ml的自然顺序进行排列了
if (alpha) { if (sortby) vector[j].u.cmpobj = getDecodedObject(byval); } else { if (byval->encoding == REDIS_ENCODING_RAW) { vector[j].u.score = strtod(byval->ptr, NULL); } else if (byval->encoding == REDIS_ENCODING_INT) { /* Don't need to decode the object if it's * integer-encoded (the only encoding supported) so * far. We can just cast it */ vector[j].u.score = (long)byval->ptr; } else { redisAssert(1 != 1); } }
取得KEY:[GET pattern]
上面的例子都是返回的ml
集合中的元素。我们也可以通过get
选项去获取指定pattern
作为新key
对应的值。
sort ml by name* get name* alpha -- 1. "lala" -- 2. "nihao" -- 3. "shi" -- 4. "wo"
这次返回的就不在是ml
中的元素了,而是name12 name13 name23 name23
对应的值。
当然排序是按照name12 name13 name23 name23
值并根据字母顺序排的。
另外get
选项可以有多个。看例子(#
特殊符号引用的是原始集合也就是ml
)
sort ml by name* get name* get # alpha -- 1. "lala" -- 2. "23" -- 3. "nihao" -- 4. "11" -- 5. "shi" -- 6. "13" -- 7. "wo" -- 8. "12"
最后在还有一个引用hash
类型字段的特殊字符->
,下面是例子
hset user1 name hanjie -- (integer) 1 hset user11 name hanjie -- (integer) 1 hset user12 name 86 -- (integer) 1 hset user13 name lxl -- (integer) 1 sort ml get user*->name -- 1. "hanjie" -- 2. "86" -- 3. "lxl" -- 4. (nil)
很容易理解,注意当对应的user23
不存在时候返回的是nil
限制范围:[LIMIT start count]
上面例子返回结果都是全部。limit
选项可以限定返回结果的数量。例子
sort ml get name* limit 1 2 -- 1. "wo" -- 2. "shi"
start
下标是从0
开始的,这里的limit
选项意思是从第二个元素开始获取2
个
保存排序:[STORE dstkey]
如果对集合经常按照固定的模式去排序,那么把排序结果缓存起来会减少不少cpu开销。
使用store
选项可以将排序内容保存到指定key
中。保存的类型是list
:
sort ml get name* limit 1 2 store cl -- (integer) 2 type cl -- list lrange cl 0 -1 -- 1. "wo" -- 2. "shi"
这个例子我们将排序结果保存到了cl中
多机集群排序
对分散在不同服务器的数据进行排序
如果我们有多个redis server的话,不同的key可能存在于不同的server上。
比如name12 name13 name23 name23
,很有可能分别在四个不同的server上存贮着。
这种情况会对排序性能造成很大的影响。
redis作者在他的blog上提到了这个问题的解 决办法,就是通过key tag
将需要排序的
key都放到同一个server上 。
由于具体决定哪个key存在哪个服务器上一般都是在client端hash的办法来做的。
我们可以通过只对key的部分进行hash。
举个例子假如我们 的client如果发现key中包含[]
。
那么只对key中[]
包含的内容进行hash。
我们将四个name相关的key,都这样命名[name]12 [name]13 [name]23 [name]23
,
于是client 程序就会把他们都放到同一server上。不知道jredis实现了没。
排序耗时卡住单线程
如果要sort的集合非常大的话排序就会消耗很长时间。 由于redis单线程的,所以长时间的排序操作会阻塞其他client的 请求。 解决办法是通过主从复制机制将数据复制到多个slave上。 然后我们只在slave上做排序操作。并进可能的对排序结果缓存。
另外就是一个方案是就 是采用sorted set
对需要按某个顺序访问的集合建立索引。
实例:
sadd tom:friend:list 123 -- tom的好友列表 里面是好友的uid sadd tom:friend:list 456 sadd tom:friend:list 789 sadd tom:friend:list 101 set uid:sort:123 1000 -- uid对应的成绩 set uid:sort:456 6000 set uid:sort:789 100 set uid:sort:101 5999 set uid:123 "{'uid':123,'name':'lucy'}" -- 增加uid对应好友信息 set uid:456 "{'uid':456,'name':'jack'}" set uid:789 "{'uid':789,'name':'marry'}" set uid:101 "{'uid':101,'name':'icej'}"
从好友列表中获得id
与uid:sort
字段匹配后排序。并根据排序后的顺序,
用key在uid表获得信息
sort tom:friend:list by uid:sort:* get uid:* -- 1. {'uid':789,'name':'marry'} -- 2. {'uid':123,'name':'lucy'} -- 3. {'uid':101,'name':'icej'} -- 4. {'uid':456,'name':'jack'} sort tom:friend:list by uid:sort:* get uid:* get uid:sort:* -- 1. {'uid':789,'name':'marry'} -- 2. 100 -- 3. {'uid':123,'name':'lucy'} -- 4. 1000 -- 5. {'uid':101,'name':'icej'} -- 6. 5999 -- 7. {'uid':456,'name':'jack'} -- 8. 6000
redis sort 对应的javaapi
最近需要用到redis的排序方法,在网上看到的基本上都是redis 客户端的命令, 没有对应成相应的Java api,今天写了两个单元测试, 关于redis的list结合hash的排序和set结合String的排序,希望家多多提意见, 有其他更好的redis 排序请指导。谢谢。
A:set结合String的排序
jedis.sadd("tom:friend:list", "123"); // tom的好友列表 jedis.sadd("tom:friend:list", "456"); jedis.sadd("tom:friend:list", "789"); jedis.sadd("tom:friend:list", "101"); jedis.set("uid:sort:123", "1000"); // 好友对应的成绩 jedis.set("uid:sort:456", "6000"); jedis.set("uid:sort:789", "100"); jedis.set("uid:sort:101", "5999"); jedis.set("uid:123", "{'uid':123,'name':'lucy'}"); // 好友的详细信息 jedis.set("uid:456", "{'uid':456,'name':'jack'}"); jedis.set("uid:789", "{'uid':789,'name':'marry'}"); jedis.set("uid:101", "{'uid':101,'name':'icej'}"); SortingParams sortingParameters = new SortingParams(); // sortingParameters.desc(); // sortingParameters.limit(0, 2); sortingParameters.get("uid:*"); // sortingParameters.get("uid:sort:*"); sortingParameters.by("uid:sort:*"); // 对应的redis 命令是./redis-cli sort tom:friend:list by uid:sort:* get uid:* List<String> result=jedis.sort("tom:friend:list", sortingParameters); for(String item:result){ System.out.println("item..."+item); }
打印输出为:
item...{'uid':789,'name':'marry'} item...{'uid':123,'name':'lucy'} item...{'uid':101,'name':'icej'} item...{'uid':456,'name':'jack'}
2:
jedis.sadd("tom:friend:list", "123"); // tom的好友列表 jedis.sadd("tom:friend:list", "456"); jedis.sadd("tom:friend:list", "789"); jedis.sadd("tom:friend:list", "101"); jedis.set("uid:sort:123", "1000"); // 好友对应的成绩 jedis.set("uid:sort:456", "6000"); jedis.set("uid:sort:789", "100"); jedis.set("uid:sort:101", "5999"); jedis.set("uid:123", "{'uid':123,'name':'lucy'}"); // 好友的详细信息 jedis.set("uid:456", "{'uid':456,'name':'jack'}"); jedis.set("uid:789", "{'uid':789,'name':'marry'}"); jedis.set("uid:101", "{'uid':101,'name':'icej'}"); SortingParams sortingParameters = new SortingParams(); // sortingParameters.desc(); // sortingParameters.limit(0, 2); sortingParameters.get("uid:*"); sortingParameters.get("uid:sort:*"); sortingParameters.by("uid:sort:*");
对应的redis 命令是
./redis-cli sort tom:friend:list by uid:sort:* get uid:* get uid:sort:*
List<String> result=jedis.sort("tom:friend:list", sortingParameters); for(String item:result){ System.out.println("item..."+item); }
打印输出为:
item...{'uid':789,'name':'marry'} item...100 item...{'uid':123,'name':'lucy'} item...1000 item...{'uid':101,'name':'icej'} item...5999 item...{'uid':456,'name':'jack'} item...6000
B:list结合hash的排序
jedis.hset("userrr1", "name", "hanjie"); jedis.hset("userrr11", "name", "hanjie"); jedis.hset("userrr12", "name", "86"); jedis.hset("userrr13", "name", "x86"); jedis.lpush("ml", "12"); jedis.lpush("ml", "11"); jedis.lpush("ml", "23"); jedis.lpush("ml", "13"); SortingParams sortingParameters = new SortingParams(); sortingParameters.get("userrr*->name"); List<String> result=jedis.sort("ml", sortingParameters); for(String item:result){ System.out.println("item...."+item); }
对应的redis客户端命令是:sort ml get user*->name