拥抱 PostgreSQL 支持 UI 配置化

1 月 26 日
 MagicCoder

前言

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

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

抛弃 SQLite

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

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

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

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

UI 配置可视化使用

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

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

新增 wiki 文档

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

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

访问明细模块优化

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

概况模块优化

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

项目地址

写在最后

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

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

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

3819 次点击
所在节点    程序员
32 条回复
yjhatfdu2
1 月 26 日
为什么不用 duckdb 呢?和 sqlite 一样是进程内数据库,但是是列存分析型数据库性能超强,存储效率也会比 pg 和 sqlite 高很多,存储体积小,也基本支持 postgresql 的语法,估计时间可以进一步缩短好几倍。
dog82
1 月 26 日
为啥把日志写到 DB 呢?
yjhatfdu2
1 月 26 日
@dog82 估计是便于分析聚合吧,不过存 pg 也确实是效率有点低了,我做的话可能会考虑存 parquet 文件用 duckdb 分析,这样文件可以存文件系统也可以存 s3 ,比较灵活,体积也非常小,10G 文件做到分钟级解析我也有信心
MagicCoder
1 月 26 日
@yjhatfdu2 好高级的词汇😂,我研究下 duckdb 之前没听过
concernedz
1 月 26 日
日志不是用 mongodb 么
yjhatfdu2
1 月 26 日
试了下,使用 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
1 月 26 日
然后使用 create table access_log as select * from 'parquet_out/*.parquet'; 创建 duckdb 的表并导入 parquet 的所有数据,耗时 20 秒,之后查询可以再快一倍,最终 duckdb 存储 7000 多万条记录占用硬盘 1.9G ,原始 access.log 一共 11G
MagicCoder
1 月 26 日
@yjhatfdu2 卧槽 这性能可以
yjhatfdu2
1 月 26 日
我看了下,你这里用了大量的维度表、预聚合、分表来提高性能,其实用 duckdb 性能足够,单机 10 亿条都用不着干这些事。可以极大的简化后端,减少存储占用和提高解析和写入速度
MagicCoder
1 月 26 日
@yjhatfdu2 对的,我后端做了大量的优化工作提升性能,感觉 duckdb 确实可行😂
MagicCoder
1 月 26 日
@yjhatfdu2 之前朋友还给我推荐过 clickhouse ,我就是看他太耗费资源了,我这个场景还用不到
yjhatfdu2
1 月 26 日
@MagicCoder clickhouse 性能上也是可以的,但是部署维护复杂,稍微老点的版本对于 update/delete 效率非常低,资源消耗也是很高。duckdb 适合直接平替 sqlite ,单机模式下很适合。当然你这里如果需要初始化也做的非常快,还是需要做一些工程优化的,如果是 duckdb 直接使用 golang 的驱动,使用 appender 接口进行写入,这个场景也就 20-30w 行一秒,我是尝试了直接使用 go-parquet ,每个线程独立直接写入 parquet 文件,最后再用 duckdb 执行 sql 直接导入,后续再使用 go 的 database/sql 接口写入增量数据,这样才能做到初始化的极速、后续处理的方便。
MagicCoder
1 月 26 日
@yjhatfdu2 懂了,非常感谢,我研究下你说的这个方案
XyIsMy
1 月 26 日
@MagicCoder duckdb ,可以直接在 postgresql 上面装一个扩展,开启扩展,会自动转换成 duckdb ,也很方便
MagicCoder
1 月 26 日
@XyIsMy 哦豁,这么高级,那就方便很多了
goodryb
1 月 26 日
OP 这下又有活可以干了,等你下个稳定版出来了我试试
Hermitist
1 月 26 日
我也在等 OP 的更新, 冒昧问下, 用你的项目在一个暂时免费, 但以后商业化的软件有什么问题吗?
MagicCoder
1 月 26 日
@Hermitist 没事,随便用
MagicCoder
1 月 26 日
@goodryb 行 别忘了🤣
yjhatfdu2
1 月 26 日
@MagicCoder duckdb 的 pg_duckdb 插件并不是很适合。首先,你还是需要 pg ,pg 需要单独部署,不能集成到应用里面,作为轻量化的,必然增加了部署的复杂度。第二,pg 需要部署 pg_duckdb 插件需要自行编译或者使用特定的发行版,还是有一定复杂度的。第三,pg_duckdb 主要是方便使用 pg 访问、查询外部 parquet 等文件,或者联合 pg 本地表进行分析查询。但是还是需要走 pg 的协议、parser 等,也没法直接写入 duckdb 自己的库,对于现在这个场景降低了性能,提高了复杂度。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://study.congcong.us/t/1188321

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX