sphinx 源码阅读之分词,压缩索引,倒排——单词对应的文档ID列表本质和lucene无异 也是外部排序再压缩 解压的时候需要全部扫描doc_ids列表偏移量相加获得最终的文档ID

转自:http://github.tiankonguse.com/blog/2014/12/03/sphinx-token-inverted-sort.html

sphinx 在创建索引前需要做下面几件事:有数据源(pSource),有分词器(pTokenizer),有停止词Stopword 和 字典(pDict),索引引擎。

我们假设 数据源是 mysql, 分词器是 utf8 分词器。

第一步是准备数据源。
这里采用 mysql 数据源。
mysql 数据的特点是一行一个记录。
每个记录有相同的字段。
每个字段可能代表数字,字符串,时间,二进制等信息,我们都可以按字符串处理即可。

  1. //数据源
  2. CSphSource_MySQL * pSrcMySQL = new CSphSource_MySQL ();
  3. CSphSource * pSource = pSrcMySQL;

第二步准备分词器和字典。
这里不多说分词器,以后会专门写一篇记录来讲解分词器。 
分词器依靠字典,可以把一个字符串分割为一些词语(word)。
然后根据这些词语,我们可以把mysql的每条记录每个字段都分割为若干词语,这里成为分词。
分割后这个分词需要保留几个信息:什么分词,属于哪个记录,属于哪个字段,在字段中的位置。
分词我们会hash (crc32) 成一个数字,冲突了就当做一个词了。
记录标示就是用自增整数ID.
字段一般不会很多,我们假设最多255个,使用8位可以表示。 字段的位置不确定,但是一个字段的内容也不会很多,我们用24位表示足够了。
所以哪个字段和字段的哪个位置就可以用一个32位整数代替了。
这样一个分词就可以用三个整数<wordId, docId, pos>来表示了。

  1. //分词器
  2. pTokenizer = sphCreateUTF8Tokenizer ();
  3. pSource->SetTokenizer ( pTokenizer );
  4. //字典
  5. CSphDict_CRC32 * pDict = new CSphDict_CRC32 ( iMorph );
  6. pSource->SetDict ( pDict );

一个分词称为一个hit, 数据结构如下

  1. struct CSphWordHit {
  2. DWORD m_iDocID; //文档ID, 唯一代表一个记录
  3. DWORD m_iWordID; //单词ID, 对单词的hash值,可以理解为唯一标示
  4. DWORD m_iWordPos; //储存两个信息:字段位置(高8位)和分词的位置(低24位)
  5. };

我们一条记录一条记录的把所有的记录都分词了,就得到一个分词列表了。
由于这个列表很大,我们需要分成多块储存,这里假设最多16块吧。
对于每块,储存前先排序一下,这样我们就得到 16 个 有序的数组了。
然后我们就可以创建索引了。

  1. //索引
  2. CSphIndex * pIndex = sphCreateIndexPhrase ( sIndexPath );
  3. //开始创建索引
  4. pIndex->Build ( pDict, pSource, iMemLimit )

其中 一切准备完毕后进入 Build 函数。

 

进入 build 函数后先准备内容。

在执行 build 函数时 ,先逐条读取记录,然后对每条记录的每个字段会进行分词(Next函数),存在 hit 数据结构中。
而且会把 hit 数据按指定块大小排序后压缩储存在 *.spr 文件中

块信息储存在 bins 数组中,块数最多16块, 块数用 iRawBlocks 表示。

接下来就是关键的创建压缩索引了。 
首先创建索引对象。

  1. cidxCreate()
  2. //打开索引文件,先写入 m_tHeader 信息 和 cidxPagesDir 信息。
  3. fdIndex = new CSphWriter_VLN ( ".spi" );
  4. fdIndex->PutRawBytes ( &m_tHeader, sizeof(m_tHeader) );
  5. //cidxPagesDir 数组全是 -1
  6. fdIndex->PutBytes ( cidxPagesDir, sizeof(cidxPagesDir) );
  7. //打开压缩数据文件,先写入一个开始符 bDummy
  8. fdData = new CSphWriter_VLN ( ".spd" );
  9. BYTE bDummy = 1;
  10. fdData->PutBytes ( &bDummy, 1 );
posted @ 2017-01-04 16:58  bonelee  阅读(467)  评论(0编辑  收藏  举报