Redis数据特性

1.过期时间 

Redis被用作缓存时,给临时数据设置过期时间。

由于某些数据可能经常改变,所以应该设置一个过期时间,来及时获取新的数据。

当对Redis中的一个键设置过期时间时,键的过期时间会被存储为一个绝对的UNIX时间戳。

这样做的目的在于,即使Redis服务器宕机了一段时间,这个时间戳也会被持久化到RDB文件中。

当Redis再次启动时,这个用来判断键是否过期的时间戳并不会发生变化,一旦当前时间超过了这个时间戳时,键就过期了。

在一个键过期后,当客户端试图访问已过期键时,Redis会立即将其从内存中删除。

Redis这种删除键的方式被称为被动过期(expiringpassively)。

对于那些已经过期且永远不会再被访问到的键,Redis还会定期地运行一个基于概率的算法来进行主动删除。

更具体地说,Redis会随机选择设置了过期时间的20个键。在这20个被选中的键中,已过期的键会被立即删除;如果选中的键中有超过25%的键已经过期且被删除,那么Redis会再次随机选择20个键并重复这个过程。默认情况下,每秒运行10次(可通过配置文件中hz的值进行设置)。

使用EXPIRE命令设置超时时间为600秒:expire 键名 600

使用TTL命令在过期前查看剩余时间(如果未设置过期时间则返回-1,如果键不存在则返回-2):ttl 键名

使用EXISTS命令判断键是否存在(存在返回1,不存在返回0):exists 键名

我们可以通过以下的方式清除一个键的过期时间:

  • 使用PERSIST命令使其成为持久的键。
  • 键的值被替换或删除(修改列表、集合或哈希的元素却不会清除过期时间,这是因为修改元素的操作并不会替换键所关联的值对象)。
  • 被另一个没有过期时间的键重命名。

EXPIREAT命令与EXPIRE命令类似,但它可以指定一个绝对UNIX时间戳为参数,该参数用于直接指定键的过期时间。

因为Redis对已过期键的主动删除动作是不可预测的,所以有些已过期的键可能永远不会被删除。当发现有太多已过期的键没有被删掉时,我们可以通过执行SCAN命令来更加“主动地”触发被动过期。

2.排序 

在输出值之前先进行排序,输出排序后的结果。

在使用SORT命令时添加修饰符DESC会按降序返回元素。

默认情况下,SORT命令会排序并返回所有元素;但是,我们可以通过使用LIMIT修饰符来限制返回元素的数量。

在使用LIMIT修饰符时,我们需要同时指定起始偏移量(要跳过元素的数量)和数量(要返回元素的数量)。

SORT命令的时间复杂度是O(N+M*log(M)),其中N是列表或集合中元素的个数,M是要返回的元素的数目。

鉴于SORT操作的时间复杂度,在对大量数据进行排序时,Redis服务器的性能可能会降低,这一点需要格外留心。

3.位图 

位图(也称为位数组或位向量)是由比特位(bit)组成的数组。

Redis中的位图并不是一种新的数据类型,它实际的底层数据类型是字符串。

因为字符串本质上是二进制大对象(BLOB,Binary Large OBject),所以可以将其视做位图。

同时,因为位图存储的是布尔信息,所以在某些情况下可以节省大量的内存空间。

4.管道 

Redis客户端和服务器是通过RESP(REdis Serialization Protocol)协议进行通信的。

客户端和服务器之间典型的通信过程可以看作:

  1. 客户端向服务器发送一个命令。
  2. 服务器接收该命令并将其放入执行队列(因为Redis是单线程的执行模型)。 
  3. 命令被执行。
  4. 服务器将命令执行的结果返回给客户端。

上述过程耗费的所有时间称为往返时延(RTT,round-triptime)。

第2步和第3步耗费的时间取决于Redis服务器,而第1步和第4步耗费的时间则完全取决于客户端和服务器之间的网络延迟。

如果需要执行多个命令,那么与服务器执行命令耗费的时间(通常非常短)相比,网络传输可能会花费大量的时间。

使用Redis管道可以加快上述的过程。

