anders murphy

RSS: https://andersmurphy.com/feed.xml
主要关于 Clojure 编程的博客。

SQLite 性能优化:预排序提升随机数据插入速度

SQLite 插入随机数据慢?作者实测:批量预排序(pre-sort)可将写入速度提升 2–2.5 倍。针对安全令牌等无法使用 UUID7 的随机 BLOB 主键场景,利用 B+Tree 顺序写入快的特性,先对百万级批次按前 8 字节排序再插入。文章附完整 Clojure 代码、1000 万行基准数据表和性能对比,不是空谈技巧,是有一手数据支撑的工程实战经验。
评论点赞收藏70 天前

SQLite 中 UUID 主键的性能陷阱与实测分析

本文通过1000万至1亿行的真实基准测试,实测了SQLite中使用UUID4/UUID7作为主键的性能代价。自增INTEGER主键可达每秒约100万次插入;UUID4+WITHOUT ROWID因B树频繁重平衡,插入速度暴跌至INTEGER的1/14~1/16;改用时间有序的UUID7可恢复大部分性能(仍慢约1.6倍)。作者附带火焰图差异分析揭示瓶颈所在,并对比了UUID4+ROWID(双索引写放大)方案,给出了明确的选型建议。对于所有使用聚簇索引的数据库均有实际参考意义。
评论点赞收藏71 天前

十亿行数据、十万 TPS:SQLite 为什么如此出人意料地高效

核心看点:实测数据证明,在交互式事务场景下,嵌入式单写入的 SQLite 反而能比网络数据库 Postgres 获得更高吞吐。作者通过 Clojure 基准测试,模拟十亿行数据、幂律分布用户(0.05% 用户承担 99.95% 事务),对比 SQLite 和 Postgres 在有无网络延迟下的 TPS。结果:无网络下 SQLite 达 44096 TPS(Postgres 13756);加入批处理后 SQLite 达 186157 TPS;混合读写仍保持 102545 TPS。核心原因:网络延迟引发 Amdahl 定律瓶颈,而 SQLite 嵌入式架构消除了网络开销。作者分享了自己在构建 Discord 打赏机器人时因此踩坑的经历,并坦诚回应了 Hacker News 上关于同步模式、连接池大小等质疑,附上更新后的数据。文章不是踩 Postgres 捧 SQLite,而是揭示网络交互式事务在幂律分布下的硬上限,以及 SQLite 作为单机方案的实际能力。
评论点赞收藏257 天前

用 Project Panama 和 Coffi 在 Clojure 中封装 SQLite C API

作者用 Coffi 库在 Clojure 中直接调用 SQLite C API(基于 Java Project Panama FFI),完整实现了从编译 SQLite、加载动态库、连接管理、预编译语句缓存、参数绑定到连接池的全流程。关键成果是与 JDBC + HikariCP 方案相比,简单查询性能提升约 3 倍(均值 545 µs)。文章不只是贴代码,给出了真实生产环境的配置调优经验(WAL 模式、页面大小、缓存阈值等),并提供了完整源码库 sqlite4clj 和基准对比数据。有实际生产验证(One Billion Checkboxes Demo 项目),有 Reddit 讨论入口。不是泛泛教程,而是有个人踩坑记录和量化对比的扎实技术分享,适合关注 Clojure 底层性能或 Java FFI 实践的开发者。
评论点赞收藏453 天前

为什么你应该考虑在 SSE 中使用 Brotli 压缩

作者用 Brotli 压缩拯救了自己被 HN 流量冲垮的多人 Game of Life 游戏服务器。核心经验:对 SSE(Server-Sent Events)流数据,Brotli 的可调上下文窗口(从默认 32KB 调至 263KB)能将压缩比从 30:1 提升到 150-250:1,同时服务器 CPU 降低 4-8 倍。一台 15 美元/月的 Hetzner VPS,若不调窗口直接被打爆。文章还对比了 GZIP(SSE 流上 Brotli 压缩比高 2-3 倍且 CPU 更低)和 ZSTD(Safari 不支持,iOS 全系不可用),附完整 Clojure 实现代码和 HN/Reddit 讨论链接。真实个人踩坑经验,有数据、有代码、有对比、有结论,对使用 SSE 做实时推送的开发者有直接参考价值。
评论点赞收藏488 天前

用 Clojure 做实时协作网页应用,但完全不用 ClojureScript

