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