当前位置: 首页 > 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.jsqmd.com/news/895660/

相关文章:

  • 汨罗市黄金回收 白银回收 铂金回收 彩金回收全攻略:五家靠谱门店横向评测,附避坑要点 - 前途无量YY
  • 3分钟让Figma说中文!设计师必备的界面汉化神器
  • 太阳膜性价比高的品牌有哪些?龙膜广州番禺臻选店值得选 - mypinpai
  • 语义业务配置锁 Token 全面剖析:守住 SAP S/4HANA 云端配置一致性的最后一道闸门
  • 为Hermes Agent配置自定义Provider接入Taotoken模型服务
  • 别再死记硬背了!用Arduino和面包板,5分钟搞懂三极管开关电路(附代码)
  • 汉中市黄金回收 白银回收 铂金回收 彩金回收全攻略:五家靠谱门店横向评测,附避坑要点 - 前途无量YY
  • listmonk容器健康检查HTTP状态码:自定义响应
  • 3分钟让Figma说中文:设计师必备的界面汉化完全指南
  • 零编程文本分析神器:KH Coder完整入门指南
  • VMware Workstation Pro 17终极免费激活指南:5步获取完整许可证密钥
  • 绵阳市黄金回收 白银回收 铂金回收 彩金回收全攻略:五家靠谱门店横向评测,附避坑要点 - 前途无量YY
  • 给技术美术的Niagara入门:对比Cascade,解锁自定义粒子模块的正确姿势
  • Agent 一接发布流水线就开始选错制品版本:从 Artifact Promotion 到 Deployment Lock 的工程实战
  • CH341驱动安装踩坑全记录:串口、I2C/SPI驱动为啥要装两个?Win10/Win11避坑指南
  • PyQt-Fluent-Widgets终极指南:打造现代化Python桌面应用的最佳实践
  • Vivado工程文件太大?三步教你用reset_project和Tcl脚本给源码“瘦身”,轻松备份到Git
  • 嵌入式系统软件复位实现与看门狗定时器应用
  • DS4Windows:终极游戏手柄兼容解决方案,让PS4/PS5手柄在PC上完美工作
  • Docker--镜像分层与镜像摘要
  • Granite Guardian 3.0-2B完全指南:从开源贡献到问题解决的终极教程 [特殊字符]
  • 绵竹市黄金回收 白银回收 铂金回收 彩金回收全攻略:五家靠谱门店横向评测,附避坑要点 - 前途无量YY
  • 东兴市黄金回收 白银回收 铂金回收 彩金回收全攻略:五家靠谱门店横向评测,附避坑要点 - 前途无量YY
  • 终极鼠标加速指南:7种曲线类型帮你精准控制光标速度
  • 告别台式机?实测用笔记本+FPGA开发板搭建轻量级PCIE验证环境(避坑指南)
  • 东营市黄金回收 白银回收 铂金回收 彩金回收全攻略:五家靠谱门店横向评测,附避坑要点 - 前途无量YY
  • 如何快速上手Microsoft Harrier-OSS-v1-0.6B:5分钟完成文本嵌入部署
  • AI对话防丢失:从IndexedDB到服务端同步的完整解决方案
  • 湖北建筑工程资质代办选哪家?资深从业者拆解靠谱选项 - 奔跑123
  • 从 GPT-5 到 Claude 4:API 迁移实战指南