当前位置: 首页 > news >正文

listmonk数据库查询重写:提升性能的高级技巧

listmonk数据库查询重写:提升性能的高级技巧

【免费下载链接】listmonkHigh performance, self-hosted, newsletter and mailing list manager with a modern dashboard. Single binary app.项目地址: https://gitcode.com/GitHub_Trending/li/listmonk

你是否遇到过发送大量邮件时系统卡顿?或者查询订阅者数据需要等待很久?本文将分享几个实用的数据库查询优化技巧,帮助你显著提升listmonk的性能表现。读完本文后,你将能够:识别低效查询、添加适当索引、重写复杂SQL语句、利用缓存机制,以及监控查询性能。

识别性能瓶颈

在优化查询之前,首先需要找到性能瓶颈。listmonk使用PostgreSQL数据库,我们可以通过分析慢查询日志来识别问题。查看项目中的数据库查询定义文件queries.sql,特别是包含复杂JOIN和子查询的语句。

例如,在queries.sql的第306-324行,有一个用于查询订阅者的复杂SQL:

-- name: query-subscribers -- raw: true -- Unprepared statement for issuring arbitrary WHERE conditions for -- searching subscribers. While the results are sliced using offset+limit, -- there's a COUNT() OVER() that still returns the total result count -- for pagination in the frontend, albeit being a field that'll repeat -- with every resultant row. SELECT subscribers.* FROM subscribers LEFT JOIN subscriber_lists ON ( -- Optional list filtering. (CASE WHEN CARDINALITY($1::INT[]) > 0 THEN true ELSE false END) AND subscriber_lists.subscriber_id = subscribers.id AND ($2 = '' OR subscriber_lists.status = $2::subscription_status) ) WHERE (CARDINALITY($1) = 0 OR subscriber_lists.list_id = ANY($1::INT[])) AND (CASE WHEN $3 != '' THEN name ~* $3 OR email ~* $3 ELSE TRUE END) AND %query% ORDER BY %order% OFFSET $4 LIMIT (CASE WHEN $5 < 1 THEN NULL ELSE $5 END);

这个查询在处理大量数据时可能会变慢,特别是当%query%部分包含复杂条件时。

优化索引策略

合理的索引是提升查询性能的关键。查看数据库模式定义文件schema.sql,可以发现listmonk已经定义了一些索引,但可能还有优化空间。

例如,在schema.sql的第29-33行,为subscribers表定义了多个索引:

DROP INDEX IF EXISTS idx_subs_email; CREATE UNIQUE INDEX idx_subs_email ON subscribers(LOWER(email)); DROP INDEX IF EXISTS idx_subs_status; CREATE INDEX idx_subs_status ON subscribers(status); DROP INDEX IF EXISTS idx_subs_id_status; CREATE INDEX idx_subs_id_status ON subscribers(id, status); DROP INDEX IF EXISTS idx_subs_created_at; CREATE INDEX idx_subs_created_at ON subscribers(created_at); DROP INDEX IF EXISTS idx_subs_updated_at; CREATE INDEX idx_subs_updated_at ON subscribers(updated_at);

如果你的查询经常根据订阅状态和创建日期过滤,可以考虑创建一个组合索引:

CREATE INDEX idx_subs_status_created_at ON subscribers(status, created_at);

重写复杂查询

复杂的子查询和JOIN操作往往是性能问题的根源。让我们以queries.sql中第306行的query-subscribers查询为例,看看如何优化。

原查询使用了LEFT JOIN和多个CASE语句,这在数据量大时可能效率低下。我们可以尝试将其重写为使用 EXISTS 子句,减少不必要的数据连接:

-- 优化后的查询示例 SELECT s.* FROM subscribers s WHERE (CARDINALITY($1) = 0 OR EXISTS ( SELECT 1 FROM subscriber_lists sl WHERE sl.subscriber_id = s.id AND sl.list_id = ANY($1::INT[]) AND ($2 = '' OR sl.status = $2::subscription_status) )) AND (CASE WHEN $3 != '' THEN s.name ~* $3 OR s.email ~* $3 ELSE TRUE END) AND %query% ORDER BY %order% OFFSET $4 LIMIT (CASE WHEN $5 < 1 THEN NULL ELSE $5 END);

这种方式可以避免不必要的行连接,特别是当subscriber_lists表很大时。

利用缓存机制

listmonk已经实现了一些缓存机制来提高性能。在internal/core/subscribers.go的第548-558行,可以看到系统尝试从缓存中获取订阅者数量:

// 如果没有查询条件,尝试从缓存获取 if queryExp == "" { _ = c.refreshCache(matListSubStats, false) total := 0 if err := c.q.QuerySubscribersCountAll.Get(&total, pq.Array(listIDs), subStatus); err != nil { return 0, echo.NewHTTPError(http.StatusInternalServerError, c.i18n.Ts("globals.messages.errorFetching", "name", "{globals.terms.subscribers}", "error", pqErrMsg(err))) } return total, nil }

