使用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 。
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 命令后,列表的长度。
LTRIM key start stop
对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。
举个例子,执行命令 LTRIM list 0 2 ,表示只保留列表 list 的前三个元素,其余元素全部删除。
下标(index)参数 start 和 stop 都以 0 为底,也就是说,以 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。
你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。
当 key 不是列表类型时,返回一个错误。
LTRIM 命令通常和 LPUSH 命令或 RPUSH 命令配合使用,举个例子:
这个例子模拟了一个日志程序,每次将最新日志 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 。
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的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