Redis教程

Redis数据类型和抽象概念

Redis不是一个简单的key-value存储,它实际上是一个数据结构体服务器,支持不同类型的值。接下来介绍所有支持的数据结构体类型:

  • 二进制安全字符串
  • 队列(Lists):字符串元素的集合,按照插入顺序排序,它们基本上是链表
  • 集合(Sets):未排序的唯一字符串集合。
  • 排序集(Sorted sets):与集合很相似,但是每个元素关联到floating数值,即分数(score)。元素总安其分数排序,因此与集合不同,它可以检索一系列元素(例如,你可以:获取前10个,或者后10个数据)
  • 哈希(Hashs):这是映射(map),它由与值相关联的字段组成。字段和值都是字符串。同Ruby和Python的Hashs很相似。
  • 位数组(Bit Array)或位图(simply bitmaps):使用特别的命令可以像处理位数一样处理字符串:设置和清除各个位,将所有位设置为1,查找第一个位或取消设置位,等。
  • HyperLogLogs:这是概率数据结构被用在估算一个集合的基数。
  • Streams:仅附加提供抽象日志数据类型的类似映射的条目的集合。 它们在Redis Streams简介中有详细介绍。

  这些数据类型如何工作,怎样使用解决给定问题参考命令。所以,这篇文章是Redis数据类型和常见模式的速成教程。对于所有示例,我们使用redis-cli程序,一个简单但是好用的命令行程序,发出针对Redis服务器的命令。

01. 键

  • Redis 键值是二进制安全,这意味着你可以使用任何二进制序列作密钥,从string到JPEG文件的内容。空字符串也是有效键。其他关于键值的规则:
    • 不推荐使用长键值。比如1024字节的键值不仅浪费内存,同时因为要从数据集中查找键值可能需要循环多次昂贵的比较键。甚至如果任务是匹配大值的存在,散列(Hash)它(例如使用SHA1)是一个更好的主意,特别是从内存和带宽的角度来看。
    • 不推荐使用超短键值。如果您可以改写“user:1000:followers”,那么将“u1000flw”写为关键字几乎没有意义。它更具可读性,并且添加空间更小。但是很显然,短键值消耗更少的内存空间,主要是寻找平衡点。
    • 尝试坚持同样的模式。 例如,”object-type:id”是一个好主意,如”user:1000”。 点或短划线通常用于多字词字段,如”comment🔢reply.to”或”comment🔢reply-to”。
    • 允许键值的最大值是512M

02. 字符串

  • Redis字符串类型是最简单类型值,它关联到Redis键值。这是Memcached的唯一数据类型,因此新手在Redis中也很自然地使用它。
  • Redis键是字符串,我们字符串映射到另一个字符串。字符串数据类型对于很多用例有用,比如HTML片段或页面。
  • SET和GET操作来设置或获取一个字符串值。SET会替换原来的值,所以SET执行了任务分配操作。
  • 我们可以设置strings(包括二进制数据)的各种类型,比如图片。这个值不能大于512M。
  • SET命令有一些选项,提供额外的参数。比如,如果键值存在则设置错误,或者只有存在这个键值才SET成功。
1
2
3
4
> set mykey newval nx
(nil)
> set mykey newval xx
OK
  • 即使strings是Redis的基本类型,它也支持一些有趣的操作。比如其中有个自动增长:
1
2
3
4
5
6
7
8
> set counter 100
OK
> incr counter
(integer) 101
> incr counter
(integer) 102
> incrby counter 50
(integer) 152
  • INCR\INCRBY、 DECR\DECRBY命令:自增命令,不会产生竞争条件,所以不会重复设置或丢失的情况。
  • GETSET命令:取回并设置
  • 操作多个key:MSET\MGET

03. 键(key)

  • 有些命令没有特定类型,但是和空间的键值交互很有用,可以用在任何类型的键值
  • EXISTS命令:返回 1 或 0 来表示库中有键值或没有键值
  • DEL命令:删除一个key和value,无论这个值是什么,它也返回 1 或 0
  • TYPE命令:返回存储在指定key的值类型

04. 有效期

  • 和数据类型无关,和DEL命令操作的结果类似。Redis 有效期的一些信息:
  • 可以被设置为秒或毫秒的精度
  • 到期时间的分辨率始终精确到1毫秒
  • 有效期的信息复制并持续保存在磁盘上,当Redis停止时,这个时间实际上会过去(这意味着Redis会保存key过期的日期)。
1
2
3
4
5
6
7
8
> set key some-value
OK
> expire key 5
(integer) 1
> get key (immediately)
"some-value"
> get key (after some time)
(nil)
  • EXPIRE命令:设置有效期期
  • PERSIST 命令:移除设置的有效期,让key永远持续下去。
  • SET通过添加ex参数设置有效期
  • ttl命令:查询有效期剩余时间
  • 以上是以秒为基本单位,如果使用毫秒做为单位,参照PEXPIRE 和 PTTL命令,以及全部 SET命令 选项。

