Posted on:
Last modified:
爬虫是一种典型的「读少写多」的应用场景。而且爬虫产生的数据一般是作为离线数据,而不是在线数据。 也就是说这些数据主要是用于离线数据分析,而不是直接供线上用户查询使用。
即使线上需要使用爬虫数据,也会需要经过清洗处理过后再放到在线库。总之,不会直接供线上直接使用。
本文总结了一些经验和踩过坑,希望能对读者有帮助。
如果我们开了一个多线程的爬虫,然后每个线程每爬到一条数据就调一下 db.insert(item)
插入数据,数据一多就是灾难性的。
首先,每个线程持有一个数据库链接对数据库的负载就产生了不小压力。其次,每条数据都去调用数据库,那么每次插入时间都得 加上数据库的往返时间,也就是 2RTT(round trip time)。再者,每次插入的都是不同的数据,可能在磁盘的不同位置,导致 磁盘的写入时间大部分都花在寻道上了,磁盘 IO 时间会大幅提升。最后,如果是 SQL 型的数据库,默认配置下, 可能还会有事务的影响。
正确的做法是——用队列。不同的线程都把数据先发到一个队列中,不管是你用的语言自带的内存里的 Queue,还是 redis list, 或者是 kafka,都可以。然后由另一个线程或者脚本读取这个队列,把数据整理之后,定期或者定量写入数据库。
这样做基本上解决了上面提到的每个问题。只有存储线程持有数据库链接,每一条数据不会在需要 2RTT,摊薄下来可能是 2RTT / 1000, 数据经过整理后,每次可以都插入统一中类型的数据,磁盘不需要总是在寻道。
当然,这种方案也会引入一些新的问题,需要注意解决:
以上这些问题都是使用 MQ 的常见问题了,这里不再展开。
大概有这些选择:
如果你只是简单地「单线程」爬几页数据分析用,那么存个 CSV 或者 JSON 就可以了。如果你开始上多线程,甚至多机了, 既要考虑写入的时候加锁,而且没法分布式写入,单文件存储就不太合适了。
规模再大一点可以考虑 MySQL。虽然 MySQL 是一个 OLTP 数据库,通常意义上来说更适用于线上数据库。但是对于数据量不大的爬虫来说, 比如说总数据量不会超过 100GiB,也已经足够用了。而且 MySQL 可以添加 unique 索引,一定程度上还能帮助解决数据去重的问题。 这里有几点需要注意:
有些人喜欢用 MongoDB 来存储爬虫数据,他们给出的理由也很有吸引力——爬虫数据多是半结构化的,而且数据结构可能经常跟着源网站变, 用 MongoDB 这种 schemaless 的文档数据库再合适不过了。然而我个人非常不推荐用 MongoDB,原因如下:
read_sql
就把数据读出来了数据再多一些,或者并发量再大一些的话,可能单独使用 MySQL 就不合适了。这时候你可以对 MySQL 做定期归档,比如说把添加时间在一个月以上的数据 都按日期写入到 Hive 或者 S3 中,然后删除掉 MySQL 中的数据。这样的做法,其实相当于隐式地把 MySQL 作为了一个消息队列,并起到了缓冲的作用。
再者,MySQL 这种毕竟是行式数据库,如果你的数据数值居多,也可以跳过 MySQL,考虑直接存储到列式数据库中。下游的 Spark,Flink 这些消费端可能更喜欢读取列式数据。
最后一种选项是直接使用 Kafka 这种持久化的消息队列作为存储。DDIA 这本书中提到一个有趣的观点:数据库是日志的积分,日志是数据库的导数。 从某种意义上来说,两者所含有的信息是等价的,可以相互转换。所以直接使用消息队列作为数据存储也未尝不可。
总之,对于爬虫这种场景来说,最重要的特点是「读少写多」,按照这个思路去选择问题不大。除了这里提到的一些数据库,还有 Cassandra,FoundationDB 等一些 数据库没有提到,在特定的场景下也都值得考虑。对于存储的选择也不只是一个技术问题,可能更重要的是你的公司现在有什么,选一个比较合适的用就好了。
爬到的数据往往会出现这样一种情况:一个对象的不同字段分别在两个接口里。这时候,比较直接的 思路是存储到一张表里,先存一部分数据,再从另一个接口里爬到数据之后,update 对应的数据。
这样的思路虽然直接,但是实际使用中会遇到一些问题:
所以,更好的方式是一个 API 就对应一个表,比如一个帖子的数据要从两个 API 爬到,那就分成 post_summary, post_detail 两个表,分别存储,使用的时候再去 join 就好了。
© 2016-2022 Yifei Kong. Powered by ynotes
All contents are under the CC-BY-NC-SA license, if not otherwise specified.
Opinions expressed here are solely my own and do not express the views or opinions of my employer.
友情链接: MySQL 教程站