作者用纯 Clojure 服务端 + Datastar 超媒体框架,零 ClojureScript、零手写 JS,实现了一个多人协作的康威生命游戏网页应用。核心方案是每 200ms 通过 SSE 把整个 main 元素从服务端推到客户端,再靠 Brotli 压缩(可达 100-230:1 的压缩比)和快速 DOM 合并算法确保性能。作者现场对比了 WebSocket 的诸多运维痛点(防火墙、负载均衡、无压缩、移动端耗电等),力挺 SSE 作为常规 HTTP 协议天然具备多路复用、压缩、断线重连等能力。给出了完整的 hyperlith 框架代码示例,并指出多人协作无需额外逻辑——只要不区分用户渲染,所有人看到的就是同一个实时状态。全文有演示、有代码、有实测性能数据,并在 HN/Reddit 上有真实讨论,观点鲜明,对 Clojure 生态开发者有实用参考价值。
评论点赞收藏496 天前

Clojure:使用虚拟线程和通道实现同步服务器推送事件

本文介绍如何在 Clojure 中利用 Java 21 虚拟线程 + core.async 通道,以同步方式实现 Server-Sent Events(SSE)推送。作者明确表达了个人偏好——同步 handler 的简洁性远胜异步复杂度,并借助虚拟线程做到了"鱼与熊掌兼得"。文章完整给出了从扩展 StreamableResponseBody、用 atom 追踪客户端连接、心跳保活、广播消息到配置 Jetty 虚拟线程池的全部代码,每个步骤都附带解释和踩坑提醒(如客户端断开时通过 IOException 捕获来清理僵尸连接)。还对比了传统 go block 写法,指出虚拟线程下阻塞式 core.async 操作更简单且无需 reach for go blocks。附有完整示例项目仓库和进一步阅读链接。适合需要为 Clojure Web 应用添加 SSE 实时推送的开发者,内容实用、有个人实践判断,不是泛泛教程。
评论点赞收藏678 天前

如何最大化从第三方服务拉取数据的吞吐量

本文从作者的真实项目经验出发,系统梳理了从第三方服务拉取大量数据时最大化吞吐量的关键考量。以一个需要拉取用户最近100笔交易签名为例,串行执行需105秒,而利用第三方API速率限制理论上可压缩到10.5秒。文章涵盖:验证真实的速率限制、批量请求优化、幂等性设计确保部分失败可恢复、退避重试机制、全局线程安全限速器、区分可并发和需顺序执行的任务、无序并发减少延迟、数据库写入瓶颈的背压控制、99%采样实测降低50%等待时间、防止饿死的FIFO调度。作者基于一个近期项目的真实数据,给出了每个环节的具体权衡和实现思路,强调幂等性是最重要的设计原则——它让你能安全处理部分同步和恢复。非空泛教程,有真实踩坑经验。
评论点赞收藏689 天前

Clojure:虚拟线程与动态变量性能对比

作者通过可复现的基准测试,对比了 Clojure 中动态变量、作用域值(Scoped Values)和显式参数在 50000 个虚拟线程下的性能表现。实测数据表明:动态变量平均耗时约 1.2 秒,而作用域值和显式参数均约 0.19 秒,前者慢了约 5 倍。根本原因在于 Clojure 动态变量底层依赖 ThreadLocal,虚拟线程大量创建时 ThreadLocal 的继承开销急剧放大(JEP 446 有详细说明)。作者的实践建议是:多数场景直接显式传递上下文就够用;若确实需要动态作用域且使用大量虚拟线程,优先选作用域值而非动态变量,但要注意作用域值的线程传递仅在结构化并发(StructuredTaskScope)下生效,使用 Executor/CompletionService 时只能走显式参数或动态变量。全文附带完整代码与 GitHub 项目,是一手经验加清晰结论的优质技术笔记。
评论点赞收藏808 天前

Clojure 结构化并发与作用域值

本文详细演示了如何在 Clojure 中利用 Java 21 的结构化并发(StructuredTaskScope)和作用域值(Scoped Values)预览特性来编写更安全的并发代码。作者通过自定义 pmap 和 alts 函数,展示结构化并发如何自动处理线程取消、避免线程泄漏和取消延迟;对比了 Clojure 动态变量的绑定传递与 Java 作用域值的继承机制,指出 StructuredTaskScope 不会自动传递绑定上下文,需要用 bound-fn* 解决;最终编写了一个 scoped-binding 宏,让作用域值的使用更符合 Clojure 习惯。文中包含完整可运行代码、错误处理演示及性能后续讨论链接,是面向中高级 Clojure 开发者的实用技术分享,有清晰的个人实践痕迹和代码品味判断。
评论点赞收藏824 天前

Clojure:使用虚拟线程管理吞吐量