你可以通过修改配置文件启用更多缓存功能,或调整缓存刷新频率来平衡性能和数据实时性。

监控查询性能

优化完成后,需要持续监控查询性能。你可以使用PostgreSQL的内置工具如pg_stat_statements来跟踪慢查询。以下是一个示例查询,用于找出执行时间最长的查询:

SELECT queryid, query, total_time, calls FROM pg_stat_statements ORDER BY total_time DESC LIMIT 10;

定期检查这些指标,可以帮助你及时发现新的性能问题。

总结

通过识别性能瓶颈、优化索引、重写复杂查询、利用缓存和持续监控,你可以显著提升listmonk的数据库性能。这些技巧不仅适用于listmonk,也可应用于其他PostgreSQL数据库项目。

主要优化点回顾:

  1. 分析queries.sql中的复杂查询
  2. 优化schema.sql中的索引策略
  3. 重写子查询和JOIN操作
  4. 利用internal/core/subscribers.go中的缓存机制
  5. 定期监控查询性能

通过这些方法,即使在处理大量订阅者和邮件 campaign 时,你的 listmonk 系统也能保持流畅运行。

【免费下载链接】listmonkHigh performance, self-hosted, newsletter and mailing list manager with a modern dashboard. Single binary app.项目地址: https://gitcode.com/GitHub_Trending/li/listmonk

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

http://www.rkmt.cn/news/1400779.html

相关文章:

  • 临一云川浮空风电非线性稳定的五行求解模型
  • 别再死记硬背了!用Arduino和面包板,5分钟搞懂三极管开关电路(附代码)
  • 物理信息神经网络梯度优化与二阶方法实践
  • 绵阳市黄金回收 白银回收 铂金回收 彩金回收全攻略:五家靠谱门店横向评测,附避坑要点 - 前途无量YY
  • Go语言零内存分配PII擦除:从正则表达式到高性能状态机实战
  • listmonk与客户反馈闭环:从收集到改进的流程
  • Granite Guardian 3.0-2B完全指南:从开源贡献到问题解决的终极教程 [特殊字符]
  • Vivado工程文件太大?三步教你用reset_project和Tcl脚本给源码“瘦身”,轻松备份到Git
  • 绵竹市黄金回收 白银回收 铂金回收 彩金回收全攻略:五家靠谱门店横向评测,附避坑要点 - 前途无量YY
  • 终极Office文件预览指南:Windows空格键快速查看文档
  • 别急着删资料!华硕ASUS电脑重装Win7前,用DiskGenius这样备份和转换分区最安全
  • 明光市黄金回收 白银回收 铂金回收 彩金回收全攻略:五家靠谱门店横向评测,附避坑要点 - 前途无量YY
  • 告别台式机?实测用笔记本+FPGA开发板搭建轻量级PCIE验证环境(避坑指南)
  • 湖北建筑工程资质代办选哪家?资深从业者拆解靠谱选项 - 奔跑123
  • G4-MeroMero-31B vs 原版Gemma4:创意任务性能对比分析
  • Cat-Catch智能资源嗅探实战:构建高效网页媒体下载工作流
  • OpenClaw数据采集实战:从技术原理到商业变现的完整指南
  • 3分钟让Figma说中文!设计师必备的界面汉化神器
  • Unity 2022.3 + ShaderGraph 实战:5分钟搞定刮刮乐游戏,从RenderTexture到UI交互全流程
  • 手把手教你用Verilog/SystemVerilog搭建一个可配置的8x8脉动阵列(附完整测试平台)
  • 抖音无水印视频下载终极指南:如何免费批量保存高清内容
  • VMware Horizon Client死活装不上?先别重装系统,试试从这3个系统级依赖入手
  • 5分钟掌握MelonLoader:Unity游戏模组加载器的革命性解决方案
  • 从MOS管到寄存器:一张图看懂STM32 GPIO硬件电路,理解八种工作模式的本质
  • 百度网盘高速下载终极指南:用Python脚本突破限速瓶颈
  • 安达市黄金回收 白银回收 铂金回收 彩金回收全攻略:五家靠谱门店横向评测,附避坑要点 - 前途无量YY
  • 2026年汉中市本地上门黄金回收门店指南 彩金+铂金+金条+白银回收门店联系方式推荐 - 大熊猫898989
  • 省钱的基本原理:十种有效策略
  • 如何用Zotero Style插件打造高效文献管理体验:3大核心优势与5分钟上手教程
  • 【Word提效 No.024】一句话搞定批量替换特殊字符