CL-REDIS:Redis 的 Common Lisp 冒险之旅 🗺️ 2024-09-20 作者 C3P00 CL-REDIS 就像一个探险家,带着你深入 Redis 的世界,探索数据存储的奥秘。它是一个快速、可靠的 Common Lisp 客户端,让你可以轻松地与 Redis 服务器进行交互。想象一下,它就像一个经验丰富的向导,带着你穿越 Redis 的广阔领域,为你提供所有你需要探索和管理数据的工具。 🧭 快速入门:准备出发 🥾 在你开始你的 Redis 冒险之旅之前,你需要准备一些必需品。首先,确保你有一台正在运行的 Redis 服务器。然后,使用 ql:quickload 'cl-redis 加载 CL-REDIS 库。就像在你的背包里装满地图和指南针一样。 接下来,你需要连接到 Redis 服务器。你可以使用 (redis:connect :host <host> :port <port>) 函数来建立连接。默认情况下,host 为 127.0.0.1,port 为 6379。就像找到你探险的起点一样。 现在,你可以使用 red 包中的 Redis 命令来与服务器进行交互了。例如,你可以使用 (red:ping) 命令测试连接。就像向你的向导打招呼一样。 完成你的探险之后,你可以使用 (redis:disconnect) 函数断开连接。或者,你可以使用 with-connection 宏,它会自动为你打开和关闭连接。就像在你的探险结束后,回到你的出发点一样。 可用命令:你的探险工具箱 🧰 CL-REDIS 提供了大量的 Redis 命令,让你可以执行各种操作,包括: 字符串操作: SET、GET、APPEND、INCR、DECR 等。就像在你的探险中,记录你的发现和修改你的笔记一样。 哈希操作: HSET、HGET、HDEL、HGETALL 等。就像在你的探险中,收集和整理各种信息一样。 列表操作: LPUSH、RPUSH、LRANGE、LREM 等。就像在你的探险中,收集和整理各种信息一样。 集合操作: SADD、SMEMBERS、SISMEMBER、SREM 等。就像在你的探险中,收集和整理各种信息一样。 排序集操作: ZADD、ZRANGE、ZSCORE、ZREM 等。就像在你的探险中,收集和整理各种信息一样。 事务操作: MULTI、EXEC、DISCARD 等。就像在你的探险中,执行一系列操作,并确保它们按顺序完成一样。 发布订阅操作: PUBLISH、SUBSCRIBE、UNSUBSCRIBE 等。就像在你的探险中,与其他探险者进行交流一样。 代码组织:你的探险地图 🗺️ CL-REDIS 提供了两个包:REDIS 和 RED。所有功能都可以在 REDIS 包中使用。为了避免符号冲突,Redis 命令在这个包中定义时,会加上一个前缀(默认情况下为 red-,在编译时设置)。 RED 包是语法糖,它只是提供了没有前缀的 Redis 命令。因此,它不建议导入,以避免与 COMMON-LISP 包发生符号冲突。你只需要使用包限定的符号名称即可。例如,同一个 Redis 命令(例如 GET)可以调用为 RED-GET(如果你导入了 REDIS 包)或 RED:GET。 安装:准备你的装备 🎒 CL-REDIS 可通过 quicklisp 获取。它依赖于以下几个库: usocket flexi-streams rutils 仅用于测试: nuts, bordeaux-threads 调试和错误恢复:你的探险指南 🧭 如果 *echo-p* 为 T,所有客户端-服务器通信将被回显到 *echo-stream* 流中,默认情况下为 *standard-output*。 错误处理模仿了 Postmodern。特别是,当发生错误导致通信流中断时,会发出 redis-connection-error 类型的条件,提供一个 :reconnect 重启。如果选择它,整个 Redis 命令将被重新发送,如果重新连接尝试成功。此外,connect 检查是否已经建立了与 Redis 的连接,如果已经建立,则提供两个重启(:leave 和 :replace)。 当服务器响应错误回复(即以 - 开头的回复)时,会发出 redis-error-reply 类型的条件。 还有一个高级的 with-persistent-connection 宏,它会尽力做到正确的事情™(即在连接断开后自动重新打开连接一次)。 高级用法:你的探险技巧 🧗♀️ 发布订阅 由于没有专门的命令通过发布订阅从 Redis 接收消息,你可以使用以下方法: (bt:make-thread (lambda () (with-connection () (red:subscribe "foo") (loop :for msg := (expect :anything) :do (print msg)))) "pubsub-listener") 要发布消息,可以使用以下方法: (with-connection () (red:publish "foo" "test")) 管道 为了提高性能,Redis 允许对命令进行管道化,并延迟接收结果,直到最后再批量处理。CL-REDIS 提供了 with-pipelining 宏来支持管道化。比较以下示例中的执行时间(使用管道和不使用管道):6.567 秒 vs. 2023.924 秒! (let ((names (let (acc) (dotimes (i 1000 (nreverse acc)) (push (format nil "n~a" i) acc)))) (vals (let (big-acc) (dotimes (i 1000 (nreverse big-acc)) (let (acc) (dotimes (i (random 100)) (push (list (random 10) (format nil "n~a" i)) acc)) (push (nreverse acc) big-acc)))))) (time (redis:with-connection () (redis:with-pipelining (loop :for k :in names :for val :in vals :do (dolist (v val) (apply #'red:zadd k v))) (red:zunionstore "result" (length names) names) (red:zrange "result" 0 -1)))) ;; Evaluation took: ;; 6.567 seconds of real time ;; 3.900243 seconds of total run time (3.200200 user, 0.700043 system) (time (redis:with-connection () (loop :for k :in names :for val :in vals :do (dolist (v val) (apply #'red:zadd k v))) (red:zunionstore "result" (length names) names) (red:zrange "result" 0 -1)))) ;; Evaluation took: ;; 2023.924 seconds of real time ;; 3.560222 seconds of total run time (2.976186 user, 0.584036 system) 请注意,with-pipelining 调用理论上可以嵌套,但结果只对最高级别的管道可用,所有嵌套的管道将返回 :PIPELINED。因此,在这种情况下会发出警告。 内部机制:你的探险指南 🗺️ 通用函数 tell 和 expect 根据 规范 实现 Redis 协议。tell 指定了 Redis 请求的格式,expect 指定了响应的处理方式。实现 expect 上另一种方法的最佳方式通常是使用 def-expect-method,它会安排从套接字读取数据,并提供一个 reply 变量,该变量保存从服务器解码的回复数据,并删除了初始字符。例如: (def-expect-method :ok (assert (string= reply "OK")) reply) Redis 操作通过 def-cmd 定义为普通函数,只需要提供参数和返回类型。def-cmd 将所有定义的函数名称加上 *cmd-prefix* 前缀,默认情况下为 'red。(请注意,设置 *cmd-prefix* 将在编译时生效)。它还将它们从 REDIS 包导出,并从 RED 包导出,但不带前缀。 下面是一个命令定义的示例: (def-cmd KEYS (pattern) :multi "Return all the keys matching the given pattern.") 请参阅 commands.lisp 查看所有定义的命令。 未实现的功能:你的探险计划 🗺️ 以下命令未实现,因为它们不打算在客户端使用:MONITOR、DEBUG OBJECT 和 DEBUG SEGFAULT。 Unix 域套接字支持 – 已计划 一致性哈希 未内置。实际上,这种东西与该库的功能是正交的,可能应该在另一个库中实现。 连接池也没有实现,因为在存在 with-persistent-connection 的情况下,它实际上并不那么需要。持久连接对于专用线程来说更简单、更高效,而且错误更少。但是,连接池还有其他用例,因此它可能会在将来的版本中实现。 致谢:你的探险伙伴 🤝 该库由 Vsevolod Dyomkin vseloved@gmail.com 开发和维护。 在最初阶段,Alexandr Manzyuk manzyuk@googlemail.com 开发了连接处理代码,遵循了 Postmodern 中的实现。后来,它被部分重写,以适应更高级的连接处理策略,例如持久连接。 许可证:你的探险指南 🧭 MIT(有关详细信息,请参阅 LICENSE 文件)。 https://github.com/vseloved/cl-redis/raw/refs/heads/master/README.md
CL-REDIS 就像一个探险家,带着你深入 Redis 的世界,探索数据存储的奥秘。它是一个快速、可靠的 Common Lisp 客户端,让你可以轻松地与 Redis 服务器进行交互。想象一下,它就像一个经验丰富的向导,带着你穿越 Redis 的广阔领域,为你提供所有你需要探索和管理数据的工具。 🧭
快速入门:准备出发 🥾
在你开始你的 Redis 冒险之旅之前,你需要准备一些必需品。首先,确保你有一台正在运行的 Redis 服务器。然后,使用
ql:quickload 'cl-redis
加载 CL-REDIS 库。就像在你的背包里装满地图和指南针一样。接下来,你需要连接到 Redis 服务器。你可以使用
(redis:connect :host <host> :port <port>)
函数来建立连接。默认情况下,host
为127.0.0.1
,port
为6379
。就像找到你探险的起点一样。现在,你可以使用
red
包中的 Redis 命令来与服务器进行交互了。例如,你可以使用(red:ping)
命令测试连接。就像向你的向导打招呼一样。完成你的探险之后,你可以使用
(redis:disconnect)
函数断开连接。或者,你可以使用with-connection
宏,它会自动为你打开和关闭连接。就像在你的探险结束后,回到你的出发点一样。可用命令:你的探险工具箱 🧰
CL-REDIS 提供了大量的 Redis 命令,让你可以执行各种操作,包括:
SET
、GET
、APPEND
、INCR
、DECR
等。就像在你的探险中,记录你的发现和修改你的笔记一样。HSET
、HGET
、HDEL
、HGETALL
等。就像在你的探险中,收集和整理各种信息一样。LPUSH
、RPUSH
、LRANGE
、LREM
等。就像在你的探险中,收集和整理各种信息一样。SADD
、SMEMBERS
、SISMEMBER
、SREM
等。就像在你的探险中,收集和整理各种信息一样。ZADD
、ZRANGE
、ZSCORE
、ZREM
等。就像在你的探险中,收集和整理各种信息一样。MULTI
、EXEC
、DISCARD
等。就像在你的探险中,执行一系列操作,并确保它们按顺序完成一样。PUBLISH
、SUBSCRIBE
、UNSUBSCRIBE
等。就像在你的探险中,与其他探险者进行交流一样。代码组织:你的探险地图 🗺️
CL-REDIS 提供了两个包:
REDIS
和RED
。所有功能都可以在REDIS
包中使用。为了避免符号冲突,Redis 命令在这个包中定义时,会加上一个前缀(默认情况下为red-
,在编译时设置)。RED
包是语法糖,它只是提供了没有前缀的 Redis 命令。因此,它不建议导入,以避免与COMMON-LISP
包发生符号冲突。你只需要使用包限定的符号名称即可。例如,同一个 Redis 命令(例如GET
)可以调用为RED-GET
(如果你导入了REDIS
包)或RED:GET
。安装:准备你的装备 🎒
CL-REDIS 可通过 quicklisp 获取。它依赖于以下几个库:
调试和错误恢复:你的探险指南 🧭
如果
*echo-p*
为T
,所有客户端-服务器通信将被回显到*echo-stream*
流中,默认情况下为*standard-output*
。错误处理模仿了 Postmodern。特别是,当发生错误导致通信流中断时,会发出
redis-connection-error
类型的条件,提供一个:reconnect
重启。如果选择它,整个 Redis 命令将被重新发送,如果重新连接尝试成功。此外,connect
检查是否已经建立了与 Redis 的连接,如果已经建立,则提供两个重启(:leave
和:replace
)。当服务器响应错误回复(即以
-
开头的回复)时,会发出redis-error-reply
类型的条件。还有一个高级的
with-persistent-connection
宏,它会尽力做到正确的事情™(即在连接断开后自动重新打开连接一次)。高级用法:你的探险技巧 🧗♀️
发布订阅
由于没有专门的命令通过发布订阅从 Redis 接收消息,你可以使用以下方法:
要发布消息,可以使用以下方法:
管道
为了提高性能,Redis 允许对命令进行管道化,并延迟接收结果,直到最后再批量处理。CL-REDIS 提供了
with-pipelining
宏来支持管道化。比较以下示例中的执行时间(使用管道和不使用管道):6.567 秒 vs. 2023.924 秒!请注意,
with-pipelining
调用理论上可以嵌套,但结果只对最高级别的管道可用,所有嵌套的管道将返回:PIPELINED
。因此,在这种情况下会发出警告。内部机制:你的探险指南 🗺️
通用函数
tell
和expect
根据 规范 实现 Redis 协议。tell
指定了 Redis 请求的格式,expect
指定了响应的处理方式。实现expect
上另一种方法的最佳方式通常是使用def-expect-method
,它会安排从套接字读取数据,并提供一个reply
变量,该变量保存从服务器解码的回复数据,并删除了初始字符。例如:Redis 操作通过
def-cmd
定义为普通函数,只需要提供参数和返回类型。def-cmd
将所有定义的函数名称加上*cmd-prefix*
前缀,默认情况下为'red
。(请注意,设置*cmd-prefix*
将在编译时生效)。它还将它们从REDIS
包导出,并从RED
包导出,但不带前缀。下面是一个命令定义的示例:
请参阅
commands.lisp
查看所有定义的命令。未实现的功能:你的探险计划 🗺️
MONITOR
、DEBUG OBJECT
和DEBUG SEGFAULT
。with-persistent-connection
的情况下,它实际上并不那么需要。持久连接对于专用线程来说更简单、更高效,而且错误更少。但是,连接池还有其他用例,因此它可能会在将来的版本中实现。致谢:你的探险伙伴 🤝
该库由 Vsevolod Dyomkin vseloved@gmail.com 开发和维护。
在最初阶段,Alexandr Manzyuk manzyuk@googlemail.com 开发了连接处理代码,遵循了 Postmodern 中的实现。后来,它被部分重写,以适应更高级的连接处理策略,例如持久连接。
许可证:你的探险指南 🧭
MIT(有关详细信息,请参阅 LICENSE 文件)。
https://github.com/vseloved/cl-redis/raw/refs/heads/master/README.md