《Redis入门指南》读书笔记第4章:进阶
目录
事务
概述
Redis中的事务(transaction)是一组命令的集合。事务同命令一样都是Redis的最小执行单位,一个事务中的命令要么都执行,要么都不执行。
Redis事务的原理是将属于一个事务的命令发送给Redis,然后再让Redis依次执行这些命令。
示例
[root@localhost ~]# cd /usr/local/redis/bin/
[root@localhost bin]# ./redis-cli
127.0.0.1:6379> multi
OK
127.0.0.1:6379> mset key1 1 key2 2
QUEUED
127.0.0.1:6379> mset key3 3 key4 4
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
说明
- 首先执行
MULTI
命令告诉Redis,下面发送的命令都属于同一个事务 - 发送要执行的命令,Redis没有执行,返回
QUEUED
表示命令进入等待执行的事务队列中 - 当把所有要在同一事务中执行的命令都发给Redis后,执行
EXEC
命令,Redis会将事务队列中的等待执行的命令按照发送命令的先后顺序依次执行,并返回命令对应的返回值,返回值的顺序和执行命令的顺序相同
错误处理
错误类型
- 语法错误
语法错误指命令不存在或者命令参数的个数不对。
同一事务中,只要有一个命令有语法错误,执行EXEC
命令后Redis会直接返回错误,连语法正确的命令也不会执行。
- 运行错误
运行错误指命令在执行时出现的错误。比如使用散列命令操作集合类型的键。
同一事务中,如果一条命令执行时出现运行错误,事务里其他的命令依然会继续执行。
说明:Redis的事务没有关系数据库事务提供的回滚(rollback)功能。
WATCH命令介绍
WATCH
命令可以监控一个或多个键,一旦其中有一个键被修改(或删除),之后的事务就不会执行。监控一直持续到EXEC
命令。
生存时间
命令介绍
EXPIRE key seconds
设置键的生成时间,seconds参数代表键的生存时间,单位是秒。返回1表示设置成功,返回0表示键不存在或设置失败。
TTL key
查询一个键还有多久的时间会被删除,返回值是键的剩余时间,单位是秒。
当键不存在时,TTL
命令会返回1;没有为键设置生存时间时,也会返回1。
PERSIST key
取消键的生存时间设置。
PEXPIRE key milliseconds
设置键的生存时间,milliseconds参数代表键的生存时间,单位是毫秒。返回1表示设置成功,返回0表示键不存在或设置失败。
PTTL key
查询 PEXPIRE
命令设置的键的生存时间,单位是毫秒。
EXPIREAT key unixtime
设置键的生存时间,unixtime 代表使用 UNIX 时间表示键的生存时间的截止时间,单位是秒。
PEXPIREAT key unixtime
设置键的生存时间,unixtime 代表使用 UNIX 时间表示键的生存时间的截止时间,单位是毫秒。
注意
- 除了
PERSIST
命令之外,使用SET
或GETSET
命令为键赋值也会同时清除键的生存时间 - 其他只对键值进行操作的命令(如INCR、LPUSH、HSET、ZREM)均不会影响键的生存时间
- 如果使用
WATCH
命令监测了一个设置生存时间的键,该键时间到期自动删除并不会被WATCH
命令认为该键被改变
排序
SORT命令
命令格式:SORT key
SORT 命令可以对列表类型、集合类型和有序集合类型键进行排序,并且可以完成与关系型数据库中的连接查询相类似的任务。
在对有序集合类型排序时会忽略元素的分数,只针对自身的值进行排序。
命令格式:SORT key ALPHA
除了可以排列数字外,SORT命令还可以通过ALPHA参数实现按照字典顺序排列非数字元素。
命令格式:SORT key DESC
SORT命令默认是按照从小到大的顺序排列。SORT命令的DESC参数可以实现将元素按照从大到小的顺序排列。
命令格式:SORT key LIMIT offset count
SORT命令还支持LIMIT参数来返回指定范围的结果。用法和SQL语句一样,LIMIT offset count
,表示跳过前offset个元素并获取之后的count个元素。
BY参数
命令格式:SORT key BY pattern
BY参数的语法为“BY 参考键”。其中参考键可以是字符串类型或者是散列类型键的某个字段(表示为键名->字段名)。如果提供了BY参数,SORT命令将不再依据元素自身的值进行排序,而是对每个元素的值替换参考键中的第一个*
并获取其值,然后依据该值对元素排序。
GET参数
命令格式:SORT key GET pattern
GET参数不影响排序,它的作用是使SORT命令的返回结果不再是元素自身的值,而是GET参数中指定的键值。GET参数支持字符串类型和散列类型的键,并使用*
作为占位符。
在一个SORT命令中可以有多个GET参数。
GET # 会返回元素本身的值。
STORE参数
命令格式:SORT key STORE destination
默认情况下SORT会直接返回排序结果,如果希望保存排序结果,可以使用STORE参数。
性能优化
SORT命令的时间复杂度是0(n+m logm)
,其中n表示要排序的列表(集合或有序集合)中元素的个数,m表示要返回的元素个数。
使用SORT命令时需要注意以下几点。
- 尽可能减少待排序键中元素的数量
- 使用LIMT参数只获取需要的数据
- 如果要排序的数据数量较大,尽可能使用STORE参数将结果缓存
消息通知
任务队列
任务队列,就是“传递任务的队列”。与任务队列进行交互的实体有两类,一类是生产者(producer),一类是消费者(consumer)。生产者会将需要处理的任务放入任务队列中,而消费者则不断地从任务队列中读入任务信息并执行。
使用任务队列有如下好处。
- 松耦合。生产者和消费者无需知道彼此的实现细节,只需要约定好任务的描述格式。这使得生产者和消费者可以由不同的团队使用不同的编程语言编写。
- 易于扩展消费者可以有多个,而且可以分布在不同的服务器中。
使用Redis实现消息队列
如果要实现任务队列,只需要让生产者将任务使用LPUSH命令加入到某个键
中,另一边让消费者不断地使用RPOP命令从该键中取出任务即可。
BRPOP命令和RPOP命令相似,唯一的区别是当列表中没有元素时BRPOP命令会一直阻
塞住连接,直到有新元素加入。
BRPOP命令接收两个参数,第一个是键名,第二个是超时时间,单位是秒。当超过了此
时间仍然没有获得新元素的话就会返回nil。
当获得一个元素后BRPOP命令返回两个值,分别是键名和元素值。
除了BRPOP命令外,Redis还提供了BLPOP,和BRPOP的区别在与从队列取元素时
BLPOP会从队列左边取。
优先级队列
Redis实现优先级队列,需要借助BRPOP
、BLPOP
命令,其完整的命令格式为BLPOP key [key ...] timeout,意义是同时检测多个键,如果所有键都没有元素则阻塞,如果其中有一个键有元素则会从该键中弹出元素。
“发布/订阅”模式
“发布/订阅”(publish/subscribe)模式同样可以实现进程间的消息传递,其原理是
这样的:
发布/订阅”模式中包含两种角色,分别是发布者和订阅者。订阅者可以订阅一个或若干
个频道(channel),而发布者可以向指定的频道发送消息,所有订阅此频道的订阅者都会收
到此消息。
发布者发布消息的命令是PUBLISH,用法是PUBLISH channel message
。
PUBLISH命令的返回值表示接收到这条消息的订阅者数量。
发出去的消息不会被持久化,也就是说当有客户端订阅后只能收到后续发布到该频道的消息,之前发送的就收不到了。
订阅频道的命令是SUBSCRIBE,可以同时订阅多个频道,用法是SUBSCRIBE channel[channel …]
。
执行SUBSCRIBE命令后客户端会进入订阅状态,处于此状态下客户端不能使用除
SUBSCRIBE/UNSUBSCRIBE/PSUBSCRIBE/PUNSUBSCRIBE这4个属于“发布/订阅”模式的命令之外的命令,否则会报错。
进入订阅状态后客户端可能收到三种类型的回复。每种类型的回复都包含3个值,第一
个值是消息的类型,根据消息类型的不同,第二、三个值的含义也不同。消息类型可能的取
值有:
- Subscribe。表示订阅成功的反馈信息。第二个值是订阅成功的频道名称,第三个
值是当前客户端订阅的频道数量。 - message。这个类型的回复是我们最关心的,它表示接收到的消息。第二个值表示
产生消息的频道名称,第三个值是消息的内容。 - unsubscribe。表示成功取消订阅某个频道。第二个值是对应的频道名称,第三个值
是当前客户端订阅的频道数量,当此值为0时客户端会退出订阅状态,之后就可以执行其他
非“发布/订阅”模式的命令了。
使用UNSUBSCRIBE命令可以取消订阅指定的频道,用法为UNSUBSCRIBE [channel[channel …]]
,如果不指定频道则会取消订阅所有频道。
按照规则订阅
除了可以使用SUBSCRIBE命令订阅指定名称的频道外,还可以使用PSUBSCRIBE命令订
阅指定的规则。规则支持glob风格通配符格式。
返回结果第一个值表示这条消息是通过PSUBSCRIBE命令订阅频道而收到的,第二个值表示订阅时使用的通配符,第三个值表示实际收到消息的频道命令,第四个值则是消息内容。
使用PSUBSCRIBE命令可以重复订阅一个频道
PUNSUBSCRIBE命令可以退订指定的规则,用法是PUNSUBSCRIBE [pattern[pattern …]]
,如果没有参数则会退订所有规则。
使用PUNSUBSCRIBE命令只能退订通过PSUBSCRIBE命令订阅的规则,不
会影响直接通过SUBSCRIBE命令订阅的频道;同样UNSUBSCRIBE命令也不会影响
通过PSUBSCRIBE命令订阅的规则。
使用PUNSUBSCRIBE命令退订某个规则时不会将其中的通配符展开,而是进行严格的字符串匹配,所以PUNSUBSCRIBE无法退订channel.*
规则,而是必须使用PUNSUBSCRIBE channel.*
才能退订。
管道
客户端和Redis使用TCP协议连接。不论是客户端向Redis发送命令还是Redis向客户端返
回命令的执行结果,都需要经过网络传输,这两个部分的总耗时称为往返时延。根据网络性
能不同,往返时延也不同,大致来说到本地回环地址(loop backaddress)的往返时延在数量
级上相当于Redis处理一条简单命令(如LPUSH list 1 2 3)的时间。如果执行较多的命令,每
个命令的往返时延累加起来对性能还是有一定影响的。
在执行多个命令时每条命令都需要等待上一条命令执行完(即收到Redis的返回结果)才
能执行,即使命令不需要上一条命令的执行结果。
Redis的底层通信协议对管道(pipelining)提供了支持。通过管道可以一次性发送多条命
令并在执行完后一次性将结果返回,当一组命令中每条命令都不依赖于之前命令的执行结果
时就可以将这组命令一起通过管道发出。管道通过减少客户端与Redis的通信次数来实现降低
往返时延累计值的目的。
节省空间
Redis是一个基于内存的数据库,所有的数据都存储在内存中,所以如何优化存储,减少内存空间占用对成本控制来说是一个非常重要的话题。
- 精简键名和键值
精简键名和键值是最直观的减少内存占用的方式。
- 内部编码优化
有时候仅凭精简键名和键值所减少的空间并不足以满足需求,这时就需要根据Redis内部
编码规则来节省更多的空间。Redis为每种数据类型都提供了两种内部编码方式。
如果想查看一个键的内部编码方式可以使用OBJECT ENCODING key命令。