第一次处理比较大的数据,本以为十个很简单的任务,结果花了两天半才算做完,学到不少经验,记一下吧。
任务是给出一个XML文件(gz格式压缩的是45M,我展开之后是450M,不得不说XML的冗余还是蛮多的),里面有240w条数据(每条数据两个字段),用这些数据去ElasticSearch里面查询两属性,最后将一共四个字段导出csv格式。
最后的代码都放在https://github.com/laixintao/test-yorg/tree/master/company200w这里了,数据文件被ignore了。由于是一次性的任务,代码也就执行一次,所以写的比较随意,还被同事吐槽了……
我的思路是,首先解析XML文件,把数据存放到mongodb里面,然后根据mongo里面的数据一条一条去ES查询,最后将数据导出。
现在看来,决定中间用mongo来存储数据而不是直接用从文件解析出来的数据是无比正确的。这么大的文件不能保证程序一次就跑通,而使用文件就不能按数据查询,不能索引,无结构化,带来不必要的麻烦。数据放到mongo之后,后面的每一部都可以看作是针对mongo中的结构化数据进行的,可以随意添加字段,可以针对性的只修改有问题的数据,可以使用多线程/多进程处理。
我以前一直不是很理解数据库的索引,可能因为接触的数据都不大,也没花心思去理解。这次解析XML插入到mongo的时候,等程序跑了一段时间之后就变得特别慢。同事提醒我针对update的字段建立一条索引。索引执行完之后,我去,速度立马提升了10倍(目测)。后来我看到了索引的好处,每次用到数据库之前都是先进行索引再用,后来导致插入的时候变得很慢。建索引也是个学问,太多太少都不好(这段话会不会暴露我跟SB一样)。另外我发现了一个特经典的视频,1分钟理解btree。看完就知道为什么索引能这么快的提升速度了。
在解析XML的时候遇到一个坑,我用的是pyhton标准库的xml.sax,因为etree好像是将文件一下子读到内存里的,可能不太适合大型XML文件,相比之下xml.sax是从一个标签的开始读到结束,处理一次,在处理下一个。具体的代码可以看这一段https://github.com/laixintao/test-yorg/blob/master/company200w/xml2mongo.py,但是characters方法好像并不是只用一次,从处理的结果来看,遇到一些特殊字符(比如html的&)就给断成几段文字了,后来发现了我又将他们连起来的。
导出就没什么好说的,用csv的标准库非常简单。
另外,现在看来自己写程序的时候急急忙忙,生怕没时间运行,但其实要是多花一个小时去写,说不定执行能少十个小时。找找哪些操作占用的时间多,想办法优化一下。比如去ES查数据,有条查询是可以一次查多条的(比如100),这样就可以有效减少网络请求的次数。
总结
- 处理数据库中的数据,而不是文件
- 数据很大的话及时建立索引
- 减少IO的次数
- 打印处理的进度,不要太多(打印也占用资源的)
- 尽量先测试,再运行