V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
MagicCoder
V2EX  ›  程序员

拥抱 PostgreSQL 支持 UI 配置化

  •  
  •   MagicCoder · 10 小时 38 分钟前 · 2000 次点击

    前言

    前阵子写的日志分析工具NginxPulse,自开源以来,已过去 2 周时间,目前 GitHub 已收获 1.5k 的 star 。收到了不少用户的反馈建议,花了点时间将这些问题都处理了下。

    本文就跟大家分享下新版本都解决了哪些问题,优化了哪些内容,欢迎各位感兴趣的开发者阅读本文。

    抛弃 SQLite

    有不少用户反馈说日志文件很大的时候( 10G+),解析速度非常慢,需要解析好几个小时,解析完成之后数据看板的查询也比较慢(接口响应在 5 秒左右)。

    于是,我重写了日志解析策略(解析阶段不做 IP 归属地查询,仅入库其他数据,将日志中 IP 记录起来),日志解析完毕后,将记录的 IP 做去重处理,随后去做归属地的查询处理(优先本地的 ip2region 库,远程的 API 调用查询做兜底),最后将解析到的归属地回填至对应的数据库表中,这样一套下来就可以大大提升日志的解析速度。

    数据库的数据量大了之后,SQLite 的表现就有点差强人意了,请教了一些后端朋友,他们给了我一些方案,结合我自身的实际场景后,最后选定了 PostgreSQL 作为新的数据库选型。

    这套方案落地后,用户群的好兄弟说:他原先需要解析 1 个小时的日志,新版只需要 10 多分钟。

    6c1c8781ddb810d57c9f508fdaf47025

    UI 配置可视化使用

    有一部分用户反馈说他非专业人士,这些晦涩的配置对他来说使用门槛太高了,希望能有一个 UI 配置页面,他只需要点一点、敲敲键盘,就能完成这些配置。

    我将整个配置流程做成了 4 步,同时也准备一个[演示视频](NginxPulse 支持 UI 配置化了) - https://www.bilibili.com/video/BV1hqzyBVEU9:

    • 配置站点
    • 配置数据库
    • 配置运行参数
    • 确认最终配置

    image-20260125235847464

    新增 wiki 文档

    因为配置过于庞大,仓库主页浏览 README.md 比较费劲,希望能整理一份 wiki 文档发上去。

    花了点时间,简化了下 README ,整理了一份: https://github.com/likaia/nginxpulse/wiki

    image-20260126000555725

    访问明细模块优化

    有部分用户反馈说希望增加更多的筛选条件以及导出 Excel 功能,现在它来了:

    image-20260126001010068

    概况模块优化

    概况页面的日期筛选之前放在趋势分析卡片的上方,但是他的切换影响的维度还包含了指标,于是我就调整了下它的位置,新版如下图所示:

    image-20260126001325265

    项目地址

    写在最后

    至此,文章就分享完毕了。

    我是神奇的程序员,一位前端开发工程师。

    如果你对我感兴趣,请移步我的个人网站,进一步了解。

    21 条回复    2026-01-26 20:32:55 +08:00
    yjhatfdu2
        1
    yjhatfdu2  
       9 小时 29 分钟前   ❤️ 1
    为什么不用 duckdb 呢?和 sqlite 一样是进程内数据库,但是是列存分析型数据库性能超强,存储效率也会比 pg 和 sqlite 高很多,存储体积小,也基本支持 postgresql 的语法,估计时间可以进一步缩短好几倍。
    dog82
        2
    dog82  
       9 小时 21 分钟前
    为啥把日志写到 DB 呢?
    yjhatfdu2
        3
    yjhatfdu2  
       9 小时 5 分钟前   ❤️ 1
    @dog82 估计是便于分析聚合吧,不过存 pg 也确实是效率有点低了,我做的话可能会考虑存 parquet 文件用 duckdb 分析,这样文件可以存文件系统也可以存 s3 ,比较灵活,体积也非常小,10G 文件做到分钟级解析我也有信心
    MagicCoder
        4
    MagicCoder  
    OP
       8 小时 59 分钟前
    @yjhatfdu2 好高级的词汇😂,我研究下 duckdb 之前没听过
    concernedz
        5
    concernedz  
       8 小时 46 分钟前
    日志不是用 mongodb 么
    yjhatfdu2
        6
    yjhatfdu2  
       6 小时 55 分钟前
    试了下,使用 golang 并行解析 11G 的 nginx accesslog ,同样适用 ip2region 解析地理位置,解析 useragent 字段,8 个线程,写入 parquet 文件,在我 m1max 老机器上可以在 40 秒左右完成。然后使用 duckdb 直接查询,7000 多万条数据,根据状态码 group by count 聚合大概 0.11 秒,还是非常适合这个场景的,整个 dashboard 尤其是只分析时间段的,应该秒级全出。
    select count(*),status from 'parquet_out/*.parquet' group by status;
    ┌──────────────┬────────┐
    │ count_star() │ status │
    │ int64 │ int32 │
    ├──────────────┼────────┤
    │ 16455120 │ 200 │
    │ 420 │ 413 │
    │ 58349130 │ 404 │
    │ 261330 │ 400 │
    │ 8310 │ 500 │
    │ 60 │ 408 │
    │ 3540 │ 501 │
    │ 3537120 │ 405 │
    │ 90 │ 403 │
    │ 7230 │ 206 │
    │ 15630 │ 304 │
    │ 4980 │ 499 │
    ├──────────────┴────────┤
    │ 12 rows 2 columns │
    └───────────────────────┘
    Run Time (s): real 0.112 user 0.937740 sys 0.047321
    yjhatfdu2
        7
    yjhatfdu2  
       6 小时 46 分钟前
    然后使用 create table access_log as select * from 'parquet_out/*.parquet'; 创建 duckdb 的表并导入 parquet 的所有数据,耗时 20 秒,之后查询可以再快一倍,最终 duckdb 存储 7000 多万条记录占用硬盘 1.9G ,原始 access.log 一共 11G
    MagicCoder
        8
    MagicCoder  
    OP
       6 小时 44 分钟前
    @yjhatfdu2 卧槽 这性能可以
    yjhatfdu2
        9
    yjhatfdu2  
       6 小时 34 分钟前
    我看了下,你这里用了大量的维度表、预聚合、分表来提高性能,其实用 duckdb 性能足够,单机 10 亿条都用不着干这些事。可以极大的简化后端,减少存储占用和提高解析和写入速度
    MagicCoder
        10
    MagicCoder  
    OP
       6 小时 29 分钟前
    @yjhatfdu2 对的,我后端做了大量的优化工作提升性能,感觉 duckdb 确实可行😂
    MagicCoder
        11
    MagicCoder  
    OP
       6 小时 28 分钟前
    @yjhatfdu2 之前朋友还给我推荐过 clickhouse ,我就是看他太耗费资源了,我这个场景还用不到
    yjhatfdu2
        12
    yjhatfdu2  
       6 小时 13 分钟前
    @MagicCoder clickhouse 性能上也是可以的,但是部署维护复杂,稍微老点的版本对于 update/delete 效率非常低,资源消耗也是很高。duckdb 适合直接平替 sqlite ,单机模式下很适合。当然你这里如果需要初始化也做的非常快,还是需要做一些工程优化的,如果是 duckdb 直接使用 golang 的驱动,使用 appender 接口进行写入,这个场景也就 20-30w 行一秒,我是尝试了直接使用 go-parquet ,每个线程独立直接写入 parquet 文件,最后再用 duckdb 执行 sql 直接导入,后续再使用 go 的 database/sql 接口写入增量数据,这样才能做到初始化的极速、后续处理的方便。
    MagicCoder
        13
    MagicCoder  
    OP
       5 小时 34 分钟前
    @yjhatfdu2 懂了,非常感谢,我研究下你说的这个方案
    XyIsMy
        14
    XyIsMy  
       5 小时 21 分钟前
    @MagicCoder duckdb ,可以直接在 postgresql 上面装一个扩展,开启扩展,会自动转换成 duckdb ,也很方便
    MagicCoder
        15
    MagicCoder  
    OP
       5 小时 15 分钟前
    @XyIsMy 哦豁,这么高级,那就方便很多了
    goodryb
        16
    goodryb  
       4 小时 34 分钟前
    OP 这下又有活可以干了,等你下个稳定版出来了我试试
    Hermitist
        17
    Hermitist  
       3 小时 46 分钟前
    我也在等 OP 的更新, 冒昧问下, 用你的项目在一个暂时免费, 但以后商业化的软件有什么问题吗?
    MagicCoder
        18
    MagicCoder  
    OP
       3 小时 1 分钟前
    @Hermitist 没事,随便用
    MagicCoder
        19
    MagicCoder  
    OP
       2 小时 59 分钟前
    @goodryb 行 别忘了🤣
    yjhatfdu2
        20
    yjhatfdu2  
       2 小时 37 分钟前
    @MagicCoder duckdb 的 pg_duckdb 插件并不是很适合。首先,你还是需要 pg ,pg 需要单独部署,不能集成到应用里面,作为轻量化的,必然增加了部署的复杂度。第二,pg 需要部署 pg_duckdb 插件需要自行编译或者使用特定的发行版,还是有一定复杂度的。第三,pg_duckdb 主要是方便使用 pg 访问、查询外部 parquet 等文件,或者联合 pg 本地表进行分析查询。但是还是需要走 pg 的协议、parser 等,也没法直接写入 duckdb 自己的库,对于现在这个场景降低了性能,提高了复杂度。
    MagicCoder
        21
    MagicCoder  
    OP
       2 分钟前
    @yjhatfdu2 好吧🤣 那看来还是得完整的用 duckdb
    关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   Solana   ·   3113 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 36ms · UTC 12:35 · PVG 20:35 · LAX 04:35 · JFK 07:35
    ♥ Do have faith in what you're doing.