本文由 Clojure 开发者分享如何利用 Java 21 虚拟线程和信号量(Semaphore)实现速率限制与无序并发处理的实战经验。Claypoole 线程池方案在虚拟线程时代不再适用,作者通过 ExecutorCompletionService 实现 unordered pmap(upmap),结合 Semaphore 令牌桶算法控制第三方 API 请求吞吐量,最后用额外虚拟线程解决提交阻塞问题。全程零依赖、全代码可运行,附 GitHub 项目和 Hacker News 讨论链接。
评论点赞收藏832 天前

Clojure 项目 CI 配置:GitHub Actions + PostgreSQL 实战指南

一篇非常实用的 Clojure CI 配置实战记录。作者因 Java 21 虚拟线程和分代 ZGC 需求从 Circle CI 迁移到 GitHub Actions,给出了可直接复用的完整 workflow 配置——包括 tools.deps 环境、PostgreSQL 服务容器、依赖缓存、格式化检查和测试运行的全套步骤。亮点在于不仅给了 YAML,还额外展示了如何用 babashka + clj-yaml 以 Clojure 代码编程生成 YAML 配置,避免了手写 YAML 的繁琐。适合正在搭建或迁移 Clojure CI 管道的开发者直接参考。
评论点赞收藏862 天前

用 clojure.walk 裁剪 HTML:一个 Clojure 爬虫数据清洗实战

作者分享了一个 Web 爬虫场景中常见的痛点:抓取到的 HTML 包含大量无用的布局标签、脚本、属性等噪声数据。文章以清理 页面为例,展示如何组合 Hickory(HTML 解析 → Hiccup 格式)、Malli(数据验证 + 模式匹配)和 Regal(组合式正则)三大库,配合 clojure.walk/postwalk 实现递归删除无用标签、解包多余嵌套、仅保留 href 属性的完整方案。代码逐段演进,每一步都有明确的动机和输出对比,最后还推荐了 Mozilla 的 Readability.js 作为概率类清洗方案的参考。对做 Clojure 爬虫或 HTML 清洗的读者来说,是一篇可直接复用的实战笔记。
评论点赞收藏867 天前

在 Emacs 中用 EMMS 收听网络电台

一篇简短的个人博客,分享如何在 Emacs 里用 EMMS(Emacs 多媒体系统)配合 mpv 播放器来听网络电台。作者怀念开车通勤时听广播的感觉,于是在 Emacs 中搭了一套方案,贴出了完整的 use-package 配置代码,并以 BBC Radio 4 为例给出可直接使用的流媒体地址。内容短小、实操性强,适合想在 Emacs 里增加电台收听功能的读者直接复制配置。有个人动机描述,有可复用的代码片段,是一篇真诚的工具类分享。
评论点赞收藏868 天前

Clojure:REPL 让开源贡献变得简单

作者通过一次真实的 Clojure 开源库调试经历,展示了 REPL 如何让贡献开源变得异常顺手——克隆项目、定位 bug、修改代码、即时验证,全程无需重启 REPL 或重新构建项目。以 Datalevin 数据库库的:validate-data? 选项失效问题为例,从堆栈追踪出发查出验证代码被放错执行顺序,移动几行代码就完成修复,整个过程不依赖 IDE 构建或外部工具链。核心观点是:REPL 驱动的开发极大降低了提交 PR 的心理门槛和实际成本,让开发者更愿意直接修 bug 而非只报 issue。文中附有完整代码示例与前后对比,是一篇具体、有实操细节的个人技术分享。
评论点赞收藏891 天前

Clojure:使用 clj-kondo 对 Datalog 查询进行静态检查

封装了底层 Datalog 查询函数的自定义 q 如何获得 clj-kondo 的语法检查支持。只需在.clj-kondo/config.edn 中添加:lint-as 配置,将自定义 q 映射到 datascript.core/q,clj-kondo 就能按已知实现自动检查 Datalog 查询语法。文章以包装 datalevin.core/q 为例,附完整代码和项目文件结构,三步完成配置,即改即用。
评论点赞收藏956 天前

Clojure 实战:在 Ring 和 Jetty 中使用虚拟线程

一篇来自 Clojure 开发者的实操分享,教你如何用 Java 虚拟线程让 Ring + Jetty 服务性能飙升 2.6 倍。作者给出了完整可复现的配置代码——只需将 Jetty 线程池替换为虚拟线程执行器,无需手动调优线程池大小,即可在 IO 密集型场景下获得接近最优的硬件利用率。文章包含了 Java 19+ 预览特性启用方式、普通固定线程池与虚拟线程池的 wrk 压测对比(842 req/s → 2218 req/s),还解释了性能提升的本质原因:虚拟线程池的无限线程能力消除了传统线程池的手动调优痛点。附完整源码仓库链接。
评论点赞收藏1065 天前

