Skip to content

ArkEngine/flexse

Repository files navigation

看sphinx的文档
(1) 为什么要做这个东东
-1- 搜索产品内部需要
    我们的相关搜索和搜索建议,本质上还是倒排结构。
-2- 搜索院对其他部门的技术支持
    云城搜索/C2C搜索/积分商城搜索。。

(2) 架构愿景和应用前景
-1- 一张图,表示云搜索,叙述工作流程,小数据+大数据量扩展
-2- 应用前景,框架的应用 + 插件使用

(3) 特性
性能方面
-0- 实时索引
-1- 快速建库
-2- 快速查询
-3- 超大数据
功能方面
-4- 算法强大
-5- 功能全配
-6- 灵活配置
数据恢复
-7- 崩溃恢复
架构远景
-8- 云搜索化
二次开发
-9- 支持插件

(1) 倒排索引
-1- index_group = mem_indexer + disk_indexer
-2- mem_indexer = postinglist + creat_sign
    #1# postinglist = (hash-bucket) + (headlist) + memblocks
-3- disk_indexer = 二级索引 + [1级索引cache] + fileblock + diskv
索引管理逻辑
-1- mem/disk的时间节点
-2- dump逻辑
-3- 修改和删除(bitmap+idmap)

(2) 文档属性
-1- bitlist + structmask

(3) 文档详细
-1- detaildb = fileblock + diskv

服务搭建
(1) 线程组成
-1- equeue + query_threads
-2- update_thread
-3- day_merger_thread
-4- his_merger_thread
-5- ontime_thread
(2) 通信协议
-1- xhead + json

===============================================================
(1) 概述
flexse目前是一个搜索的框架,提供了搜索引擎中数据的存储和访问,特别实现了倒排数据的实时索引。
-1- 倒排索引原理
    倒排索引抽象成数据结构,可以理解为map<string, vector<index_t>>,string就是分词形成的每
    个term,而vector<index_t>就是含有这个term的各个文档ID列表,index_t就是描述这个term属性
    的结构,index_t中必须含有一个ID。通过flexse/utils/indexer/index_group/index_group.cpp
    中的get_posting_list方法,可以获得一个term对应的postinglist,如果内存不够存储,则返回
    最新的内存大小的postinglist,失败返回-1。postinglist按照ID降序排列。
-2- 如何做到实时索引和实时更新(插件开发者不需要知道)
    每个文档都会有个外部ID和内部ID,外部ID都是连续的,内部ID也是连续的,一个外部ID对应于一
    个最新的内部ID。通过flexse/utils/idmap来管理内部和外部ID,当插入或更新一个文档时,根据
    该文档的外部ID,来检查是否已经为其分配过内部ID,如果不存在,则内部ID自增后,为其分配一
    个内部ID;如果已经为其分配过内部ID,则把内部ID对应的bitmap对应位置设置为1,且为其分配一
    个新内部ID。
    这样,当失效的内部ID经过mod_bitset过滤时,就能去掉已经失效的文档了。
    举个例子:
    文档1含有term: a/b/c, 外部ID为1,插入后,为其分配内部ID为1;
    文档1更新,term更新为: a/d/e,更新后,为其分配一个新的内部ID为2,同时把mod_bitset中,内
    部ID为1的位置设置为1。当我们搜索a时,可以得到内部ID为1和2的文档,经过mod_bitset过滤后,
    文档2得以保留,去掉了失效的文档1。
-3- 如何访问文档属性数据
    文档属性数据存储于文件中,以mmap的方式load到内存中,以数组的方式访问,使用内部ID作为偏移
    以structmask的机制来访问数据,structmask的原理见这个wiki
(2) 更新
-1- 如何更新倒排索引postinglist
    输入: xhead+json-string,有固定的格式,解析过程详见代码flexse/plugin/flexse/
    输出: xhead.
    测试工具: flexse/flexse/test/update.py,与配置文件flexse/flexse/conf/plugin.config.json
    相互对应,目前支持分词/前缀/数组前缀这三种方式
    postinglist根据term形成的机制分为两种类型,NLP分词和前缀生成,视频搜索视需求来选择这两
    个类型
    a) NLP分词形成的term,在flexse/utils/nlp_processor/nlp_processor.cpp中可以设置分词形成
       的各个term的描述信息,如tf/idf/offset/title_hit/tag_hit等等,需要注意的是,视频全网
       搜索的offset计算,建议使用title即可,因为视频的描述信息在各大网站上看,基本都是空的
    b) 前缀生成的term,比如"TAG^搞笑"等等,term的描述一般都为默认值
-2- 如何更新文档属性document-attribute
    这部分代码已经完成,原理是根据配置中,迭代STRUCTMASK/document_attr中各个字段,把其中的
    key<->value存入vector中,update_thread.cpp中对外部的doc_id进行内部ID分配,然后更新内部
    ID对应的document_attribute属性
