目前我想到的存储方案有两种:单文件单页面存储和单文件多页面存储。
单文件单页面存储就是一个页面存储在一个文件中,文件名称可以使用页面编号doc_id。这种方案查找的时候容易定位,直接使用文件名就可以,缺点是会产生大量琐碎的文件,管理、冗余和查询感觉都不太方便。
单文件多页面存储即一个文件存储多个页面,写满一个文件后,自动开始写下一个。每个文件有一个编号store_file_id,在查找某一个页面的时候,通过页面的编号doc_id,可以找到对应的store_file_id及页面在文件中的位置position,然后就能找个这个页面。每个文件的大小是可以定制的,默认200M一个。一个页面的数据不会分在两个文件中,只会在一个页面写入完成后,才会发生写新文件的操作,所以存储实际的大小会是200M多几十K。
目前采用的就是单文件多页面的存储方式。其中的每个页面又按以下格式存放数据:
- doc_id:页面文档编号,unsigned int,该页面的url的md5签名。
- timestamp:页面的抓取时间,time_t类型。
- is_analyzed:该页面是否已经分析,bool(short)类型。
- url_length:该页面url的长度,unsigned int。
- url:该页面的url,长度为url_length的char数组。
- domain_length:域名的长度,unsigned int,类似url_length。
- domain:域名,长度为domain_length的char数组,类似url。
- response_length:响应的长度,unsigned int,类似url_length。
- response:响应的内容,长度为response_length的char数组,类似url。
存储格式图示如下(单一存储文件):
store模块中的主要函数如下:
/* * 初始化文件存储,在调用store_page方法前,必须先调用此方法。 * @store_path 存储路径 * @store_file_max_length 存储文件的最大长度,单位是字节 */ extern int initial_store(char* store_path, unsigned long store_file_max_length); /* * 关闭文件存储 */ extern int close_store(); /* * 将页面存储到磁盘 * @page 要储存的页面 */ extern int store_page(page* page); /* * 取得下一个未分析的页面 * @page 用于存在数据的页面 * @return 成功返回0,出错返回-1,没有未分析的页面,返回1 */ extern int get_page_to_parse(page* page);
initial_store和close_store这对方法用于开始和结束。initial_store会根据给出的store_path自动计算出下一个要写入的文件的编号和位置。close_store负责关闭文件指针,这两个函数在主程序中出现,且只出现一次:initial_store在出程序的开始,close_store在主程序的结束。
store_page和get_page_to_parse这对方法用于写入和读出。store_page将给出的page结构体写入文件,同时负责新存储文件的创建等。get_page_to_parse则负责读取下一个要分析的页面,存储在page指针中。get_page_to_parse还会将这次读取给出的文件在磁盘中标记为已分析。
这里的分析的含义仅指提取页面中的url。