05. 队列(Lists)

  • Redis采用链表结构存储队列,它让添加操作无论是到头部还是尾部,都是常量级时间。
  • LPUSH命令:添加到队列头部10个元素和添加1000万个元素是一样到。
  • 不好到方面?采用数组方式实现到队列,通过索引存取操作非常快;但是链表就没这么快了(其中操作需要与所访问元素的索引成比例的工作量)
  • 采用链表来实现Redis队列是至关重要到,因为对于数据库添加一个长表需要很快速。另外到存储优势,正如您稍后将看到的那样,Redis Lists可以在恒定时间内保持恒定长度。
  • 当快速访问大量元素集合的中间位置很重要时,可以使用不同的数据结构,称为排序集。 排序集将在本教程后面介绍。

5.1 第一步

  • LPUSH命令:添加一个新元素到队列左侧(head),它是可变参数,可同时push多个元素
  • RPUSH命令:添加一个新元素到队列右侧(tail),它是可变参数,可同时push多个元素
  • LRANGE命令:从队列取出范围内到元素,两个参数是第一个元素,当为负数时,表示从尾部开始。-1表示最后一个元素,-2表示倒数第二个元素
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
> rpush mylist A
(integer) 1
> rpush mylist B
(integer) 2
> lpush mylist first
(integer) 3
> lrange mylist 0 -1
1) "first"
2) "A"
3) "B"
  • 另外一个重要的操作是pop element,取出队列中的元素,并从队列中消除。如果pop空都表,返回的是nil
  • LPOP命令:
  • RPOP命令

5.2 常见使用场景

  • 队列的两个典型应用
    • 记住社交网络用户的最后一次推送
    • 进程间通信,使用消费-生产模型。生产者推送项到队列,消费者消费这些项并执行。Redis的特定命令来保证这个场景的可靠性和高效性。
  • 比如,著名的Ruby库resquesidekiq使用Redis队列实现后台作业。
  • 著名的Twitter社交网络用户实时最后推送使用Redis队列
  • 要逐步描述常见用例,请假设您的主页显示在照片共享社交网络中发布的最新照片,并且您希望加快访问速度。
    • 每次用户发布新照片时,我们都会将其ID添加到带有LPUSH的列表中。
    • 当用户访问主页时,我们使用LRANGE 0 9来获取最新的10个帖子。

5.3 上限

  • LTRIM命令:Redis允许只记住最近的N条数据,并丢弃就的数据项
  • LTRIM命令类似于LRANGE,但它不是显示指定范围的元素,而是将此范围设置为新队列的值。移除超出给定范围的所有元素。
  • 这个很简单但是很有用的模式:队列的添加操作和队列的限制操作来添加新元素和移除超出限制的元素。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
> rpush mylist 1 2 3 4 5
(integer) 5
> ltrim mylist 0 2
OK
> lrange mylist 0 -1
1) "1"
2) "2"
3) "3"

LPUSH mylist <some element>
LTRIM mylist 0 999
  • LRANGE的复杂度是O(N),访问队列的头部或尾部的小范围是恒定时间操作。

5.4 阻塞操作

  • 通常队列作为进程间通信系统的构建块:阻塞操作
  • 想象下你像通过一个进程push一个项到队列,同时另外的进程想用这些项做一些工作。这是通常的生产者/消费者设置,可以通过下面方式实现:
    • 用LPUSH推送这些项到队列,LPUSH是生产者
    • 提取/执行队列中的项,RPOP是消费者
  • 但是,当队列为空时,RPOP返回空。消费者只有等待并重新执行RPOP来获取,这中方式叫轮询(polling),这并不是个好主意,有以下一些缺点
    • 强制Redis和客户端处理无用命令(当队列为空时,所有请求都不会完成实际工作,它们只会返回NULL)
    • 为项目处理添加延迟,因为在worker收到NULL之后,它会等待一段时间。 为了使延迟更小,我们可以在对RPOP的呼叫之间等待更少,从而放大问题1,即对Redis进行更多无用的呼叫。
  • 所以,Redis实现命令叫BRPOPBLPOP,这是RPOP和LPOP的阻塞版本,如果列表为空:它们只有在数据添加新数据或者用户具体时间到了。
1
2
3
> brpop tasks 5
1) "tasks"
2) "do_something"
  • 上面语句的意思是等待获取队列的元素,如果5秒依然没有返回数据,就返回null;当然也可以使用0来永久等待;还可以获取多个队列的值,当有一个数据获取就返回。BRPOP使用的几点注意:
    • 客户端以顺序的方式提供服务:阻塞等待队列的第一个客户在其他客户端推送元素时首先提供服务。
    • 与RPOP相比,返回值是不同的:它返回一个双元素数组,因为它还包含键的名称,因为BRPOP和BLPOP能够阻止等待来自多个列表的元素。
    • 如果等待时间到了,返回NULL
  • 需要了解队列的阻塞操作,请阅读如下的资料:
    • 可以使用RPOPLPUSH构建更安全的队列或轮换队列。
    • 还有一个命令的阻塞变体,称为BRPOPLPUSH

06 自动创建和删除key

  • 官方文档 - 原文
  • 当队列为空时,redis负责自动删除key;当插入到不存在队列时,redis负责自动创建这个队列。比如使用[LPUSH]()

07 Redis散列表(Hashs)

08 Redis集合(Sets)

09 Redis有序集(Sorted sets)

10 范围操作

11 词典分数

12 更新分数:排行榜

13 Bitmaps

14 HyperLogLogs

15 其他重要特征

16 了解更多