《Redis入门指南》笔记二

本篇幅讲解Redis的进阶知识

 Redis事务

Redis事务的原理是先将属于一个事务的命令发送给Redis,然后再让Redis依次执行这些命令。例 Redis保证一个事务中的命令要么都依次执行,要么都不执行。如果在发送EXEC命令前客户端断线了,则Redis会清空事务队列,事务中的所有命令都不会执行。而一旦客户端发送了EXEC命令,所有的命令就都会被执行,即使此后客户端断线也没关系,因为Redis中已经记录了所有要执行的命令。 除此之外,Redis的事务还能保证一个事务内的命令依次执行而不被其他命令插入。试想客户端A需要执行几条命令,同时客户端B发送了一条命令,如果不使用事务,则客户端B的命令可能会插入到客户端A的几条命令中执行。如果不希望发生这种情况,也可以使用事务。

MUULTI
...
EXEC

错误处理

  • (1)语法错误:只要有一个命令有语法错误执行EXEC命令后Redis会直接返回错误,即使包括语法正确的命令
  • (2)运行错误:运行错误是指在执行时出现的错误,这种错误在执行之前是无法被发现的,所以语法错误的命令会被正确加入到事务队列,Redis的事务没有关系数据库提供的回滚功能。为此开发者必须在事务执行出错后自己收拾剩下的摊子

Watch命令

WATCH命令可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行。监控一直持续到EXEC命令(事务中的命令是在EXEC之后才执行的,所以在MULTI命令后可以修改WATCH监控的键值

redis>SET key 1
ok
redis>WATCH key
ok
redis>SET key 2
ok
redis>MULTI
ok
redis>SET key 3
QUEUED
redis>EXEC
(null)
redis>GET key
2

 提示 由于WATCH命令的作用只是当被监控的键值被修改后阻止之后一个事务的执行,而不能保证其他客户端不修改这一键值,所以我们需要在EXEC执行失败后重新执行整个函数。

执行EXEC命令后会取消对所有键的监控,如果不想执行事务中的命令也可以使用UNWATCH命令来取消监控

TTL:生存时间

1.设置生存时间

SET key value
EXPIRE key seconds //设置成功返回1返回0表示键不存在或者设置失败
PEXPIRE key millseconds //同EXPIRE但是这个命令可以设置TTL精确到毫秒

2.查看键还有多久被删除。当键不存在时TTL命令会返回-1。另外同样会返回-1的情况是没有为键设置生存时间(即永久存在,这是建立一个键后的默认情况):

TTL key

 3.取消键的TTL

PERSIST key

使用SET或者GETSET命令为键赋值也会清楚键的生存时间。eg:执行SET foo bar命令则会清楚原有的foo的生存时间

4.设置键的截止时间,即在固定的时刻过期

EXPIREAT key timestamp //精确到秒
PEXPIREAT key timestamp //精确到毫秒

5.实现缓存

实际开发中会发现很难为缓存键设置合理的生存时间,为此可以限制Redis能够使用的最大内存,并让Redis按照一定的规则淘汰不需要的缓存键,这种方式在只将Redis用作缓存系统时非常实用。

具体的设置方法为:修改配置文件的maxmemory参数,限制Redis最大可用内存大小(单位是字节),当超出了这个限制时Redis会依据maxmemory-policy参数指定的策略来删除不需要的键,直到Redis占用的内存小于指定内存。


 如当maxmemory-policy设置为allkeys-lru时,一旦Redis占用的内存超过了限制值,Redis会不断地删除数据库中最近最少使用的键,直到占用的内存小于限制值。

Redis实现排序

1,使用排序

SORT key

 对有序集合进行排序时会忽略分数,只针对元素自身的值进行排序

2.SORT命令支持通过ALPHA参数实现按照字典顺序排列非数字元素

SORT key ALPHA

 示例

redis>LPUSH mylist a c e d B A C
7
redis>SIORT mylist
ERR one or more scores can't be converted into double
redis>SORT mylist ALPHA
1)"A"
2)"B"
3)"C"
4)"a"
5)"c"
6)"e"

 从这段示例中可以看到如果没有加ALPHA参数的话,SORT命令会尝试将所有元素转换成双精度浮点数来比较,如果无法转换则会提示错误。

3.一般排序格式

sort key [DESC][ALPHA]  [limit offset  count ]

4.BY参数排序

