Redis笔记_6


使用Redis构建应用程序组件


自动补全

例如用户在浏览器地址栏中输入部分字符, 即可自动填充整个URL
自动补全最近的100个联系人实现规则
1.如果联系人已经存在于列表中, 则冲列表中移除
2.将联系人添加到联系人列表的最前面
3.如果列表长度超过100个, 则只保留前一个联系人


联系人列表使用list结构存储, 使用的API
1. LREM
2. LPUSH
3. LTRIM
为了保证操作的原子性, 还需要用到pipeline的MULTI, EXECUTE命令


如果用户不想再看到某个联系人时, 需要将指定联系人从列表里面移除, 这个调用LREM来完成


最后就是自动补全匹配的联系人, 使用的API

1. LRANGE


涉及的API

LREM key count value

根据参数 count 的值,移除列表中与参数 value 相等的元素。

count 的值可以是以下几种:

  • count > 0 : 从表头开始向表尾搜索,移除与 value 相等的元素,数量为 count 。
  • count < 0 : 从表尾开始向表头搜索,移除与 value 相等的元素,数量为 count 的绝对值。
  • count = 0 : 移除表中所有与 value 相等的值。
可用版本:
>= 1.0.0
时间复杂度:
O(N),  N 为列表的长度。
返回值:
被移除元素的数量。
因为不存在的  key 被视作空表(empty list),所以当  key 不存在时,  LREM 命令总是返回  0 。
# 先创建一个表,内容排列是
# morning hello morning helllo morning

redis> LPUSH greet "morning"
(integer) 1
redis> LPUSH greet "hello"
(integer) 2
redis> LPUSH greet "morning"
(integer) 3
redis> LPUSH greet "hello"
(integer) 4
redis> LPUSH greet "morning"
(integer) 5

redis> LRANGE greet 0 4         # 查看所有元素
1) "morning"
2) "hello"
3) "morning"
4) "hello"
5) "morning"

redis> LREM greet 2 morning     # 移除从表头到表尾,最先发现的两个 morning
(integer) 2                     # 两个元素被移除

redis> LLEN greet               # 还剩 3 个元素
(integer) 3

redis> LRANGE greet 0 2
1) "hello"
2) "hello"
3) "morning"

redis> LREM greet -1 morning    # 移除从表尾到表头,第一个 morning
(integer) 1

redis> LLEN greet               # 剩下两个元素
(integer) 2

redis> LRANGE greet 0 1
1) "hello"
2) "hello"

redis> LREM greet 0 hello      # 移除表中所有 hello
(integer) 2                    # 两个 hello 被移除

redis> LLEN greet
(integer) 0

LPUSH key value [value ...]

将一个或多个值 value 插入到列表 key 的表头

如果有多个 value 值,那么各个 value 值按从左到右的顺序依次插入到表头: 比如说,对空列表 mylist 执行命令 LPUSH mylist a b c,列表的值将是 c b a ,这等同于原子性地执行 LPUSH mylist a 、 LPUSH mylist b 和 LPUSH mylist c 三个命令。

如果 key 不存在,一个空列表会被创建并执行 LPUSH 操作。

当 key 存在但不是列表类型时,返回一个错误。

在Redis 2.4版本以前的 LPUSH 命令,都只接受单个 value 值。

可用版本:
>= 1.0.0
时间复杂度:
O(1)
返回值:
执行  LPUSH 命令后,列表的长度。
# 加入单个元素

redis> LPUSH languages python
(integer) 1


# 加入重复元素

redis> LPUSH languages python
(integer) 2

redis> LRANGE languages 0 -1     # 列表允许重复元素
1) "python"
2) "python"


# 加入多个元素

redis> LPUSH mylist a b c
(integer) 3

redis> LRANGE mylist 0 -1
1) "c"
2) "b"
3) "a"

LTRIM key start stop

对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。

举个例子,执行命令 LTRIM list 0 2 ,表示只保留列表 list 的前三个元素,其余元素全部删除。

