当前位置: 首页 > 科技观察

500W数据,20Wqps分词搜索,架构怎么设计?

时间:2023-03-12 20:20:12 科技观察

作者|KG沉剑有水友问:沉哥,我们有一个业务,类似“标题分词搜索”,并发量非常大,每秒20万次左右,数据量大不是很大,50万左右,数据不会经常更新,平均一天一次。有什么好的解决办法吗?这是一个典型的短文本分词搜索问题。我简单说一下我自己的经历吧。常见的文本检索方案有哪些?(1)数据库LIKE方法将题名数据存入数据库,使用like查询。该方案非常简单,可以支持简单的模糊搜索,但不支持分词。画外音:显然不适用于这种情况。(2)数据库全文检索方法将题名数据存储在数据库中,建立全文索引供检索。方案还是比较简单的,利用了数据库的能力,不需要额外开发,但是性能比较低。画外音:这个例子中的并发肯定是处理不了的。(3)开源解决方案索引外部方法构建lucene、solr、ES等开源搜索工具,构建索引,支持分词,支持数据量和吞吐量的横向扩展。该方案可以很好的满足本例的需求。但是,杀鸡用牛刀,这个例子有一些业务特点:文本短,更新不频繁。如果利用好这两个特点,可以有更巧妙的解决办法。画外音:任何倒闭的建筑设计都是耍流氓。针对“不常更新”的特点,可以采用“分词+DAT”的方案。画外音:分词就不多说了。什么是DAT?DAT是doublearraytrie的缩写。它是特里树的一种变体优化数据结构。在保证trie树检索效率的同时,可以大大减少内存的使用。常用于解决检索、信息过滤等问题。画外音:更具体的,你可以谷歌“DAT”。DAT的缺点是需要提前建立索引,而且索引不能实时更新。为什么要用trie树的变体DAT,trie树可以直接用吗?trie树的优点是可以实时更新索引;缺点是占用内存大。在这个例子中,索引不需要实时更新,不能发挥trie树的优势。但是,如果300W的短文已经建立,内存中可以存入trie树,那么就可以使用trie树,否则只能使用DAT。普及一下,什么是trie树?Trie树,又称查词树,常用于搜索引擎词频统计、短文本检索、输入法输入提示等。画外音:你必须熟悉什么样的数据结构适合什么样的业务场景。它的特点是可以使用字符串的公共前缀来减少查询时间,减少不必要的字符串比较。其查询时间的复杂度只与树的高度有关,与查询数据量级无关,因此查询效率很高。高的。画外音:“时间复杂度与查询的数量级无关”这太糟糕了。例如:上面的trie树可以表示{and,as,at,cn,com}等5个标题的集合,可以用来做词频统计或者检索这5个字符串。画外音:检索时,节点存储命中item的doc_list。分词后,是否需要多次扫描trie树?是的。分词后每个item需要扫描一次trie树,得到的doc_list的交集就是最终命中每个item的检索结果。针对“短文本”、“500W数据”、“更新不频繁”的特点,也可以采用“分词+内存哈希”的方案。该方案需要先初始化索引:将所有短文本切分,以word的hash为key,以doc_id的集合为value。查询过程也很简单:将查询字符串切词,对每个词进行哈希处理,直接查询哈希表得到doc_list,然后对每个词的检索结果进行求交。举个栗子来说明。例如:doc1:我爱北京doc2:我爱大家doc3:道家很美首先对短文本进行切分:doc1:我爱北京->我爱北京doc2:我爱道家->我爱道家doc3:到家美号->到家,美号对分词进行哈希处理,创建哈希表:hash(me)->{doc1,doc2}hash(love)->{doc1,doc2}hash(Beijing)->{doc1}hash(Daojia)->{doc2,doc3}hash(beautiful)->{doc3}这样初始化所有短文本,类似于trie树,查询时间复杂度与文本数据量无关.画外音:只和分片后的数据量有关,也就是哈希桶的个数。查询过程如下:如果用户输入“我爱”,则分词变为{I,love},对每个分词的hash进行内存检索:hash(me)->{doc1,doc2}hash(love)->{doc1,doc2}然后合并,最终查找结果为{doc1,doc2}。这种方式的好处是纯内存操作可以满足大并发量,延迟也很低,占用内存也不大。实现非常简单快速,冗余索引易于水平扩展。画外音:让索引高可用并不难,只需要创建两个相同的哈希索引即可。它的缺点也很明显。索引已满内存,尚未执行。它还需要在数据库中存储固化的短文本数据。如果内存数据全部丢失,数据恢复会很慢。总结短文本、高并发、支持分词、不需要实时更新的检索场景可以使用:ES,杀鸡用牛刀;分词+DAT(trie);分词+内存哈希;和其他解决方案。思路比结论更重要,希望大家有所收获。