BY参数的语法为“BY参考键”。其中参考键可以是字符串类型键或者是散列类型键的某个字段(表示为键名->字段名)。如果提供了BY参数,SORT命令将不再依据元素自身的值进行排序,而是对每个元素使用元素的值替换参考键中的第一个“*”并获取其值,然后依据该值对元素排序

SORT key BY 参考键

示例

redis >SORT tag:java:posts BY post:* -> time DESC 
1)"12"
2)"26"
3)"6"
4)"2"

将tag:ruby:posts集合的元素(文章id),按照key(文章id)为post的散列表的元素(文章信息)的time字段降序排序

5.GET参数

GET参数不影响排序,它的作用是使SORT命令的返回结果不再是元素自身的值,而是GET参数中指定的键值。GET参数的规则和BY参数一样,GET参数也支持字符串类型和散列类型的键,并使用“*”作为占位符。

redis >SORT tag:java:posts BY post:* -> time DESC  GET post:* -> title
1)"《Redis入门指南》"
2)"《Thinking In Java》"
3)"《Effective Java》"

不难看到,SortBy然后在使用GET参数可以拿到自己想要的字段。比如在这里就是文章名称在一个SORT命令中可以有多个GET参数(而BY参数只能有一个),所以还可以这样用

redis >SORT tag:java:posts BY post:* -> time DESC  GET post:* -> title GET post:*-> time
1)"《Redis入门指南》"
2)  "5555555555555"
3)"《Thinking In Java》"
4)  "4444444444444"
5)"《Effective Java》"
6)  "3333333333333"

可见有N个GET参数,每个元素返回的结果就有N行。这时有个问题:如果还需要返回文章ID该怎么办?答案是使用GET #。就像下面这样:

redis >SORT tag:java:posts BY post:* -> time DESC  GET post:* -> title 
GET post:*-> time GET #

 6.STORE参数

默认情况下SORT会直接返回排序结果,如果希望保存排序结果,可以使用STORE参数。如希望把结果保存到sort.result键中:

redis >SORT tag:java:posts BY post:* -> time DESC  GET post:* -> title 
GET post:*-> time GET # STORE sort.result
(integer)9

返回值为结果的个数;保存后的键的类型为列表类型,如果键已经存在则会覆盖它。加上STORE参数后SORT命令的返回值为结果的个数。

redis >LRANGE sort.result 0 -1

 SORT注意事项

SORT是Redis中最强大最复杂的命令之一,如果使用不好很容易成为性能瓶颈。SORT命令的时间复杂度是O(n+mlogm),其中n表示要排序的列表(集合或有序集合)中的元素个数,m表示要返回的元素个数。当n较大的时候SORT命令的性能相对较低,并且Redis在排序前会建立一个长度为n[插图]的容器来存储待排序的元素,虽然是一个临时的过程,但如果同时进行较多的大数据量排序操作则会严重影响性能。

所以开发中使用SORT命令时需要注意以下几点。

  • (1)尽可能减少待排序键中元素的数量(使n尽可能小)。
  • (2)使用LIMIT参数只获取需要的数据(使m尽可能小)。
  • (3)如果要排序的数据数量较大,尽可能使用STORE参数将结果缓存

Redis对pipelining(管道)的支持

通过管道可以一次性发送多条命令并在执行完后一次性将结果返回,当一组命令中每条命令都不依赖于之前命令的执行结果时就可以将这组命令一起通过管道发出。管道通过减少客户端与Redis的通信次数来实现降低往返时延累计值的目的

未使用piepline

使用pieplinging

消息通知

1.任务队列,有点像生产者消费者模式实现的BlockingQueue

redis A>BRPOP queue 0

BRPOP命令接收两个参数,第一个是键名,第二个是超时时间,单位是秒。当超过了此时间仍然没有获得新元素的话就会返回nil。上例中超时时间为“0”,表示不限制等待的时间,即如果没有新元素加入列表就会永远阻塞下去。

在执行LPUSH命令往queue中加入元素

redis B>LPUSH queue task

可以看到在redisA实例的shell里立马返回

1)"queue" 
2)"task"

同时queue中的元素被取走

redis>LLEN queue 

(integer)0

2.优先级的任务队列

BRPOP命令可以同时接收多个键,其完整的命令格式为BLPOP key [key…] timeout,如BLPOP queue:1 queue:2 0。意义是同时检测多个键,如果所有键都没有元素则阻塞,如果其中有一个键有元素则会从该键中弹出元素。例如,打开两个redis-cli实例,在实例A中:

redis A>BLPOP queue:1 queue:2 queue:3 0

在实例B中:

