lane 发布的文章

Redis是天生的队列好手。RPOP,LPUSH就可以看到。生产者是队列任务的提出方,消费者队列任务的执行方。生产者提出大量的任务,他们排队一个接一个的被消费者执行。执行一个就RPOP,提出一个新任务就LPUSH。如果要插队呢?就RPUSH。Redis队列的伪代码:

//无限循环
loop
    $task = RPOP queue
    if($task)
        execute($task)
    else
        sleep(1)

一个无限循环,从队列的最头部弹出一个任务,如果该任务存在则执行,如果不存在则睡眠,1秒后再次进入循环。这段代码实现了对队列任务的死循环来进行监听任务列表。这样并不好,每1秒扫描一次,如果一晚上都没有呢,那不是在白白浪费资源吗。这时候借助Redis 队列命令家族中的BRPOP。如果队列列表中有任务则弹出,如果没有任务就一直将连接阻塞,直到有新的任务加入才会放开。

//无限循环
loop
    $task = BRPOP queue 0
    execute($task)

BRPOP第一个参数是键,第二个参数是时间,如果时间为0则没有新任务加入的时候永久阻塞。
Redis队列家族是可以进行优先级的。比如有三个列表任务列表,queue1,queue2,queue3。那么那个优先级高就拍在前面:

    BRPOP queue2 queue3 queue1 0

如果队列2中有任务则优先弹出任务2。

Redis SORT是由Redis提供的一个排序命令。集合中的标签是无序的,可以使用SORT排序。如:

redis>SADD jihe 5
(integer) 1
redis>SADD jihe 1
(integer) 1
redis>SADD jihe 2
(integer) 1
redis>SADD jihe 8
(integer) 1
redis>SORT jihe
1) "1"
2) "2"
3) "5"
4) "8"

如果使用Redis SORT排序的不是数字,是字母,将他们按照字典的顺序排名,则需要使用

SORT jihe ALPHA

如果不加ALPHA参数,则会报错,提示:(error) ERR One or more scores can't be converted into double。我们还可以使用关系型数据库的DESC进行倒序排序和LIMIT offset count来限定获取的条数

SORT jihe DESC LIMIT 0 2

还可以对Redis SORT命令添加BY参数。一条语句只能有一个BY参数。这时,SORT不会根据自身的值排序,比如(1,5,2,8和a,A,g,B),而是根据指定的另一个键中的字段来排序。如:

SORT tag:redis:article BY article:*->time DESC

解释:根据tag:redis:article中的值(tag是redis的文章ID),来组合成一个新的key就是article:(ag:redis:article中的一个值):time。获取到tag是redis的文章ID列表,然后根据他们的发布时间来排序。
Redis SORT命令还有个GET参数,GET参数类似在关系型数据库中的关联查询。比如查询tag是redis的文章ID列表,将列表根据发布时间倒序排序,然后获取每个文章的标题。GET可以有多个:

SORT tag:redis:article BY article:*->time DESC GET article:*->title GET article:*->time GET #

GET #的意思是,将文章ID返回回来,你可以写GET article:*->id,也可以写GET #。
Redis SORT命令还有个参数是STORE,是将排序后的内容存储到一个新的key中。新key的类型是列表类型,如果存在则会覆盖。这个时候可以用EXPIRE来设置缓存:

SORT tag:redis:article BY article:*->time DESC GET article:*->title GET article:*->time GET # STORE resultKey

Redis的SORT命令是Redis最复杂最强大的命令之一,时间复杂度是O(n+mLOGm)。n是待排序的列表长度,m是返回的元素个数。减少n和m会提高SORT的性能。

Redis对键提供生存时间,在不指定生存时间时,生存时间是永久。时间到期后Redis会自动删除这个键。可以用EXPIRE命令,时间单位时秒,如果一个键是被设为有限的生存时间,那么在SET key进行重新赋值的时候会被再次设为永久:

SET session:captcha sd2a
EXPIRE session:captcha 600

取消生存时间,将键的生存时间设为永久,是PERSIST:

PERSIST session:captcha

查看一个键的生存时间用TTL命令,-1表示永久或者以及到期被删除。

TTL session:captcha

在Redis的INCR,LPUSH,HSET,ZREM等命令时不会改变生存时间的。
想要精确到毫米来控制时间,就需要PEXPIRE即可,使用PTTL查看剩余时间。
如果想要给定一个到期的时间而不是多少秒后到期呢?就需要EXPIREAT和PEXPIREAT。EXPIREAT的参数是到期时的时间戳(秒),PEXPIREAT的参数是到期时间是时间戳(毫秒)

SET session:captcha sd2a
EXPIREAT session:captcha 1399902009
PEXPIREAT session:captcha 1399902009000

应用场景一:访问频率限制:我们限定每个用户1分钟只能浏览10个页面。伪代码如下:

$isExists = EXISTS limit:user1:192.168.1.2
if($isExists){
    $num = INCR limit:user1:192.168.1.2
    if($num > 10){
        print '超过限制'
        exit
    }
}else{
    MULTI
    INCR limit:user1:192.168.1.2
    EXPIRE limit:user1:192.168.1.2 60
    EXEC
}