下标(index)参数 start 和 stop 都以 0 为底,也就是说,以 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。

你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。

当 key 不是列表类型时,返回一个错误。

LTRIM 命令通常和 LPUSH 命令或 RPUSH 命令配合使用,举个例子:

LPUSH log newest_log
LTRIM log 0 99

这个例子模拟了一个日志程序,每次将最新日志 newest_log 放到 log 列表中,并且只保留最新的 100 项。注意当这样使用 LTRIM 命令时,时间复杂度是O(1),因为平均情况下,每次只有一个元素被移除。

注意LTRIM命令和编程语言区间函数的区别

假如你有一个包含一百个元素的列表 list ,对该列表执行 LTRIM list 0 10 ,结果是一个包含11个元素的列表,这表明 stop 下标也在LTRIM 命令的取值范围之内(闭区间),这和某些语言的区间函数可能不一致,比如Ruby的 Range.new 、 Array#slice 和Python的 range()函数。

超出范围的下标

超出范围的下标值不会引起错误。

如果 start 下标比列表的最大下标 end ( LLEN list 减去 1 )还要大,或者 start > stop , LTRIM 返回一个空列表(因为 LTRIM 已经将整个列表清空)。

如果 stop 下标比 end 下标还要大,Redis将 stop 的值设置为 end 。

可用版本:
>= 1.0.0
时间复杂度:
O(N),  N 为被移除的元素的数量。
返回值:
命令执行成功时,返回  ok 。
# 情况 1: 常见情况, start 和 stop 都在列表的索引范围之内

redis> LRANGE alpha 0 -1       # alpha 是一个包含 5 个字符串的列表
1) "h"
2) "e"
3) "l"
4) "l"
5) "o"

redis> LTRIM alpha 1 -1        # 删除 alpha 列表索引为 0 的元素
OK

redis> LRANGE alpha 0 -1       # "h" 被删除了
1) "e"
2) "l"
3) "l"
4) "o"


# 情况 2: stop 比列表的最大下标还要大


redis> LTRIM alpha 1 10086     # 保留 alpha 列表索引 1 至索引 10086 上的元素
OK

redis> LRANGE alpha 0 -1       # 只有索引 0 上的元素 "e" 被删除了,其他元素还在
1) "l"
2) "l"
3) "o"


# 情况 3: start 和 stop 都比列表的最大下标要大,并且 start < stop

redis> LTRIM alpha 10086 123321
OK

redis> LRANGE alpha 0 -1        # 列表被清空
(empty list or set)


# 情况 4: start 和 stop 都比列表的最大下标要大,并且 start > stop

redis> RPUSH new-alpha "h" "e" "l" "l" "o"     # 重新建立一个新列表
(integer) 5

redis> LRANGE new-alpha 0 -1
1) "h"
2) "e"
3) "l"
4) "l"
5) "o"

redis> LTRIM new-alpha 123321 10086    # 执行 LTRIM
OK

redis> LRANGE new-alpha 0 -1           # 同样被清空
(empty list or set)

LRANGE key start stop

返回列表 key 中指定区间内的元素,区间以偏移量 start 和 stop 指定。

下标(index)参数 start 和 stop 都以 0 为底,也就是说,以 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。

你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。

注意LRANGE命令和编程语言区间函数的区别

假如你有一个包含一百个元素的列表,对该列表执行 LRANGE list 0 10 ,结果是一个包含11个元素的列表,这表明 stop 下标也在LRANGE 命令的取值范围之内(闭区间),这和某些语言的区间函数可能不一致,比如Ruby的 Range.new 、 Array#slice 和Python的range() 函数。

超出范围的下标

超出范围的下标值不会引起错误。

如果 start 下标比列表的最大下标 end ( LLEN list 减去 1 )还要大,那么 LRANGE 返回一个空列表。

如果 stop 下标比 end 下标还要大,Redis将 stop 的值设置为 end 。