Redis管道的基本思想是,客户端将多个命令打包在一起,并将它们一次性发送,而不再等待每个单独命令的执行结果;同时,Redis管道需要服务器在执行所有的命令后再返回结果。即便是执行多个命令,但由于第1步和第4步只发生一次,所以总的执行时间会大大减少。

redis-cli中的--pipe选项会一次性地发送所有来自stdin的命令,从而极大地减少往返时延的开销。

也可以通过构造客户端与Redis服务器通信所用的原始RESP协议报文来发送命令。

5.事务 

类似关系数据库的事务,也有所不同;区别在于,Redis事务没有回滚功能。

一般来说,在一个Redis事务中可能会出现两种类型的错误,而针对这两种类型的错误会采取不同的处理方式:

  1. 第一种错误是命令有语法错误。在这种情况下,由于在命令人队时就能发现存在语法错误,所以整个事务会快速失败且事务中的所有命令都不会被处理。
  2. 第二种错误是,虽然所有命令都已成功人队,但在执行过程中发生了错误。位于发生错误命令之后的其他命令将继续执行,而不会回滚。

6.发布订阅 

发布·订阅(Publish-Subscribe,PubSub),想要发布事件(event)的发布者(publisher)会把消息(message)发送到一个PubSub频道(channel),这个频道会把事件投递(deliver)给对这个频道感兴趣的每一个订阅者(subscriber)。

SUBSCRIBE命令用来监听特定频道中的可用消息。一个客户端可以使用SUBSCRIBE命令一次性地订阅多个频道,也可以使用PSUBSCR工BE命令订阅匹配指定模式的频道。要取消订阅频道,可以使用UNSUBSCRIBE命令。

PUBLISH命令用于将一条消息发送到指定的频道。订阅了该频道的所有订阅者将接收到这条消息。

另一个重要的命令是PUBSUB,用于进行频道管理(例如可以通过PUBSUB CHANNELS命令获取当前活跃的频道)。

对频道的生命周期而言,如果给定的频道之前未曾被订阅过,那么SUBSCRIBE命令会自动创建频道。此外,当频道上没有活跃的订阅者时,频道将会被删除。

PubSub相关的机制均不支持持久化。这意味着,消息、频道和PubSub的关系均不能保存到磁盘上。如果服务器出于某种原因退出,那么所有的这些对象都将丢失。

在消息投递和处理场景中,如果频道没有订阅者,那么被发到频道上的消息将被丢弃。换句话说,Redis并没有保证消息投递可靠性的机制。

总之,虽然Redis中的PubSub功能并不适合重要消息的投递场景,但是有些人可能会由于其简洁的通信方式而在速度方面获益。Redis中基于PubSub的键空间通知(keyspacenotification)功能允许客户端订阅一个Redis频道来接收命令发布或数据改变的事件。

7.Lua脚本 

是一种被用作嵌入到程序内部的脚本语言,以此来实现程序的可配置性和可扩展性;

Lua脚本可以将多个操作打包在一起并原子性的执行(类似于事务)。

与Redis事务类似,也必须注意Lua脚本的执行时间。在执行Lua脚本期间,Redis服务器不能处理任何其他命令。因此,我们必须确保Lua脚本可以尽快地执行完。

在默认情况下,一旦Lua脚本的执行时间超过了Lua脚本运行时长限制(默认为5秒,在Redis配置文件中的lua-time-limit选项可以修改默认值),我们就可以调用SCRIPT KILL来将其终止。但是,如果此正在运行的脚本中已经调用了任何写人相关的命令,那么我们就不得不采用无持久化的SHUTDOWN NOSAVE命令来关闭Redis服务器以中止此脚本的运行。如果某个Lua脚本的执行时间小于Lua脚本运行时长限制,SCRIPT KILL命令则会被阻塞,直到脚本执行超时。

对于Lua脚本的管理而言,我们可以通过调用SCRIPT EXISTS命令并传人脚本的SHA-1标识来判断一个脚本是否已存在并注册。最后要注意的一点是,因为脚本只是被保存在Redis服务器进程的脚本缓存中,而脚本缓存在重新启动时将会消失,所以在重新启动Redis服务器之后必须重新加载Lua脚本。