redis B>LPUSH queue:2 task

则实例A中会返回:

1)"queue:2"
2)"task"

如果多个键都有元素则按照从左到右的顺序取第一个键中的一个元素。我们先在queue:2和queue:3中各加入一个元素:

redis B>LPUSH queue:2 task1
1)(integer) 1
redis B>LPUSH queue:3 task2
2)(integer) 1

然后执行BRPOP

redis>BRPOP queue:1 queue:2 queue:3 0
1)"queue:2"
2)"task1"

 借此特性可以实现区分优先级的任务队列

3"发布订阅"模式

需要注意,如果先发布消息,订阅者再去订阅,是收不到历史消息的。

“发布/订阅”模式中包含两种角色,分别是发布者和订阅者。订阅者可以订阅一个或若干个频道(channel),而发布者可以向指定的频道发送消息,所有订阅此频道的订阅者都会收到此消息。

3.1发布者发布消息的命令是PUBLISH,用法是PUBLISH channelmessage,如向channel.1说一声“hi”:

redis>PUBLISH channel.1 hi
(integer) 0

这样消息就发出去了。PUBLISH命令的返回值表示接收到这条消息的订阅者数量。因为此时没有客户端订阅channel.1,所以返回0。发出去的消息不会被持久化,也就是说当有客户端订阅channel.1后只能收到后续发布到该频道的消息,之前发送的就收不到了。

3.2订阅频道的命令是SUBSCRIBE,可以同时订阅多个频道,用法是SUBSCRIBE channel [channel …]。现在新开一个redis-cli实例A,用它来订阅channel.1:

redis A>SUBSCRIBE channel.1
Reading messages...
1)"subscribe"
2)"channel.1"
3)(integer)1

 执行SUBSCRIBE命令后客户端会进入订阅状态,处于此状态下客户端不能使用除SUBSCRIBE/UNSUBSCRIBE/PSUBSCRIBE/PUNSUBSCRIBE这4个属于“发布/订阅”模式的命令之外的命令(后面3个命令会在下面介绍),否则会报错。

进入订阅状态后客户端可能收到三种类型的回复。每种类型的回复都包含3个值,第一个值是消息的类型,根据消息类型的不同,第二、三个值的含义也不同。消息类型可能的取值有:

(1)Subscribe。表示订阅成功的反馈信息。第二个值是订阅成功的频道名称,第三个值是当前客户端订阅的频道数量。

(2)message。这个类型的回复是我们最关心的,它表示接收到的消息。第二个值表示产生消息的频道名称,第三个值是消息的内容。

(3)unsubscribe。表示成功取消订阅某个频道。第二个值是对应的频道名称,第三个值是当前客户端订阅的频道数量,当此值为0时客户端会退出订阅状态,之后就可以执行其他非“发布/订阅”模式的命令了。上例中当实例A订阅了channel.1进入订阅状态后收到了一条subscribe类型的回复,这时我们打开另一个redis-cli实例B,并向channel.1发送一条消息:

redis B>PUBLISH channel.1 hi!
1)(integer)1

返回值为1表示有一个客户端订阅了channel.1,此时实例A收到了类型为message的回复:

1)"message"
2)"channel.1"
3)"hi!"

3.3使用UNSUBSCRIBE命令可以取消订阅指定的频道,用法为UNSUBSCRIBE [channel [channel …]],如果不指定频道则会取消订阅所有频道

 4.按照规则订阅

除了可以使用SUBSCRIBE命令订阅指定名称的频道外,还可以使用PSUBSCRIBE命令订阅指定的规则。规则支持glob风格通配符格式.下面我们新打开一个redis-cli实例C进行演示:

redis C>PSUBSCRIBE channel.?*
Reading messages...
1)"psubscribe"
2)"channel.?*"
3)(integer)1

 规则channel.?*可以匹配channel.1和channel.10,但不会匹配channel.。这时在实例B中发布消息:

redis B>PUBLISH channel.1 hi!
(integer)2

返回结果为2是因为实例A和实例C两个客户端都订阅了channel.1频道。实例C接收到的回复是:

1)"pmessage"
2)"channel.?*"
3)"channel.1"
4)"hi"

第一个值表示这条消息是通过PSUBSCRIBE命令订阅频道而收到的,第二个值表示订阅时使用的通配符,第三个值表示实际收到消息的频道命令,第四个值则是消息内容。是不是特别简单,在我还不知道有ZooKeeper的时候,我觉得可以用Redis的发布订阅功能来做配置中心。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值