From a9057466a84eaff208b56b80fc1c813dbe63db76 Mon Sep 17 00:00:00 2001 From: asahi Date: Sat, 6 Sep 2025 15:45:17 +0800 Subject: [PATCH] =?UTF-8?q?doc:=20=E9=98=85=E8=AF=BBredis=20lists=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 中间件/redis/redis.md | 151 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) diff --git a/中间件/redis/redis.md b/中间件/redis/redis.md index fb80f7d..5be03fc 100644 --- a/中间件/redis/redis.md +++ b/中间件/redis/redis.md @@ -50,6 +50,12 @@ - [Redis List Impl](#redis-list-impl) - [`LPUSH, RPUSH`](#lpush-rpush) - [`LRANGE`](#lrange) + - [`LPOP, RPOP`](#lpop-rpop) + - [Common use cases for lists](#common-use-cases-for-lists) + - [Capped lists - `latest n`](#capped-lists---latest-n) + - [Blocking operations on lists](#blocking-operations-on-lists) + - [BRPOP](#brpop) + - [Automatic Creation and removal of keys](#automatic-creation-and-removal-of-keys) # redis @@ -700,3 +706,148 @@ LPUSH和RPUSH接收的参数都是可变的,在单次调用中可以向list中 2) "bike:1" 3) "bike:2" ``` +#### `LPOP, RPOP` +lists支持pop元素,从list中移除元素,并且获取元素的值。lists支持从list的左端和右端pop元素,使用示例如下所示: +```redis-cli +> RPUSH bikes:repairs bike:1 bike:2 bike:3 +(integer) 3 +> RPOP bikes:repairs +"bike:3" +> LPOP bikes:repairs +"bike:1" +> RPOP bikes:repairs +"bike:2" +> RPOP bikes:repairs +(nil) +``` + +#### Common use cases for lists +redis lists拥有如下有代表性的用例场景: +- 记录用户最新上传的推文 +- 用于进程间的通信,使用consumer-producer pattern,其中生产者向lists中推送内容,而消费者消费lists中的内容 + +#### Capped lists - `latest n` +在许多用例场景下,会使用lists来存储latest items,例如`social network updates, logs`等。 + +redis允许将lists作为`拥有容量上限的集合使用`,可以通过`LTRIM`命令来实现`only remembering the latest N items and discarding all the oldest items`。 + +`LTRIM`命令和`LRANGE`类似,但是`LRANGE`用于获取获取list中指定范围内的元素,`LTRIM`会将选中的范围作为`new list value`,所有位于选中范围之外的元素都会从list中被移除。 + +`LTRIM`的使用示例如下所示: +```redis-cli +> RPUSH bikes:repairs bike:1 bike:2 bike:3 bike:4 bike:5 +(integer) 5 +> LTRIM bikes:repairs 0 2 +OK +> LRANGE bikes:repairs 0 -1 +1) "bike:1" +2) "bike:2" +3) "bike:3" +``` +`LTRIM 0 2`会令redis保留index位于`[0, 2]`范围内的3个元素,并且移除其他的元素。将`push操作`和`LTRIM`操作组合,可以实现`add a new element and discard elements exceeding a limt`的操作。 + +例如,使用`LRANGE -3 -1`可用于实现`仅保留最近添加的三个元素`的场景 + +```redis-cli +> RPUSH bikes:repairs bike:1 bike:2 bike:3 bike:4 bike:5 +(integer) 5 +> LTRIM bikes:repairs -3 -1 +OK +> LRANGE bikes:repairs 0 -1 +1) "bike:3" +2) "bike:4" +3) "bike:5" +``` + +#### Blocking operations on lists +lists的`blocking operation`特性令其适合用于实现queues,并广泛用于进程间通信系统。 + +在通过redis lists实现进程间通信系统时,如果某些时刻list为空,并不存在任何元素,那么此时消费者client在调用`pop`操作时只会返回为空。通常来讲,consumer会等待一定的时间并且重新尝试调用pop,该操作被称为`polling`,其通常被认为是一种不好的实现: +- 其会强制redis/client来处理无用的命令(`当list为空时,pop请求只会返回为空而不会任何的实际处理`) +- 会增加`delay to processing of items`,因为worker在接收到redis server返回的null时,其会等待一定的时间。为了令delay更小,可以在调用POP操作之间等待更短的时间,但是其可能方法前一个问题(`当pop调用之间的时间间隔更小时,redis server可能会处理更多的无用命令`) + +故而,redis实现支持`BRPOP`和`BLPOP`命令,其命令在list为空时会阻塞:`上述命令造成的阻塞会在list中被添加新元素时返回,如果直到设置的超时到达后,该操作也会返回`。 + +`BRPOP`的使用示例如下所示: +```redis-cli +> RPUSH bikes:repairs bike:1 bike:2 +(integer) 2 +> BRPOP bikes:repairs 1 +1) "bikes:repairs" +2) "bike:2" +> BRPOP bikes:repairs 1 +1) "bikes:repairs" +2) "bike:1" +> BRPOP bikes:repairs 1 +(nil) +(2.01s) +```` +上述示例中,`BRPOP bikes:repairs 1`代表`wait for elements in the list bikes:repairs`,但是`当list中元素为空时,最多等待1s。` + +当为`BRPOP`指定timeout为0时,代表会永久等待elements。并且,`可以为BRPOP`命令指定多个lists,其会`等待所有的list,并且当任何一个list中接收到元素时,当前BRPOP命令会立刻返回`。 + +示例如下: +```redis-client +# client 1 等待 event-queue:1, event-queue:2, event-queue:3三个list +client 1> brpop event-queue:1 event-queue:2 event-queue:3 1000 + +# client 2 向event-queue:2 中追加元素 +client 2> rpush event-queue:2 baka +(integer) 1 + +# client 1 立刻返回,返回结果如下 +1) "event-queue:2" +2) "baka" +(19.55s) +``` +##### BRPOP +- 对于`BRPOP`命令造成的阻塞,其处理是按照顺序的:`the first client that blocked waiting for a list, is served first when an element is pushed by some other client, and so forth` +- `BRPOP`命令的返回结果其结构和`RPOP`命令不同:`BRPOP`返回的是一个包含两个元素的array,`arrary[0]`为list对应的key,`array[1]`为弹出的元素(因为BRPOP可以等待多个list) +- 如果超时后list中仍然没有可获取的元素,那么将会返回null + +#### Automatic Creation and removal of keys +在先前示例中,向list中添加元素时,并没有预先创建空的list,或是在list中没有元素时将list手动移除。 + +在redis中,`list的创建和删除都是redis的职责`: +- 当list中不再包含元素时,redis会自动删除list对应的key +- 当想要对不存在的key中添加元素时,redis会自动创建一个empty list + +故而,可以整理除如下准则: +- 当将元素添加到一个聚合数据类型时,如果target key不存在,那么在添加元素前一个empty aggregate data type将会被自动创建 +- 当从aggregate data type中移除元素时,如果移除后该aggregate data type中为空,那么key将会被自动销毁(`stream data type`除外) +- 调用一个read-only command(例如`LLEN`)或write command(用于移除元素)对一个`empty key`做操作时,其返回结果和针对`an key holding an empty aggregate type of type the command expects to find`的操作一直 + +上述三个准则的示例如下: + +准则1示例如下所示,当new_bikes不存在时,通过`LPUSH`命令向其中添加元素,一个empty list在添加前会自动创建 +```redis-cli +> DEL new_bikes +(integer) 0 +> LPUSH new_bikes bike:1 bike:2 bike:3 +(integer) 3 +``` + +准则2示例如下所示,当pop出所有的元素后,key将会被自动销毁,通过`EXISTS`命令返回的结果为0: +```redis-cli +> LPUSH bikes:repairs bike:1 bike:2 bike:3 +(integer) 3 +> EXISTS bikes:repairs +(integer) 1 +> LPOP bikes:repairs +"bike:3" +> LPOP bikes:repairs +"bike:2" +> LPOP bikes:repairs +"bike:1" +> EXISTS bikes:repairs +(integer) 0 +``` +准则3的示例如下所示,当key不存在时,对该key进行`read-only`操作和`remove element`操作所返回的结果,和对`empty aggregated data type`操作所返回的结果一致: +```redis-cli +> DEL bikes:repairs +(integer) 0 +> LLEN bikes:repairs +(integer) 0 +> LPOP bikes:repairs +(nil) +```