我们用了事务的原因是因为,加入在执行了INCR limit:user1:192.168.1.2之后,在执行EXPIRE limit:user1:192.168.1.2 60之前,客户端被关闭了。那么这个键和值就会被持久化保存。且该ID终身只能访问10次了。这就太糟糕了。

应用场景二:实现缓存。计算一万名用户的排行榜,是很耗费资源的,那么我们把数据在第一次计算后存进一个key,然后对这个key设置生存时间。在1个小时后生存时间到期,key被删除,再次进行计算新排名并保存的一个临时key。我们用伪代码实现:

//战斗排行榜
$rank = GET cache:rank:fight
if not $rank
    $rank = 计算排名()
    MULTI
    SET cache:rank:fight $rank
    EXPIRE cache:rank:fight 3600
    EXEC

Redis是内存存储的数据库,假如内存被缓存占满了,Redis会根据配置文件来删除一定的缓存。配置项是Redis的配置文件中的maxmemory参数,单位是字节。超过这个限制之后,会根据配置文件的maxmemory-policy参数来删除不需要的键。maxmemory-policy的可选规则是如下四种:
1、volatile-lru:使用LRU算法删除一个键(设置了生存时间的键)。
2、allkey-lru:使用LRU算法删除一个键。
3、volatile-random:随即删除一个键(设置了生存时间的键)。
4、allkey-random:随即删除一个键。
5、volatile-ttl:删除生存时间即将过期的一个键。是随即取出来N个键,然后删除N个键中即将过期的键,而不是遍历所有的键删除即将过期的。N是几?配置文件配的。
6、nevication:不删除,返回错误。

Redis WATCH是用来补充那不完美的事务功能,Redis WATCH命令监听指定KEY的变化。本篇是Redis WATCH详解部分。对Redis WATCH命令如何使用,有什么作用来进行讲解。WATCH是Redis事务系统中的一个成员命令。它有一个参数,是key。Redis WATCH命令的作用是监听在参数位置指定的key(key可以是五种类型中任意一种),如果这个key被修改或者被删除了,那么WATCH命令的监听作用就停止了,同时WATCH命令后面的第一个事务将不会被执行,直接跳过。

redis>SET num 1
OK
redis>WATCH num
OK
redis>SET num 2
OK
redis>MULTI
OK
redis>SET num 3
QUEUE
redis>EXEC
(nil)
redis>GET num
"2"

上例可以看出,Redis的WATCH命令的作用。值的注意的时,在WATCH监听的key被修改或删除后,WATCH后的第一个事务不会被执行,但是第二个、第三个、第N个都是会正常执行的。

Redis事务以及事务的处理方式,Redis的事务教程将在本篇讲解。事务功能在数据完整性和数据一致性发挥着不可或缺的巨大的作用。Redis数据库为我们提供了不那么完美的Redis事务功能和Redis事务操作。为什么提供了事务缺说不那么完美呢?本篇是Redis事务教程之入门篇。
事务也是Redis的最小执行单位,是原子的,不怕高并发下的竞态条件。一个事务的多条命令语句要么都执行,要么都不执行。事务的应用也非常广泛。Redis事务的原理是先将一个事务命令发送给Redis,然后再让Redis一次执行这些命令。 如:

redis>MULTI
OK
redis>SADD user:10002:friendId 10001
QUEUE
redis>SADD user:10001:friendId 10002
QUEUE
redis>EXEC
1)integer 1
2)integer 1

MULT告诉Redis开始一个事务,EXEC告诉Redis执行一个事务。中间两条SADD是属于同一个事务的语句。返回结果是第一条结果1,第二条语句结果1。如果,两条语句语法错误呢?

redis>MULTI
OK
redis>SADD user:10002:friendId 10001
QUEUE
redis>SADDDDDD user:10001:friendId 10002
ERR unknown command ‘SADDDDD’
redis>EXEC
ERROR..............

如果语法错误,在录入Redis事务的过程中,Redis就会发现,并提示错误,在EXEC之后,仍然会返回错误提示,两条Redis事务的语句都不会被执行。可是,如果语法没有错误,可是在执行的时候却发生了错误呢?比如用SADD(集合类型的操作)去操作一个字符串数据类型的key呢?

redis>MULTI
OK
redis>SET num 1
QUEUE
redis>SADD num 2
QUEUE
redis>EXEC
1)integer 1
2)ERR ....................

可以看到,如果用集合类型的操作命令去操作字符串类型的key的话,第一条语句是正常执行并返回1.第二条语句却报错了。如果GET num可以的得到结果是1。也就是说即使有一条语句错误,但是第一条是执行成功了。这就是Redis提供了Redis事务机制,但是在操作中,不完美地方,Redis事务操作不提供所谓的回滚。
不过Redis却提供了一个新的办法,叫做WATCH命令。生病了,坐不住了。关于WATCH的内容下篇写。。