Clojure 实战:在 Ring 和 http-kit 中使用虚拟线程

Java 虚拟线程正式到来后,作者在 Clojure 的 http-kit 服务器上实测了性能提升:配置虚拟线程后,在 50ms 模拟 IO 延迟场景下,吞吐量从 928 req/s 提升到 2208 req/s(2.3 倍)。文章给出了完整的 Ring 路由 + http-kit 配置代码和 wrk 压测命令。更可贵的是,作者最初发布时曾报告 29 倍提升,随后在 Reddit 社区指出 http-kit 默认线程仅为 4 的配置问题后,诚实更新了数据和结论。最终结论并非虚拟线程本身更快,而是无需再手动调优线程池大小——在 IO 密集场景下,虚拟线程池可以自动达到接近最优的硬件利用率,省去了针对不同机器反复调优的麻烦。
评论点赞收藏1066 天前

使用Fennel编写PICO-8游戏

核心看点:一篇教你如何用类Lisp语言Fennel替代Lua编写PICO-8游戏的技术教程,附完整工程配置方案。作者刚用此方案完成了一场Game Jam游戏(Staglight),从实际踩坑出发分享经验。背景是PICO-8以128x128分辨率、32k容量、8K token等硬性约束著称,适合限制游戏范围、提高完赛率。Lua虽好但缺少Lisp的括号编辑和宏系统,Fennel则兼具Lua性能与Lisp表达力。文章依次给出项目目录结构、symlink软链接到PICO-8 carts文件夹的方法、以及Makefile中利用Fennel提前编译(AOT)把.fnl转成.lua并去除模块return语句的完整脚本。对想用PICO-8做Game Jam但又习惯Lisp语法的开发者,这是一份可直接fork使用的配置模板,信息密度高、可复用性强。
评论点赞收藏1073 天前

Clojure 中使用 JDBC 实现 SQLite 应用自定义 SQL 函数

本文解决一个具体技术痛点:在 Clojure 中利用 SQLite 的 Application-Defined SQL Functions,通过 JDBC 扩展 SQLite,无需写 C 也不用为不同操作系统管理预编译二进制。由于 Clojure 的 proxy 无法访问 org.sqlite.Function 的 protected 方法,作者使用 gen-class 宏创建命名类并暴露继承方法,再通过 defn 覆写 xFunc 实现自定义逻辑。给出完整可跑步骤:gen-class 配置、regex_capture 函数实现、deps.edn 路径与编译设置,以及 REPL 启动时自动编译的 dev alias 技巧。附完整项目代码链接,并预告后续将分享更便捷的辅助宏。属于有真实踩坑经验和一手代码的技术笔记,信息密度高,针对性极强。
评论点赞收藏1127 天前

在 macOS 上从源码编译 SQLite

一篇极简的 macOS 上编译 SQLite 的操作笔记。起因是作者为 Clojure 尝试 Project Panama 项目,需要自己编译 SQLite C 源码。步骤仅三步:下载 amalgamation 压缩包、解压、用 gcc 连带 shell.c 和 sqlite3.c 编译出可执行文件。内容可靠、无错误,但几乎没有展开——没有解释各个编译参数的作用、没有对比 Homebrew 等替代方案、没有踩坑记录或调试经验。对于熟悉 C 编译的读者而言信息密度极低,属于个人备忘级别,缺乏可讨论的切入点和可复用的深度知识。
评论点赞收藏1134 天前

使用 Postal 和 Gmail SMTP 在 Clojure 中发送邮件

用 Gmail SMTP 替代 SendGrid 等付费服务在 Clojure 应用里发邮件,成本极低——Google Workspace 单账号月费不到 $5,每天可发 10000 封。本文是一份亲身实践后的完整配置指南:从开启两步验证、生成应用专用密码开始,到 postal 库的 send-message 函数调用,给出可直接运行的 Clojure 代码示例。作者坦率指出了 Gmail SMTP 的硬伤——无法改写发件人地址(始终显示实际发件邮箱),这让它不适合需要冒充发件人的场景。还对比了 SendGrid 月费 $89.95/20 万封的定价,以及 Amazon SES 作为替代方案。没有废话,没有 SEO 注水,是一个有真实踩坑经验的工程笔记。
评论点赞收藏1524 天前

登录芦苇

登录后关注作者、收藏内容和参与讨论。