zoukankan      html  css  js  c++  java
  • 【】Redis sort 排序命令详解

    转载地址:http://www.jb51.net/article/69131.htm

    本文介绍redis排序命令

    redis支持对list,set,sorted set元素的排序

    sort 排序命令格式:

    sort key [BY pattern] [LIMIT start count] [GET pattern] [ASC|DESC] [ALPHA] [STORE dstkey] 

    1) sort key (list)

    这是最简单的情况,没有任何选项对集合自身元素排序并返回排序结果,默认为value升序。

    示例:

    代码如下:


    127.0.0.1:6379> lpush mimvp 12
    (integer) 1
    127.0.0.1:6379> lpush mimvp 11
    (integer) 2
    127.0.0.1:6379> lpush mimvp 13
    (integer) 3
    127.0.0.1:6379> lpush mimvp 10
    (integer) 4
    127.0.0.1:6379> lrange mimvp 0 -1
    1) “10”
    2) “13”
    3) “11”
    4) “12”
    127.0.0.1:6379> sort mimvp
    1) “10”
    2) “11”
    3) “12”
    4) “13”
    127.0.0.1:6379> sort mimvp desc
    1) “13”
    2) “12”
    3) “11”
    4) “10”

    2) [ASC|DESC] [ALPHA]  (list)

    sort默认的排序方式(asc)是从小到大排的,当然也可以按照逆序或者按字符顺序排。

    逆序可以加上desc选项,想按字母顺序排可以加alpha选项,alpha可以和desc一起用。

    sort 默认以分数(数值)排序,字母使用默认的sort排序,会报错!

    示例:

    代码如下:


    127.0.0.1:6379> lpush mylist forum
    (integer) 1
    127.0.0.1:6379> lpush mylist proxy
    (integer) 2
    127.0.0.1:6379> lpush mylist blog
    (integer) 3
    127.0.0.1:6379> lpush mylist apptop
    (integer) 4
    127.0.0.1:6379> sort mylist
    (error) ERR One or more scores can't be converted into double
    127.0.0.1:6379> sort mylist alpha
    1) “apptop”
    2) “blog”
    3) “forum”
    4) “proxy”
    127.0.0.1:6379> sort mylist alpha desc
    1) “proxy”
    2) “forum”
    3) “blog”
    4) “apptop”
    127.0.0.1:6379> sort mylist desc alpha
    1) “proxy”
    2) “forum”
    3) “blog”
    4) “apptop”

    3) [BY pattern]   (set)

    除了可以按集合元素自身值(数字,字母)排序外,还可以将集合元素内容按照给定pattern组合成新的key,并按照新key中对应的内容进行排序。

    示例:

    代码如下:


    127.0.0.1:6379> set mimvp_12 mimvp_12
    OK
    127.0.0.1:6379> set mimvp_11 mimvp_11
    OK
    127.0.0.1:6379> set mimvp_13 mimvp_13
    OK
    127.0.0.1:6379> set mimvp_10 mimvp_10
    OK
    127.0.0.1:6379> sort mimvp by mimvp_*         // mimvp_* 是字符串,因此需要alpha
    (error) ERR One or more scores can't be converted into double
    127.0.0.1:6379> sort mimvp by mimvp_* alpha
    1) “10”
    2) “11”
    3) “12”
    4) “13”
    127.0.0.1:6379> sort mimvp by mimvp_* alpha desc
    1) “13”
    2) “12”
    3) “11”
    4) “10”


    *代表了上面示例1) mimvp已经给出的元素值,所以这个排序是按照mimvp_12, mimvp_11,mimvp_13,mimvp_10这四个key对应值排序的,但返回的仍然是排序后mimvp集合中的元素,即数值13,11,12,10,而不是mimvp_*的字符串元素。

    4) [GET pattern]

    上面的例子都是返回的mimvp集合中的数值元素,也可以通过get选项去获取指定pattern作为新key(mimvp_*)对应的字符串值。

    示例:

    代码如下:


    127.0.0.1:6379> sort mimvp by mimvp_* get mimvp_* alpha
    1) “mimvp_10″
    2) “mimvp_11″
    3) “mimvp_12″
    4) “mimvp_13″
    127.0.0.1:6379> sort mimvp by mimvp_* get mimvp_* alpha desc
    1) “mimvp_13″
    2) “mimvp_12″
    3) “mimvp_11″
    4) “mimvp_10″


    这次返回的就不在是mimvp中的元素,而是mimvp_12, mimvp_11,mimvp_13,mimvp_10对应的值,当然排序是按照mimvp_12, mimvp_11,mimvp_13,mimvp_10值并根据字母顺序排的。

    另外,get选项可以有多个,#特殊符号引用的是原始集合也就是mimvp(类似与sorted-set withscores)

    示例:

    代码如下:


    127.0.0.1:6379> sort mimvp by mimvp_* get mimvp_* get # alpha
    1) “mimvp_10″
    2) “10”
    3) “mimvp_11″
    4) “11”
    5) “mimvp_12″
    6) “12”
    7) “mimvp_13″
    8) “13”
    127.0.0.1:6379> sort mimvp by mimvp_* get mimvp_* get # alpha desc
    1) “mimvp_13″
    2) “13”
    3) “mimvp_12″
    4) “12”
    5) “mimvp_11″
    6) “11”
    7) “mimvp_10″
    8) “10”


    最后在还有一个引用hash类型字段的特殊字符 ->   (hash)

    示例1:

    代码如下:


    127.0.0.1:6379> hset user_12 name yanggang
    (integer) 1
    127.0.0.1:6379> hset user_11 name yangjie
    (integer) 1
    127.0.0.1:6379> hset user_13 name yangliang
    (integer) 1
    127.0.0.1:6379> hset user_10 name yangchuang
    (integer) 1
    127.0.0.1:6379> sort mimvp get user_*->name
    1) “yangchuang”
    2) “yangjie”
    3) “yanggang”
    4) “yangliang”
    127.0.0.1:6379> sort mimvp get user_*->name desc
    1) “yangliang”
    2) “yanggang”
    3) “yangjie”
    4) “yangchuang”


    很容易理解,如果mimvp当对应的数值不存在,则对应返回的是nil

    5) [LIMIT start count]  (limit)

    上面例子返回结果都是全部元素,limit选项可以限定返回结果的数量。

    示例:

    代码如下:


    127.0.0.1:6379> sort mimvp get mimvp_* limit 1 2
    1) “mimvp_11″
    2) “mimvp_12″
    127.0.0.1:6379> sort mimvp get mimvp_* limit 0 3
    1) “mimvp_10″
    2) “mimvp_11″
    3) “mimvp_12″


    start下标是从 0 开始,这里的limit选项(limit 1 2)意思是从第二个元素开始获取2个。

    6) [STORE dstkey]   (store)

    如果对集合经常按照固定的模式去排序,那么把排序结果缓存起来会减少不少cpu开销,使用store选项可以将排序内容保存到指定key中,保存的类型是list

    代码如下:


    127.0.0.1:6379> sort mimvp get mimvp_* limit 0 3 store mimvp_store
    (integer) 3
    127.0.0.1:6379> type mimvp_store
    list
    127.0.0.1:6379> lrange mimvp_store 0 -1
    1) “mimvp_10″
    2) “mimvp_11″
    3) “mimvp_12″


    这个例子将排序结果保存到mimvp_store中,后面使用直接从mimvp_store提取即可。

    7)  根据hash 指定域排序 获取hash对个域内容

    示例

    代码如下:

    127.0.0.1:6379> lpush sl 2 1 3

    (integer) 3

    127.0.0.1:6379> hmset ss:1 name n1 score 100
    OK
    127.0.0.1:6379> hmset ss:2 name n2 score 60
    OK
    127.0.0.1:6379> hmset ss:3 name n3 score 90
    OK
    127.0.0.1:6379> sort sl by ss:*->score desc
    1) "1"
    2) "3"
    3) "2"
    127.0.0.1:6379> sort sl by ss:*->score desc get ss:*->name
    1) "n1"
    2) "n3"
    3) "n2"
    127.0.0.1:6379> sort sl by ss:*->score desc get ss:*->name get ss:*->score
    1) "n1"
    2) "100"
    3) "n3"
    4) "90"
    5) "n2"
    6) "60"

    小结

    功能介绍完后,再讨论下关于排序的一些问题。

    如果有多个redis server的话,不同的key可能存在于不同的server上。比如mimvp_12, mimvp_11,mimvp_13,mimvp_10,很有可能分别在四个不同的server上存贮着,这种情况会对排序性能造成很大的影响。

    redis作者在他的blog上提到了这个问题的解决办法,就是通过key tag将需要排序的key都放到同一个server上 。由于具体决定哪个key存在哪个服务器上一般都是在client端hash的办法来做的,我们可以通过只对key的部分进行hash

    举个例子:

    假如我们 的client如果发现key中包含[],那么只对key中[]包含的内容进行hash,我们将四个name相关的key,都这样命名[name]12 [name]13 [name]11 [name]10,于是client 程序就会把他们都放到同一server上(不知道jredis实现了没)。

    还有一个问题也比较严重

    如果要sort的集合非常大的话排序就会消耗很长时间。由于redis单线程的,所以长时间的排序操作会阻塞其他client的 请求。解决办法是通过主从复制机制将数据复制到多个slave上。然后我们只在slave上做排序操作,并尽可能的对排序结果缓存。另外就是一个方案是就是采用sorted set对需要按某个顺序访问的集合建立索引。

    示例:

    代码如下:


    127.0.0.1:6379> sadd tom:friend:list 123        # tom的好友列表里面是好友的uid
    (integer) 1
    127.0.0.1:6379> sadd tom:friend:list 456
    (integer) 1
    127.0.0.1:6379> sadd tom:friend:list 789
    (integer) 1
    127.0.0.1:6379> sadd tom:friend:list 101
    (integer) 1
    127.0.0.1:6379> set uid:sort:123 1000            # uid对应的成绩
    OK
    127.0.0.1:6379> set uid:sort:456 6000
    OK
    127.0.0.1:6379> set uid:sort:789 100
    OK
    127.0.0.1:6379> set uid:sort:101 5999
    OK
    127.0.0.1:6379> set uid:123 “{‘uid':123,'name':'lucy'}”            # 增加uid对应好友信息
    OK
    127.0.0.1:6379> set uid:456 “{‘uid':456,'name':'jack'}”
    OK
    127.0.0.1:6379> set uid:789 “{‘uid':789,'name':'marry'}”
    OK
    127.0.0.1:6379> set uid:101 “{‘uid':101,'name':'icej'}” 
    OK
    127.0.0.1:6379> sort tom:friend:list by uid:sort:* get uid:*    # 从好友列表中获得id与uid:sort字段匹配后排序,并根据排序后的顺序,用key在uid表获得信息
    1) “{‘uid':789,'name':'marry'}”
    2) “{‘uid':123,'name':'lucy'}”
    3) “{‘uid':101,'name':'icej'}”
    4) “{‘uid':456,'name':'jack'}”
    127.0.0.1:6379> 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”

    问题与分析

    1.  sort mimvp by mimvp_* get mimvp_* get # alpha 为什么会按照 10 – 13 的顺序排下来,这个跟单纯的排序name*和name * alpha的结果都不一样

    这个问题要从redis的实现逻辑上来分析了

    a) list在插入后,默认是按照时间的先后反序排列的(先进后出,FILO) , lrange ml 0 -1,结果是:12 13 11 10. 这是因为list插入时是将最新的item插入到链表头

    b) sort mimvp by mimvp_* 确定是会按照mimvp_*的值进行排序的.但当mimvp_*对应的value不是num型并且没有设置alpha的时候,会导致排序分值都是相同的,因为程序将把mimvp_*对应的值尝试转换为nun型

    c) 这就会导致sort mimvp by mimvp_*会按照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);
        }  
    }

    性能优化:

  • 相关阅读:
    关于求 p_i != i and p_i != i+1 的方案数的思考过程
    poj 3041 Asteroids 二分图最小覆盖点
    poj 1325 Machine Schedule 最小顶点覆盖
    poj 1011 Sticks 减枝搜索
    poj 1469 COURSES 最大匹配
    zoj 1516 Uncle Tom's Inherited Land 最大独立边集合(最大匹配)
    Path Cover (路径覆盖)
    hdu 3530 SubSequence TwoPoint单调队列维护最值
    zoj 1654 Place the Rebots 最大独立集转换成二分图最大独立边(最大匹配)
    poj 1466 Girls and Boys 二分图最大独立子集
  • 原文地址:https://www.cnblogs.com/wjlbk/p/12633317.html
Copyright © 2011-2022 走看看