(3) 查询
    输入为xhead+json-string,有固定的格式,解析过程详见代码flexse/plugin/flexse/
    输出是xhead+struct数组(不使用json或其他序列化方案是为了性能考虑),返回值表示struct数组
    的sizeof大小
    查询操作在flexse/plugin/flexse/flexse_plugin.cpp中实现
    测试工具: flexse/flexse/test/qquery.py,与配置文件flexse/flexse/conf/plugin.config.json
    相互对应,目前支持分词/前缀/数组前缀这三种方式
    查询的步骤一般可以分为
    a) 根据termlist,读取对应的postinglist。
    b) 把一组postinglist交给归并算法进行合并,目前已经支持按照term的权重进行归并(algo.cpp)。
    c) 对归并结果postinglist进行过滤,目前已经支持按照某个字段执行(等于/小于/大于/大于小于
        /集合这5种过滤)(algo.cpp)
    d) 对过滤结果postinglist进行调权,目前已经支持按照某个字段执行(等于/小于/大于/大于小于
        /集合这5种调权)(algo.cpp)
    e) 按照权重返回top2000的结果,把每个ID的document-attribute带上

-----------------------------------------
(1) 对数据的抽象
    -1- 倒排索引
    -2- 文档属性
    -3- 文档详细
(2) 对流程的抽象
    -1- 更新流程
    -2- 查询流程
-----------------------------------------
(1) 调权
    -1- L0层, query与term的相关性(title_hit/anchor_hit/tag_hit/offset)
    -2- L1层, 文档自身属性调权(等于/大于/小于/区间/集合)
    -3- L2层, 用户个性化调权(点击调权+用户类型与文档分类切合度)
(2) 过滤
    -1- 对文档属性进行(等于/大于/小于/区间/集合)几种过滤
    -2- 实现标题内搜索
    -3- 站内搜索(等于/集合)
    -4- 标签搜索(tag索引)
(3) 排序
    -1- 可以按照任意的field进行排序
    -2- 可以实现"对信誉度最高的前30个商户按照最近销量排序"
(4) 聚类
    -1- 可以根据某个字段进行group_count
-----------------------------------------
(1) query分析
    -1- 纠错
    -2- 同义词
    -3- term的weight
(2) query语法树
    -1- a&b&c, b->b|B, c->c|C, a&(b|B)&(c|C)
    -2- 按照weight进行merge,实现模糊查询,不必要求每个关键词都命中(百度视频搜索),后续可以强制必须命中某些term
-----------------------------------------
(1) 搜索直达车
    -1- SEU(标准实体单元)的资源建设,如电视剧电影->视频搜索,商品信息->电商
-----------------------------------------
(1) 数据的分片(Sharding)
    -1- php-interface处对key进行滚动分区(建立一个监控机制)
    -2- 如果key不是连续自增的外部ID,则需要对key进行签名,我们为其分配外部ID
    -3- 文档详细的访问采用的key为分片时的key,或者是其他形式,如签名
    -4- mongodb的sharding访问需要支持按照DB进行sharding
(2) 数据的复制(Repset)
    -1- 通过消息队列复制消息,各个服务互不干扰
    -2- 对消息队列的进度进行监控,发现异常队列,及时报警
    -3- 每个app独占一个消息队列文件序列

-----------------------------------------
画个图来表明flexse是怎么构建的
shijing的wiki
(1) 如何提高可测性
    -1- 模块化编程
        a) 每个类或函数做简单的事情,一个4百行的函数和linux的管道
        b) 性能考虑会打破规律
    -2- 面向接口编程
        a) 面向实现编程与面向接口编程,要对对象或函数的应用环境考虑清楚(需求),然后设计合适的接口。一句话就是想清楚再干。
        b) 类的协作是通过接口来完成的,面向接口编程是复用性的前提,电脑的USB接口
        c) 类之间通信简单易懂,扩展性好,提高复用性,扩展性,松耦合
        c) 团队更应该使用面向接口编程
    -3- 做好异常处理(fileblock的一个异常的例子,尽早的发现错误,测试也是调试,要得到有效的测试)
    -4- 只要是接口,就可以执行单元测试(接口就是协议,就是预期,不想是电脑彩票)
(2) 单元测试角度
    -1- offset by 1(我灌入了2800万term,测试出了一个bug)
    -2- 临界值()
    -3- 小数据集合(灯下黑)
    -4- 大数据测试
    -5- 压力测试(多线程并发)
(3) 单测之前
    -1- 编译警告去除
    -2- 静态代码检查工具pclint
    -3- 运行时测试工具valgrind
    -4- 冥思
        a) 如果那陀代码很恶心,那他一定会出问题。(重构?: 经历就是财富,不要担心没人关注)
        b) 小心代码copy,即使是自己的代码,copy也会出问题(周六的SB-bug)
        c) 时常进行codereview
(4) 单元测试好处
    -1- 回归测试(我的struct宏曾经几经变动,谢谢test.cpp,别人接手时,也会有信心)
    -2- 增强信心(我的代码都是经过单元测试的,如果我现在放弃了,多可惜)
    -3- 节奏欢快(把开发的焦点聚焦于当前任务,因为我对之前经过单元测试的代码有信心)
    -4- 合作顺畅(我们的代码都是经过单元测试的,代码bug的数量可以得到控制,不会严重打击我们的合作心情)
    -5- 大型软件系统中,BUG就像癌症一样,越早发现越好
    -6- 专业习惯受益终审
(5) 覆盖率测试
(6) gtest测试








About

mirror repository of flexse in google code. Author: scenbuffalo@gmail.com

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published