Redis新手向
时间:2023-10-05 23:07:01
文章目录
- 相关资料
- 前言
- Redis Keys
-
- 修改和查询key space
-
- SET && EXISTS && DEL && TYPE
- Redis expires:keys with limited time to live
-
- EXPIRE && TTL && PERSIST
- Redis Strings
-
- SET && GET
- Redis Lists
-
- First steps with Redis Lists
-
- RPUSH && LPUSH
- LRANGE
- LPOP && RPOP
- Common use cases for lists
- Capped lists
-
- LTRIM
- Blocking operations on lists
- Automatic creation and removal of keys
- Redis Hashes
- Redis Sets
- Redis Sorted Sets
-
- Operating on ranges
- Lexicographical(按字典序) scores
- Updating the score: leader boards
- BitMaps
- HyperLogLogs
- 其它值得注意的功能
- 了解更多
相关资料
Redis官网
An introduction to Redis data types and abstractions
在线Tutorial: Try Redis
下载与安装
Redis命令参考(中文)
官方手册command reference
前言
Redis: The name Redis means REmote DIctionary Server.
Redis是一个Key-Value存储系统,一个data structures server。与传统的存储字符串key和字符串value的存储系统相比,Redis的value提供丰富多彩的数据结构。
- Binary-safe strings
- Lists:基于链表的字符串按插入顺序集合
- Sets:无序、不重复的字符串集合
- Sorted Sets:类似于字符串,但每个字符串元素都给出了一个浮点值,称为score。按照所有元素 score 因此,可以使用值排序 ZRANGE 命令以index获取元素
- Hashes:其 value 存储由键值组成的映射。键值对是字符串,类似于Ruby和Python中的hashes
- Bit arrays(or simply bitmaps):字符串值通过特殊指令视为特殊指令bits可以设置和清楚单个数组结果bits,计算所有bits为1的找到第一个1或0的位置bit等等
- HyperLogLogs:数据结构(作者个人看法)是一个非常轻量级的计算不重复元素的数据结构
- Streams:见Introduction to Redis Streams
这篇文章只是常见的Redis总结数据类型和命令,更详细的命令可以查看官方手册command reference
Redis Keys
Redis存储系统的顶层Key,注意与Redis在数据类型中Hash部分的key区分Hash不难知道运算和散列表的知识。Key用于运算Redis的value存储位置。
关于Redis Keys一些特点和规则:
- 二进制安全意味着你可以把二进制序列作为一个key,从字符串 “foo” 到JPEG文件都可以。空字符串也是有效的key
- 从内存、带宽和效率的角度来看,过长key不是个好主意
- 太短的key也不是个好主意。 “u1000flw” 可读性明显低于 “user:1000:followers应该在那里key在长度上寻找平衡
- 最好固定一个key模板。合理使用多个字段
:
.
-
等待字符提升key可读性 “object-type:id"这种模式对应"user:1000” 。多单词字段可以写作 “comment:1000:reply.to” 或者 “comment:1000:reply-to” - key最大允许大小为512MB
修改和查询key space
有几种与特定类型无关的对key操作命令。
SET && EXISTS && DEL && TYPE
/** * EXISTS:returns 1 or 0 to signal if a given key exists or not in the database * DEL:deletes a key and associated value, whatever the value is. * returns 1 or 0 depending on whether the key was removed(it existed) or not(there was no such key with that name). */ 127.0.0.1:6379> SET mykey hello OK 127.0.0.1:6379> EXISTS mykey (integer) 1 127.0.0.1:6379> DEL mykey (integer) 1
127.0.0.1:6379> EXISTS mykey
(integer) 0
/** * TYPE: returns the kind of value stored at the specified key. */
127.0.0.1:6379> SET mykey x
OK
127.0.0.1:6379> TYPE mykey
string
127.0.0.1:6379> DEL mykey
(integer) 1
127.0.0.1:6379> TYPE mykey
none
更多与key space相关的指令参考command reference
Redis expires:keys with limited time to live
在介绍更复杂的数据结构之前,有必要介绍一下另一个与具体类型无关的指令,Redis expires。你可以通过expire指令来给key设定一个寿命,在寿命倒数完毕后相应的key将被销毁,就像执行了DEL指令一样。
- 寿命能以秒和毫秒做单位
- 到期时间分辨率总是1毫秒
- 有关过期的信息被复制并保留在磁盘上,当您的Redis服务器停止时,时间实际上已经过去了(这意味着Redis保存key到期的时间)[参考资料硬翻译,本句笔者并没有理解,有知道的欢迎评论区指出]
EXPIRE && TTL && PERSIST
/** * EXPIRE: set a timeout for a key, which is a limited time to live. */
127.0.0.1:6379> SET key somevalue
OK
127.0.0.1:6379> EXPIRE key 10 /* 10秒后key将被销毁 */
(integer) 1
127.0.0.1:6379> GET key /* 立即执行GET,还能获取到值 */
"somevalue"
127.0.0.1:6379> GET key /* 10秒之后GET,返回nil,nil有(无、不存在的)之意 */
(nil)
/** * TTL: Time To Live. Check the remaining time for the key. * 当key不存在时,返回-2; * 当key存在但没有设置生存时间时,返回-1; * 否则,以秒为单位,返回key的剩余生存时间 */
127.0.0.1:6379> SET key somevalue ex 10 /* create keys with expires using SET options */
OK
127.0.0.1:6379> TTL key
(integer) 5
127.0.0.1:6379> TTL key
(integer) 1
127.0.0.1:6379> TTL key
(integer) -2
/** * PERSIST: remove the expire and make the key persist forever */
127.0.0.1:6379> SET key somevalue ex 20
OK
127.0.0.1:6379> TTL key
(integer) 18
127.0.0.1:6379> PERSIST key
(integer) 1
127.0.0.1:6379> TTL key
(integer) -1
Redis Strings
SET && GET
Redis Key所能对应的最简单的value。初学者最先接触的Redis数据类型。这种情况下,我们是把string类型的key映射到string类型的value。string类型有多种用法,比如可以用来缓存HTML网页。
说明:本文档所有redis命令通过redis-cli工具展示。127.0.0.1是本机ip,6379是redis-server的默认端口。markdown文档内代码块使用的js风格。所以部分代码高亮可能并不合适。
127.0.0.1:6379> SET mykey somevalue
OK
127.0.0.1:6379> GET mykey
"somevalue"
使用SET
和GET
命令可以存取string value。SET其实是赋值操作,意味着如果对应的key已经有了value,那么后续针对该key的SET操作将覆盖原先的值。
value可以是string(包括binary data)的各种形式,你甚至可以把jpeg image当作一个value,但是一个value的大小不能超过512MB。
SET命令可以添加附加参数,比如我可以让key只在没有值的时候才赋值成功,我可以使用以下命令:
/** * NX -- Only set the key if it does not already exist. * XX -- Only set the key if it already exist. */
127.0.0.1:6379> SET mykey somevalue
OK
127.0.0.1:6379> SET mykey newval NX /* 只在key没有初始值的时候赋值 */
(nil)
127.0.0.1:6379> GET mykey
"somevalue"
127.0.0.1:6379> SET mykey newval XX /* 只在key有初始值的时候赋值 */
OK
127.0.0.1:6379> GET mykey
"newval"
number型表现形式的string在redis中可以直接进行原子性的增减操作,比如:
127.0.0.1:6379> INCR num
(integer) 101
127.0.0.1:6379> INCR num
(integer) 102
127.0.0.1:6379> INCRBY num 50
(integer) 152
INCR命令把string value转化成integer,加一,然后把新值赋值给value。与之类似的指令还有INCRBY、DECR和DECRBY。
原子性意味着什么呢?当多个客户端同时读取同一个string value时,进行加减操作,结果永远不会并发出错。比如客户端1读取“10”,客户端2读取“10”,客户端1+1,客户端2加1,结果永远是“12”。
Redis的所有操作都是原子性的,意思就是要么成功执行,要么失败完全不执行。单个操作是原子性的,多个操作也支持事务进行原子性包装,通过MULTI和EXEC指令包起来。
Redis支持单挑指令进行多个SET和GET操作,此种做法有助于减少延迟。
127.0.0.1:6379> MSET a 10 b 20 c 30
OK
127.0.0.1:6379> MGET a b c /* When MGET is userd, return an array of values */
1) "10"
2) "20"
3) "30"
127.0.0.1:6379> GET a
"10"
127.0.0.1:6379> GET b
"20"
127.0.0.1:6379> GET c
"30"
Redis Lists
List在各种编程语言和信息技术中都有,不同的编程语言语境表达的意思稍有差异,比如“Python Lists”指的是Arrays,但不管底层实现用的什么,其本质思想就是一个线性表。存储的元素按序排列,对于Arrays是使用数据的物理顺序表达逻辑顺序,而Linked List则是用一个指向下一个元素的指针表示逻辑顺序。
Redis Lists的底层实现是Linked Lists。这意味着即使有上百万的元素,在对Lists头部和尾部添加元素时耗费的都是常数时间。
缺点是通过indes访问元素时比用Array实现时要慢许多。
Redis使用Linked Lists实现的一个原因是对于数据库而言,插入元素是一个很常见和重要而且很在乎效率的操作,而且对于Redis而言,固定长度的index访问能在常量时间内完成。
当需要快速访问到集合中间位置时,Redis提供了另一个结构Sorted Sets供用户使用。
First steps with Redis Lists
RPUSH && LPUSH
/** * RPUSH:adds a new element into a list, on the right(at the tail) * LPUSH:adds a new element into a list, on the left(at the head) */
127.0.0.1:6379> RPUSH mylist A
(integer) 1
127.0.0.1:6379> RPUSH mylist B
(integer) 2
127.0.0.1:6379> LPUSH mylist first
(integer) 3
/** * Both LPUSH and RPUSH commands are variadic commands, meaning that you are free to push multiple elements into a list in a single call. */
127.0.0.1:6379> RPUSH mylist 1 2 3 4 5 "foo bar" /* foo bar 是计算机科学中常用的占位符和伪变量,并无实际意义 */
(integer) 9
127.0.0.1:6379> LRANGE mylist 0 -1
1) "first"
2) "A"
3) "B"
4) "1"
5) "2"
6) "3"
7) "4"
8) "5"
9) "foo bar"
维基百科: foo bar
LRANGE
/** * LRANGE: extracts ranges of elements from lists, L means this range for list. * Takes two indexs, the first and the last element of the range to return. * Both the indexes can be negative, telling Redis to start counting from the end. * so -1 is ths last element, -2 is the penultimate element of the list, and so forth. */
127.0.0.1:6379> LRANGE mylist 0 -1 /* */
1) "first"
2) "A"
3) "B"
注意:虽然LRANGE从技术上讲是一个O(N)命令,但访问列表头部或尾部的小范围是一个恒定时间操作。
LPOP && RPOP
/** * RPOP && LPOP: Popping elements is the operation of both retrieving the element from the list and eliminating it from the list at the same time. * RPOP: pop elements from right * LPOP: pop elements from left * Returns: nil if the list is empty */
127.0.0.1:6379> RPUSH list a b c
(integer) 3
127.0.0.1:6379> RPOP list
"c"
127.0.0.1:6379> LPOP list
"a"
127.0.0.1:6379> RPOP list
"b"
127.0.0.1:6379> RPOP list
(nil)
Common use cases for lists
Lists可以用于许多任务
- 记住用户在社交网络上发布的最新更新
- 进程之间的通信,使用消费者-生产者模式,其中生产者将项目推送到列表中,消费者(通常是工人)消耗这些项目并执行操作。Redis有特殊的列表命令,使这个用例更可靠、更高效。
例如,流行的Ruby库reque和sidekiq都使用引擎盖下的Redis Lists来实现后台作业。
流行的Twitter社交网络将用户发布的最新推文纳入Redis Lists。
这里逐步描述一下更常见的情况,请想象您的主页显示照片共享社交网络中发布的最新照片,并希望加快访问速度。
每次用户发布新照片时,我们都会将其ID添加到LPUSH列表中。
当用户访问主页时,我们使用LRANGE 0 9来获取最新的10个发布项目。
Capped lists
LTRIM
在许多用例中,我们只想使用Lists来存储最新项目,无论它们是什么:社交网络更新、日志或其他任何内容。
Redis允许我们使用列表作为带上限的集合,仅记住最新的N个项目,并使用LTRIM命令丢弃所有最古老的项目。
LTRIM命令类似于LRANGE,但它没有显示指定的元素范围,而是将这个范围设置为新的列表值。删除给定范围以外的所有元素。
/** * LTRIM: similar to LRANGE, but instead of displaying the specified range of elements it sets this range as the new list value. All the elements outside the given range are removed. */
127.0.0.1:6379> RPUSH mylist 1 2 3 4 5
(integer) 5
127.0.0.1:6379> LTRIM mylist 0 2
OK
127.0.0.1:6379> LRANGE mylist 0 -1
1) "1"
2) "2"
3) "3"
这允许一个非常简单但有用的模式:一起做列表推送操作+列表修剪操作,以添加新元素并丢弃超过限制的元素:
LPUSH mylist <some element>
LTRIM mylist 0 999
上述组合添加了一个新元素,仅将1000个最新元素放入列表中。使用LRANGE,您可以访问顶级项目,而无需记住非常旧的数据。
Blocking operations on lists
列表具有特殊功能,使其适合实现队列,通常作为进程间通信系统的基石:阻塞操作。
想象一下,您想通过一个进程将项目推送到列表中,并使用不同的进程来实际使用这些项目进行某种工作。这是常见的生产者/消费者设置,可以通过以下简单方式实现:
- 要将项目推入列表中,生产商调用LPUSH。
- 要从列表中提取/处理项目,消费者调用RPOP。
然而,有时列表可能是空的,没有什么需要处理的,所以RPOP只是返回NULL。在这种情况下,消费者被迫等待一段时间,然后使用RPOP再次重试。这被称为轮询,在这种情况下不是一个好主意,因为它有几个缺点:
- 强迫Redis和客户端处理无用的命令(列表为空时的所有请求将无法完成实际工作,他们只会返回NULL)。
- 增加了项目处理的延迟,因为工人收到NULL后,它会等待一段时间。为了减少延迟,我们可以在调用RPOP之间减少等待,从而放大问题1,即对Redis的更多无用调用。
因此,Redis实现了名为BRPOP和BLPOP的命令,这些命令是RPOP和LPOP的版本,如果列表为空,则可以阻止:它们只有在向列表中添加新元素或达到用户指定的超时时,它们才会返回给调用方。
以下是我们可以在工人中使用的BRPOP调用的示例:
127.0.0.1:6379> RPUSH tasks "tasks" "do_something"
(integer) 2
127.0.0.1:6379> BRPOP tasks 5
1) "tasks"
2) "do_something"
127.0.0.1:6379> BRPOP tasks 5
1) "tasks"
2) "tasks"
127.0.0.1:6379> BRPOP tasks 5
(nil)
(5.04s)
这意味着:“等待列表任务中的元素,但如果5秒后没有可用的元素,则返回”。
请注意,您可以使用0作为超时器,以永久等待元素,您还可以指定多个列表,而不仅仅是一个列表,以便同时在多个列表中等待,并在第一个列表收到元素时收到通知。
关于BRPOP,需要注意的几点:
- 客户端以有序的方式提供服务:阻止等待列表的第一个客户端,当元素被其他客户端推送时,首先服务,依此类推。
- 与RPOP相比,返回值不同:它是一个双元素数组,因为它也包含密钥的名称,因为BRPOP和BLPOP能够阻止从多个列表中等待元素。
- 如果达到超时,将返回NULL。
关于列表和阻止操作,你应该知道更多事情。我们建议您查阅一下命令:
使用LMOVE可以构建更安全的队列或旋转队列。
该命令还有一个阻止变体,称为BLMOVE。
Automatic creation and removal of keys
到目前为止,在我们的示例中,我们从未在推送元素之前创建空Lists,或在其中不再有元素时删除空Lists。Redis有责任在LIsts为空时删除key,如果key不存在,我们正在尝试向其中添加元素,例如使用LPUSH,则创建空Lists。
这不特定于列表,它适用于由多个元素组成的所有Redis数据类型——Steams、Sets、Sorted Sets和Hashes。
基本上,我们可以用三条规则来总结:
- 当我们将元素添加到聚合数据类型时,如果目标key不存在,则在添加元素之前创建一个空聚合数据类型。
127.0.0.1:6379> DEL mylist (integer) 1 127.0.0.1:6379> LPUSH mylist 1 2 3 (integer) 3 /* 然而,如果密钥存在,我们无法对错误的类型执行操作 */ 127.0.0.1:6379> SET foo bar OK 127.0.0.1:6379> LPUSH foo 1 2 3 (error) WRONGTYPE Operation against a key holding the wrong kind of value 127.0.0.1:6379> TYPE foo string
- 当我们从聚合数据类型中删除元素时,如果value保持空,key将自动销毁。Steam类型是此规则的唯一例外。
127.0.0.1:6379> LPUSH mylist 1 2 3 (integer) 3 127.0.0.1:6379> EXISTS mylist (integer) 1 127.0.0.1:6379> LPOP mylist "3" 127.0.0.1:6379> LPOP mylist "2" 127.0.0.1:6379> LPOP mylist "1" 127.0.0.1:6379> EXISTS mylist /* The key no longer exists after all the elements are popped. */ (integer) 0
- 使用空key调用LLEN(返回列表长度)等只读命令或删除元素的写命令总是产生与键持有命令预期找到的类型的空聚合类型相同的结果。
127.0.0.1:6379> del mylist (integer) 0 127.0.0.1:6379> llen mylist (integer) 0 127.0.0.1:6379> lpop mylist (nil)
Redis Hashes
Redis Hashes与人们期望的“hash”一致,属于键值对:
127.0.0.1:6379> HMSET user:1000 username holon birthyear 1997 verified 1
OK
127.0.0.1:6379> HGET user:1000 username
"holon"
127.0.0.1:6379> HGET user:1000 birthyear
"1997"
127.0.0.1:6379> HGETALL user:1000
1) "username"
2) "holon"
3) "birthyear"
4) "1997"
5) "verified"
6) "1"
虽然hashes可用于表示_objects_,但实际上,您可以放置在Hashes中的字段数量没有实际限制(可用内存除外),因此您可以在应用程序内以多种不同方式使用Hashes。
HMSET命令设置Hashes的多个字段,而HGET检索单个字段。HMGET与HGET相似,但返回一个数组:
127.0.0.1:6379> HMGET user:1000 username birthyear no-such-field
1) "holon"
2) "1997"
3) (nil)
有些命令也可以对单个字段执行操作,如HINCRBY:
127.0.0.1:6379> HINCRBY user:1000 birthyear 1
(integer) 1998
127.0.0.1:6379> HINCRBY user:1000 birthyear 10
(integer) 2008
您可以在文档中找到Hash命令的完整列表。
值得注意的是,小散列(即几个值较小的元素)在内存中以特殊方式编码,使其内存效率很高。
Redis Sets
Redis Sets是字符串的无序集合。SADD命令向Set添加新元素。也可以对Set进行许多其他操作,例如测试给定元素是否已经存在,在多个集合之间执行交集、联合或差分等。
127.0.0.1:6379> SADD myset 1 2 3
(integer) 3
127.0.0.1:6379> SMEMBERS myset
1) "1"
2) "2"
3) "3"
在这里,我在集合中添加了三个元素,并告诉Redis返回所有元素。如您所见,它们没有排序——Redis在每次调用时都可以自由返回元素,因为与用户没有关于元素顺序的约定。
Redis有检查元素存在与否的命令。例如,检查元素是否存在:
127.0.0.1:6379> SISMEMBER myset 3
(integer) 1
127.0.0.1:6379> SISMEMBER myset 30
(integer) 0
“3”是Sets的成员,而“30”不是。
Sets有利于表达对象之间的关系。例如,我们可以很容易地使用集合来实现标签。
建模这个问题的一个简单方法是为我们想要标记的每个对象设置一个set。该set包含与对象关联的标签的ID。
一个例子是给新闻文章贴标签。如果文章ID 1000用标签1、2、5和77标记,一组可以将这些标签ID与新闻项目关联:
127.0.0.1:6379> SADD news:1000:tags 1 2 5 77
(integer) 1
我们可能还希望有相反的关系:所有带有给定标签的新闻的列表:
127.0.0.1:6379> SADD tag:1:news 1000
(integer) 1
127.0.0.1:6379> SADD tag:2:news 1000
(integer) 1
127.0.0.1:6379> SADD tag:5:news 1000
(integer) 1
127.0.0.1:6379> SADD tag:77:news 1000
(integer) 1
获取给定对象的所有标签非常简单:
127.0.0.1:6379> SMEMBERS news:1000:tags
1) "1"
2) "2"
3) "5"
4) "7"
5) "77"
注意:在示例中,我们假设您有另一个数据结构,例如Redis散列,它将标签ID映射到标签名称。
使用正确的Redis命令,还有其他非琐碎的操作仍然很容易实现。例如,我们可能想要一个带有标签1、2、10和27的所有对象的列表。我们可以使用SINTER命令做到这一点,该命令执行不同集之间的交集。我们可以使用:
127.0.0.1:6379> SINTER tag:1:news tag:2:news tag:10:news tag:27:news
... results here ...
除了交叉外,您还可以执行联合、差异、提取随机元素等。
提取元素的命令称为SPOP,用于建模某些问题。例如,为了实现基于网络的扑克游戏,想象一下,我们对(C)lubs(♣️)、(D)iamonds(♦️)、(H)earts(♥️)、(S)pades(♠️)使用单字符前缀:
127.0.0.1:6379> SADD deck C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 CJ CQ CK
D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 DJ DQ DK
H1 H2 H3 H4 H5 H6 H7 H8 H9 H10 HJ HQ HK
S1 S2 S3 S4 S5 元器件数据手册、IC替代型号,打造电子元器件IC百科大全!