sphinx 源码阅读之分词,压缩索引,倒排——单词对应的文档ID列表本质和lucene无异 也是外部排序再压缩 解压的时候需要全部扫描doc_ids列表偏移量相加获得最终的文档ID
转自:http://github.tiankonguse.com/blog/2014/12/03/sphinx-token-inverted-sort.html
外部排序
现在我们的背景是有16个已经排序的数据存在磁盘上。
由于数据量很大,我们不能一次性全部读进来。
我们的目标是依次挑出最小的hit,然后交给索引引擎处理。
sphinx 使用了 CSphHitQueue 这个数据结构。
CSphHitQueue 你猜是什么? 队列? 恭喜你,猜错了。
CSphHitQueue 是一个最小堆。
且堆的最大个数是 iRawBlocks。
由于 iRawBlocks 个 hits 数组已经排序,所以我们只需要得到 已排序的hits数组的第一个元素,就可以用堆得到最小的那个元素了。
然后我们把最小的这个元素建索引压缩储存,删除最小元素,并取出最小元素所在 hits数组中下一个元素,扔到堆中。
这样就可以从小到大取出所有的元素,并逐个建立索引压缩储存了。
这段话看不懂的话,可以看下面的图。
创建索引压缩储存
其中创建索引压缩储存主要依靠这个函数
cidxHit ( tQueue.m_pData );
其中 tQueue.m_pData 的数据结构如下
/// fat hit, which is actually stored in VLN index
struct CSphFatHit{
DWORD m_iDocID; ///< document ID
DWORD m_iGroupID; ///< documents group ID
DWORD m_iTimestamp; ///< document timestamp
DWORD m_iWordID; ///< word ID in current dictionary
DWORD m_iWordPos; ///< word position in current document
};
hit 是先按 m_iWordID 排序, 相等了再按 m_iDocID 排序, 最后才按 m_iWordPos 排序的。
现在我们先不考虑上面的堆,我们假设所有的 hit 已经在一个数组中了,且按上面的规则排序了。
现在我们想做的是对这个 hit 数组创建索引,并压缩储存。
主要做了这个几件事。
第一,根据 m_iWordID 将分词分为 2014 块。
并使用 cidxPagesDir 记录块的偏移量(还记得索引文件第二个写入的数据吗)。
第二,对于每一块,我们按分词分组,并在索引文件 spi 中储存每个词组的信息。
具体储存的信息如下
- 和上一个分词(wordID)的偏差
- 这个分词组在 spd 文件内的长度
- 这个分词记录的变化次数
- 这个分词的 hit 数量
第三,对于每个hit,我们存两部分信息。
- 位置(pos)偏移量信息
- 文档(docId)偏移量的信息
上面的三部分信息都储存后,我们就可以快速的解析出来。
推理 - 搜索信息
假设我们又上面的压缩的信息了。
我们要搜索一个词时,会如何工作呢?
假设我们已经得到这个词的 wordId 了,只需要二分一下,就可以再 O(log(1024)) 的时间内得到 wordId 在那个块内。
找到一个块内,出现一个问题,我们不能再次二分查找来找到对应的分词列表。 因为这个 index 储存的是和上一个分词的相对偏移量,那只好全部读入内存,扫描一遍对偏移量求和,然后才能找到对应的词。
这个过程中我们进行了两次 IO 操作。
第一次读取块列表信息 cidxPagesDir。
第二次读取选中的那一块的所有数据。
虽然储存偏移量节省了一些磁盘储存,但是却是用扫描整块数据为代价的。我们本来可以直接二分整块数据的。
不管怎样,我们在索引中找到了需要查找的那个分词的位置。
然后我们可以在数据文件内读取对应的信息,然后得到对应记录的id了。
当然,上面这个只是我的推理,下面我们来看看 sphinx 是怎么搜索的吧。
sphinx 搜索方法
看 sphinx 的搜索方法,只需要看 CSphIndex_VLN 的 QueryEx 函数即可。
首先对查询的语句进行分词,然后读取索引头 m_tHeader, 读取分块信息 cidxPagesDir。
然后就对分词进行搜索了。
为了防止相同的分词重复查找,这里采用二层循环,先来判断这个分词之前是否搜索过,搜索过就记下搜索过的那个词的位置。
没搜索过,就搜索。
xxx代码略!
看了这个代码,和我想的有点出入,但是总体思路还是一样的。
它是把所有的 cidxPagesDir 全储存起来了,这样直接定位到指定的位置了。少了一个二分搜索。
定位到某个块之后, 果然采用暴力循环来一个一个的增加偏移,然后查找对应的分词。
找到了记录对应的位置的四大元信息。
再然后由于数据量已经很小了,就把匹配的数据取出来即可。
当然,取数据的时候会进行布尔操作,而且会加上权值计算,这样就搜索满足条件的前若干条了。
sphinx 源码阅读之分词,压缩索引,倒排——单词对应的文档ID列表本质和lucene无异 也是外部排序再压缩 解压的时候需要全部扫描doc_ids列表偏移量相加获得最终的文档ID的更多相关文章
- sphinx源码分析总结
http://www.cnblogs.com/bonelee/p/6667955.html shinx索引部分源码分析——过程:连接到CSphSource对应的sql数据源,通过fetch row取其 ...
- 【转】cJSON 源码阅读笔记
前言 cjson 的代码只有 1000+ 行, 而且只是简单的几个函数的调用. 而且 cjson 还有很多不完善的地方, 推荐大家看完之后自己实现一个 封装好的功能完善的 cjson 程序. json ...
- 【原】AFNetworking源码阅读(二)
[原]AFNetworking源码阅读(二) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇中我们在iOS Example代码中提到了AFHTTPSessionMa ...
- spring-framework-4.1.x源码阅读环境搭建(导入Eclipse)
注意:搭建spring-framework-4.1.x源码 eclipse工作空间需要安装jdk8. spring-framework-4.1.x项目采用目前主流的项目管理工具gradle进行构建,至 ...
- 【原】FMDB源码阅读(二)
[原]FMDB源码阅读(二) 本文转载请注明出处 -- polobymulberry-博客园 1. 前言 上一篇只是简单地过了一下FMDB一个简单例子的基本流程,并没有涉及到FMDB的所有方方面面,比 ...
- 【原】FMDB源码阅读(一)
[原]FMDB源码阅读(一) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 说实话,之前的SDWebImage和AFNetworking这两个组件我还是使用过的,但是对于 ...
- 【原】SDWebImage源码阅读(五)
[原]SDWebImage源码阅读(五) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 前面的代码并没有特意去讲SDWebImage的缓存机制,主要是想单独开一章节专门讲 ...
- Spring源码阅读笔记
前言 作为一个Java开发者,工作了几年后,越发觉力有点不从心了,技术的世界实在是太过于辽阔了,接触的东西越多,越感到前所未有的恐慌. 每天捣鼓这个捣鼓那个,结果回过头来,才发现这个也不通,那个也不精 ...
- 【合集】TiDB 源码阅读系列文章
[合集]TiDB 源码阅读系列文章 (一)序 (二)初识 TiDB 源码 (三)SQL 的一生 (四)INSERT 语句概览 (五)TiDB SQL Parser 的实现 (六)Select 语句概览 ...
随机推荐
- 【java开发系列】—— struts2简单入门示例
前言 最近正好有时间总结一下,过去的知识历程,虽说东西都是入门级的,高手肯定是不屑一顾了,但是对于初次涉猎的小白们,还是可以提供点参考的. struts2其实就是为我们封装了servlet,简化了js ...
- CKplayer功能配置
开源ckplayer 网页播放器, 跨平台(html5, mobile),flv, f4v, mp4, rtmp协议. webm, ogg, m3u8 ! 博客分类: Javascript /Jque ...
- Java关于链表的增加、删除、获取长度、打印数值的实现
package com.shb.java; public class Demo8 { public Node headNode = null; /** * @param args * @date 20 ...
- -Xmx 和 –Xms 设置最大堆和最小堆
C:\Java\jre1.6.0\bin\javaw.exe 按照上面所说的,最后参数在eclipse.ini中可以写成这个样子: -vmargs -Xms128M -Xmx512M ...
- 4Web Service中的几个重要术语
4.1WSDL: web service definition language 直译:Webservice定义语言 1.对应一种类型的文件.wsdl 2.定义了webservice的服务端与客户端应 ...
- Azure Table storage 之改进DynamicTableEntity类为其添加动态语言扩展
在之前的一篇文章中提到,storage类库中包含一个可以用来动态获取Azure table storage 表结构的类-DynamicTableEntity. 我们可以通过这个类,我们无需为每一个表提 ...
- 关于ORACLE隐式转换后性能问题
SELECT TM.MONEY_CODE FROM T_CONTRACT_MASTER T,T_MONEY TM WHERE T.MONEY_ID = TM.MONEY_ID AND T.POLICY ...
- python-判断系统平台
1.windows 2.linux 总结 python提供了sys,os及platform等个模块读取平台信息,客官可以根据自己的喜好选择使用
- /var/spool/postfix/maildrop 占用inode索引及磁盘空间解决办法
1.问题表现和检查 运行df -i / 查看inode使用是否满: 2.查看/var/spool/postfix/maildrop是否有非常多的小文件,ls直接卡死等情况 解决: 删除小文件: cd ...
- JAVA基础知识之JDBC——离线RowSet
离线RowSet 如果直接使用ResultSet, 程序在得到ResultSet记录之后需要立即使用,否则一旦关闭Connection就不再可用,要解决这种情况要么将ResultSet的结果转换成Ja ...