挖井

类库大魔王的挖井日记

挖一口属于自己的井


某日志查看程序开发小结

这几天写了个查看日志的程序,日志是公司里产品项目用户log4cxx生成的,用于定位问题。以前看到过其他同事自己开发的这类程序,但都只限于他们自己使用,等他们离职了,就基本失传了,尽管他们是交接给其他同事了。

曾经想动过这个念头,想自己写一个,主要的理念是,定位问题的时候,往往会在几个日志文件中搜索固定的一些关键字,比较套路,我希望能把这些套路放在程序中,以后再查看新的日志文件时先把这些套路跑一遍,可以快速地查看一些常见的case。后来么,还是拖延症,直到前几天,第一次的提交记录是在6月13日。

这个程序的功能还是很简单的,就是把几个纯文本格式的日志读出来,然后用户可以在程序界面上输入关键字,程序实时地根据关键词在列表中显示匹配的,我的做法是把这些文件都按行读出来,解析成多个字段,存储到sqlite数据库里,查询关键字就用SQL语句从sqlite查。

最早完成基本的数据转存到关键字过滤显示,发现性能比较低下,6个日志文件,最大每个10MB,总共在20万-30万条记录,转存就总共要花10秒多,于是开始了优化的历程。

一开始,我怀疑是每行数据解析时我用了比较复杂正则表达式匹配,于是我自以为地用了一个比较简单的正则表达式,简单测试下来发现确实有提升,但极其微小的提升,大概是少了1秒钟。然后我再接再厉,换用了手写的字符串解析,又提升了1-2秒。

后来我通过直接写入硬编码的字符串到数据库,发现耗时的操作在插入记录到sqlite这个过程,于是上网找了点优化sqlite插入性能的文章看了看,效果还是比较明显的。

  • 要用transaction就不用多说了;
  • 建表了之后不要着急建索引,在插入记录后再建索引,比先建索引再插入记录,可以快2-3秒;
  • 加一句”PRAGMA synchronous = OFF”,写磁盘时用缓冲,这是常用的磁盘IO策略;
  • 加一句”PRAGMA journal_mode = MEMORY”,写sqlite数据库时,默认会生成临时的journal文件,用完又被删掉,这句希望sqlite把这个文件放在内存里,不写磁盘又能再快点;

这几步下来,总耗时降到5秒!粗略算来每秒插入5万条记录,虽然跟网上的数据比还是慢了不少,但跟自己以前的比已经有很大提升了。

另外,可以看到sqlite本身针对性能优化的措施,有很多是在减少磁盘IO上,所以我还给程序加了一个选项,可以指定程序使用的临时目录路径,sqlite数据库会存放在临时目录中,我公司的一台Windows机器装有一块普通机械硬盘和一块Intel SSD,当初把系统装在机械硬盘上,把其他数据放在SSD上,程序默认使用系统临时目录则是在系统盘上,所有通过命令行选项把临时目录切换到SSD上,可以明显感觉到sqlite读写变快了。

后来又发现一个问题,我读日志文件时,用Qt的QTextStream或QIODevice的readLine()方法,一来它不认单个的\r作为换行符,二来似乎性能仍有提高的空间。于是换成了file mapping,直接从内存地址中挨个查找\r和\n来读取一行。性能又有些微提升。

然后是查询的性能,由于用了Qt的Model-View架构,在Model中先用SELECT COUNT(*)查出所有符合要求的记录数,在起始阶段Model类的data()方法中统一返回空,并立马去查数据库,只查附近的200条,这样就不用每次data()为空时都查,如果view从上往下滚动时,只要依次增大查询的起始偏移即可,但如果是从下往上滚,这样逐条减小起始偏移效率就比较低下了。我的做法是将起始偏移以0x40个递增或递减,至于为什么是0x40个,原本我随便想了个40这个数字,后来想50,64这些数字貌似都可以,只是为了计算方便,只要将0x3F取反再与,就能得到对齐后该递减的数了。

最后,提一下文件查找的实现。日志中有一个字段是哪个源代码文件第几行打印了这条日志,我希望能双击这个字段时,程序可以找到这个源代码文件并打开然后跳转到那一行显示。源代码显示,用的是scintilla控件,它有官方的Qt binding,很好用。关键问题是如何快速地找到那个源代码文件,即使指定了源代码的根目录,我也不想自己手写代码递归遍历目录层次结构,索引数量庞大的文件,太低效了!于是我用了比较省事的做法:

  • Windows下,NTFS文件系统,想来我司员工应该都是用这个文件系统的,有个叫Everything的神器,通过遍历uSN记录来快速索引磁盘上的所有文件。Everything还提供了SDK,可以通过IPC来让其他程序调用Everything进行搜索。
  • Mac OS X下,自带的Spotlight就索引了很多东西,可以用mdfind命令查找指定名字的文件所在的完整路径,而苹果也提供了MDQuery等一系列的API,可供第三方应用程序搜索Spotlight索引的内容。

到目前为止,总共大约花了4天时间,这个程序已经基本可用了,期间的优化和方案选择上,都是值得回味的。

本文地址:

https://minidump.info/blog/2016/06/summary-of-cjlv-development/

上一篇

纯净DNS解析

DNS污染主要是两种,一是丢包,二是抢答,两种方式基本上无规律出现。做纯净DNS解析基本上就是为了解决这两个问题,现在主流的方法大体有以下几种: 换DNS服务器。一般ISP会自动分配一个或两个DNS服务器,这种服务器对于国内或ISP内网的CDN特别友好,但对国外的比如Apple的CDN就抓瞎...…

gfw 全文阅读
下一篇

让Qt的sqlite插件支持REGEXP查询

仍然是日志查看程序,突然想要加个按正则表达式的查询,记得sqlite是支持REGEXP的,不过sqlite官方文档上说了,需要程序自己提供一个进行正则匹配的函数,然后调用sqlite3_create_function来实现。在Qt中实现需要以下几步: 获取sqlite3 *; 调用sqli...…

Qt 全文阅读