Redis需特别注意的场景
1、 时间复杂度大O(Big ONotation)
当问题的规模,不断变化,执行时间也会不断变化。这就是时间复杂度大O的概念。对于Redis来说,时间复杂度可以用来描述在某个场景的数据规模下,一个命令会有多快。
Redis文档会告诉我们每一个命令的时间复杂度,它也会告诉我们影响命令性能的关键因素。
我们这里举几个例子:
O(1) 可以表示使用时间最短的。例如,sismember,Redis还有好多命令也都是O(1).
O(log(N)) 使用时间仅次于O(1)。例如,zadd。
O(N)属于线性的,在关系型数据库中,查找没有索引的列,就是O(N)的操作。Redis中的ltrim也是O(N)的操作。不过,这个N不是列表的个数,而是要删除的元素个数。
O(log(N)+M) 比线性使用时间还长。例如,zremrangebyscore,删除有序集合中排序在某个区间段上的数据。N是集合的元素个数,M是要删除的元素个数。
O(N+M*log(M)),是Redis中时间复杂度最高的。例如,sort
2、 伪多键查询(PseudoMulti Key Queries)
有一个很常见的情况,你要通过多个key,查询同一个value。比如,你既想通过email 查询用户数据,也想通过id查询用户数据。一个非常可怕的解决办法,就是再复杂一份value,类似下面的。
set users:[email protected] '{"id": 123, "name":"test", "email": "[email protected]", ...}'
set users:123 '{"id": 123, "name":"test", "email": "[email protected]", ...}'
一个比较好的解决办法,应该使用Redis的hash。
set users:123 '{"id": 123, "name":"test", "email": "[email protected]", ...}'
hset users:id:email [email protected] 123
如果想通过id找到用户,可以直接使用命令:
get users:123
如果想通过email找到用户,需要执行两个命令:
hget users:id:email [email protected]
get users:#{上面命令的返回值}
3、 管道(Pipeline)
Redis还支持管道。通常情况下,客户端每个请求命令发出后,会阻塞,等待redis服务处理,Redis处理完后请求命令后会将结构通过响应报文返回给client。利用管道模式,我们可以从客户端发出多条命令,不需要等待单条命令的返回,Redis服务端会将多条命令的处理结果打包一起返回给客户端。这减少网络开销,性能得到显著提升。 利用Pipeline打包命令发送,Redis必须在处理完所有命令前线缓存起所有命令的处理结果。打包的命令越多,缓存消耗内存也越多。所以并不是打包的命令越多越好。具体多少合适需要根据情况测试。下面是Jedis客户端利用Pipeline的一段代码。
public void usePipeline(int count){
Jedisjr = null;
try{
jr= new Jedis("10.10.232.123", 6379);
Pipelinepl = jr.pipelined();
for(inti =0; i
4、 事务(Transactions)
Redis的每个命令都是原子的,包括那些需要多步操作的命令(incr,getset,setnx)。
Redis提供了事务机制,可以把多个命令作为一个原子性操作组执行。步骤很简单,先执行multi命令,跟着执行你的命令,最后可以执行exec命令提交所有操作,也可以执行discard命令放弃所有操作。先看看Redis对于事务的都有哪些要素:
(1)、全部命令会被顺序执行。
(2)、全部命令执行不会被打断。
(3)、全部命令或者全部执行,或者一个都不执行。
(4)、如果一个命令出现了异常,其他命令也不会回滚。
multi
incr a
incr b
exec
最后,Redis可以允许在一个key或多个keys上使用watch命令,可以根据这些key值是否有变化,来有条件的执行事务。这个非常有用,当你在一个事务里想获取一个key的值,并且操作这个key。如果像下面这样:
redis.multi()
current = redis.get('count')
redis.set('count’, current + 1)
redis.exec()
这样是行不通的,因为get操作,需要在exec执行后,才会提交。这时,我们使用watch命令,就可以解决这个问题:
redis.watch('count')
current = redis.get('count')
redis.multi()
redis.set('count', current + 1)
redis.exec()
如果有其他客户端修改了“count”的值,那么这个事务就会执行失败。
5、 Keys命令
Keys通过模式匹配,可以找到所有的keys。这个命令看起来很万能,但是绝不建议在生产环境使用。因为这个命令会扫描所有的key。
比如你要创建一个主机bug跟踪系统,会不会这样设计系统?
Keys: bug:account_id:bug_id
Values:异常信息字符串
那么你要查询一个主机的所有bug,你可能会习惯的执行: keys bug:123:* 命令。
但是这样,会很慢。使用hash 会是一个不错的替代方案。
hset bug:1233 1 ‘{“id”:1,”account”:123,”excepiton”:….}’
想得到某一个主机的所有bug。我们可以使用 hkeys bugs:1233.