可用版本:
>= 1.0.0
时间复杂度:
O(S+N),  S 为偏移量  start ,  N 为指定区间内元素的数量。
返回值:
一个列表,包含指定区间内的元素。
redis> RPUSH fp-language lisp
(integer) 1

redis> LRANGE fp-language 0 0
1) "lisp"

redis> RPUSH fp-language scheme
(integer) 2

redis> LRANGE fp-language 0 1
1) "lisp"
2) "scheme"

redis的pipeline(管道)功能在命令行中没有,但redis是支持pipeline的,而且在各个语言版的client中都有相应的实现。

pipeline在某些场景下非常有用,比如有多个command需要被“及时的”提交,而且他们对相应结果没有互相依赖,而且对结果响应也无需立即获得,那么pipeline就可以充当这种“批处理”的工具;而且在一定程度上,可以较大的提升性能,性能提升的原因主要是TCP链接中较少了“交互往返”的时间。不过在编码时请注意,pipeline期间将“独占”链接,此期间将不能进行非“管道”类型的其他操作,直到pipeline关闭;如果你的pipeline的指令集很庞大,为了不干扰链接中的其他操作,你可以为pipeline操作新建Client链接,让pipeline和其他正常操作分离在2个client中。不过pipeline事实上所能容忍的操作个数,和socket-output缓冲区大小/返回结果的数据尺寸都有很大的关系;同时也意味着每个redis-server同时所能支撑的pipeline链接的个数,也是有限的,这将受限于server的物理内存或网络接口的缓冲能力。

(1)pipeline主要提升在与redis交互的网络延迟时间,对于多条命令不要求实时执行的情形而言,效果较为明显
(2)使用时最好注意一下pipeline执行的redisContext和redisReply(如果遇到诡异问题,可以尝试一下使用局部新建、独占两者进行处理的方式)


import org.jredis.JRedis;
import org.jredis.connector.ConnectionSpec;
import org.jredis.ri.alphazero.JRedisClient;
import org.jredis.ri.alphazero.JRedisPipelineService;
import org.jredis.ri.alphazero.connection.DefaultConnectionSpec;

public class TestPipeline {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        //采用pipeline方式发送指令
        usePipeline();
        long end = System.currentTimeMillis();
        System.out.println("用pipeline方式耗时:" + (end - start) + "毫秒");

        start = System.currentTimeMillis();
        //普通方式发送指令
        withoutPipeline();
        end = System.currentTimeMillis();
        System.out.println("普通方式耗时:" + (end - start) + "毫秒");
    }

    //采用pipeline方式发送指令
    private static void usePipeline() {
        try {
            ConnectionSpec spec = DefaultConnectionSpec.newSpec(
                    "192.168.115.170", 6379, 0, null);
            JRedis jredis = new JRedisPipelineService(spec);

            for (int i = 0; i < 100000; i++) {
                jredis.incr("test2");
            }

            jredis.quit();
        } catch (Exception e) {
        }
    }

    //普通方式发送指令
    private static void withoutPipeline() {
        try {
            JRedis jredis = new JRedisClient("192.168.115.170", 6379);

            for (int i = 0; i < 100000; i++) {
                jredis.incr("test2");
            }

            jredis.quit();
        } catch (Exception e) {
        }
    }
}
执行结果

-- JREDIS -- INFO: Pipeline thread <response-handler> started. 
-- JREDIS -- INFO: Pipeline <org.jredis.ri.alphazero.connection.SynchPipelineConnection@1bf73fa> connected 
用pipeline方式耗时:11531毫秒
-- JREDIS -- INFO: Pipeline <org.jredis.ri.alphazero.connection.SynchPipelineConnection@1bf73fa> disconnected 
-- JREDIS -- INFO: Pipeline thread <response-handler> stopped. 
普通方式耗时:15985毫秒
还可以参照 : http://zhousheng29.iteye.com/blog/1332664







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值