记一次需求的表结构设计变更

需求描述

爬虫每天会爬取很多公司信息,将这些公司的最新信息保存下来,并记录每个公司信息发生了哪些变化。对比最近爬取的两次数据,如果最新的数据里某个公司没爬取到,则将该公司的状态设为"注销"。
爬虫一天爬一次。

公司信息的格式:


新增字段的概率还是比较大的

表结构设计

最初设计

需要两张表,一张存放公司的最新信息(t_info),一张存放公司的变更信息(t_change_info)

t_info:每个小字段为一列

md5 crawl_time status A1 A2 A3 B1 B2 ...
*** *** *** *** *** *** *** *** ***
这样是最直观的设计,但列很多,达到几十个,而且扩展性极差,只要增加了字段,表结构就得改
t_change_info:
md5 change_time change_info
:----: :----: :----:
*** *** ***

改进设计1

t_info:将每个大字段单独作为一列

md5 crawl_time status A B C ...
*** *** *** *** *** *** ***
这样设计使表的列减少了很多,但如果有大字段的增加,还是得改表结构

改进设计2

上个设计只要增加了大字段就得加列,现在把加列转为加行,表结构如下
t_info:

md5 crawl_time status type info
*** *** *** A ***
*** *** *** B ***
*** *** *** C ***
*** *** *** D ***
type代表大字段,info代表具体的信息,如果增加了大字段,只是多加行而已,不会改表结构,
以前一行代表一个公司,现在多行代表一个公司

需求里还有将没爬取到的公司状态改为"注销",要找到没爬取到的公司,就得找到昨天爬取的公司和今天爬取到的公司,然后找出差集。
每天爬取的数据量很大,可能达到百W级别。在数据库里根据时间查找昨天爬取的公司信息,可能会很慢,而且将这么大的数据从数据库
里查出来放入内存,可能会撑爆内存。即使内存受得了,百W级别的集合,要找出差集,运算量很大,很耗时间。(我用两个50W的集合试了下,要2分多钟)
爬虫调java这边的接口不可能一次性把数据全传过来,太大了,会分多次调,一次传较少的数据。数据传完后会通知java这边。所以,可以在每次调用中
给传入的公司做个标记,标识该公司爬取到了。比如今天爬取的公司标记为1,明天爬取的公司标记为2,则当明天爬取完成后,标记仍为1的公司就代表没
爬取到,直接sql语句把标记为1的公司的状态改了,问题就解决了。
t_info:

md5 crawl_time status type info mark
*** *** *** A *** *
*** *** *** B *** *
*** *** *** C *** *
*** *** *** D *** *
t_mark:每天爬取完成后mark+1
mark
:----:
*

需求里还要记录公司的变化信息,没爬取到的公司当然也算变化,也要记录进t_change_info表里,同样的,没爬取到的公司数量可能也很大,上W很正常,毕竟世界上那么多公司。。。。
从t_info取出上万条数据放在一个list1里,在这个list1里for循环,每次循环得new一个t_change_info结构的对象放入另一个list2里,而且在循环里需要去数据库里查该公司上一轮爬取到的信息,
然后再将list2插入t_change_info表里。可能会有内存溢出的风险,解决办法就是将list1分成几个小的list,大而化小,分而治之,哈哈。

原文地址:https://www.cnblogs.com/liuwhut/p/12598735.html