ElasticSearch核心技术解析:倒排索引与IK分词器如何解决中文搜索痛点_elasticsearch中文分词器不好用
在传统数据库中,模糊查询往往难以应对现实中的复杂情况。随着数据量的增加,查询时间显著变慢,功能也相对单一。当搜索时出现错别字或拼音输入时,搜索结果往往不尽如人意。因此,我们需要学习一种强大的搜索框架技术——ElasticSearch。
倒排索引
在传统的模糊搜索中,系统会从表中逐条读取数据,筛选出与模糊搜索条件相关的记录。这种方式通常使用B+树来建立索引,但由于索引与每条数据的内容没有直接关联,导致搜索时需要逐条检索,效率较低。
倒排索引
倒排索引是ElasticSearch等现代搜索引擎的核心技术之一。它引入了文档和词条两个概念:
- 
文档:相当于数据库中的一条记录。
 - 
词条:对文档内容进行分词处理。例如,句子“我是倒排索引”可以通过特定算法拆分为“我”、“是”、“倒排”、“索引”等词条。这些词条可能还包含其他语义相关的词语。
 
倒排索引的工作原理
假设我们有以下商品数据:
- 
\"黑玩具\"
 - 
\"红玩具\"
 - 
\"红书包\"
 
倒排索引会对这些文档进行词条划分,并建立词条与文档编号的映射关系:
- 
黑:1
 - 
玩具:1, 2
 - 
红:2, 3
 - 
书包:3
 
当用户搜索“粉玩具”时,系统会首先对搜索词进行分词处理,得到“粉”和“玩具”两个词条。然后,系统会在倒排索引中查找这些词条对应的文档编号:
- 
“粉”没有对应的文档编号。
 - 
“玩具”对应的文档编号是1和2。
 
最终,系统会返回文档编号为1和2的结果。由于词条和文档编号都建立了索引,搜索效率非常高。
倒排索引的优势
- 
高效检索:通过词条与文档编号的映射,系统可以快速定位相关文档,避免了全表扫描。
 - 
容错性强:即使搜索词中存在错别字或拼音输入,系统仍然可以通过分词和索引匹配找到相关结果。
 - 
支持复杂查询:倒排索引支持多种查询方式,如布尔查询、短语查询、范围查询等,能够满足多样化的搜索需求。
 
IK分词器
ElasticSearch 依靠倒排索引来实现高效的全文搜索,而倒排索引的核心在于分词。对于中文文本,分词尤为重要,因为中文不像英文那样有天然的空格分隔。IK分词器是一个专门为中文设计的分词器,它能够将连续的汉字序列切分成有意义的词语,而不是简单地将每个字分开。这种分词方式不仅保留了语句的完整性,还方便了词条的创建和索引。
在 IK 分词器的配置中,IKAnalyzer.cfg.xml 文件用于对分词行为进行定制化配置。例如,当网络新梗或流行语出现时,传统的分词规则可能无法正确识别这些新词,导致分词结果不准确。通过修改配置文件,我们可以动态地扩展词典或调整分词规则,以适应实际需求。
以下是一个典型的 IKAnalyzer.cfg.xml 配置文件示例:
IK Analyzer 扩展配置ext.dic
配置文件解析:
- 
ext_dict:用于指定扩展词典文件(如
ext.dic)。扩展词典允许用户添加新词或专有名词,确保这些词在分词时不会被错误地拆分。例如,网络流行语“内卷”、“躺平”等可以通过扩展词典加入分词器的词库。 - 
ext_stopwords:用于指定停用词文件。停用词是指在文本中出现频率较高但对搜索结果影响较小的词,例如“的”、“了”、“是”等。通过配置停用词,可以减少索引的大小,提高搜索效率。
 
IK分词器的两种模式:
- 
ik_smart:智能分词模式,采用粗粒度分词策略。这种模式会将文本切分成较少的词条,适合用于搜索引擎的初步筛选。例如,句子“人工智能技术”在
ik_smart模式下可能只会分成“人工智能”和“技术”两个词条。 - 
ik_max_word:细粒度分词模式,采用最大分词策略。这种模式会尽可能多地将文本切分成不同的词条,适合用于需要高精度的场景。例如,句子“人工智能技术”在
ik_max_word模式下可能会分成“人工”、“智能”、“人工智能”、“技术”等多个词条。 
基本概念
ElasticSearch 的许多概念可以从 MySQL 中延伸理解:
- 
索引(Index):相当于 MySQL 中的表(Table),用于存储相关数据。
 - 
字段(Field):相当于 MySQL 中的列(Column),用于定义数据的属性。
 - 
文档(Document):相当于 MySQL 中的一行数据(Row),但在 ElasticSearch 中,文档是以 JSON 格式存储的。
 - 
映射(Mapping):相当于 MySQL 的表结构(Schema),用于定义字段的数据类型和约束。
 
与 MySQL 不同,ElasticSearch 将每一行数据转换为 JSON 格式存储,这种设计使其在处理非结构化或半结构化数据时更加灵活。此外,ElasticSearch 在应对大规模数据搜索场景时表现出更高的效率,尤其是在全文搜索和复杂查询方面。
然而,MySQL 在数据安全性和事务支持方面更具优势,适合需要强一致性和复杂事务处理的场景。
索引库操作
索引相当于数据库中的表, 映射相当于数据库的表结构, 用来进行数据的约束, 要想进行倒排索引就要设置索引和映射
Mapping映射属性
在数据建模和搜索引擎配置中,映射属性(Mapping)用于定义字段的类型及其处理方式。以下是常见的字段类型及其相关属性:
字段类型
- 
字符串类型:
- 
Text: 适用于需要分词的文本字段
 - 
Keyword: 适用于不需要分词的精确文本,如品牌名称、国家代码等。
 
 - 
 - 
数字类型:
- 
Integer: 32位整数。
 - 
Long: 64位整数。
 - 
Short: 16位整数。
 - 
Double: 双精度浮点数。
 - 
Float: 单精度浮点数。
 - 
Byte: 8位整数。
 
 - 
 - 
布尔类型:
- 
Boolean: 表示真或假的值。
 
 - 
 - 
日期类型:
- 
Date: 用于存储日期和时间。
 
 - 
 - 
对象类型:
- 
Object: 用于嵌套的复杂数据结构。
 
 - 
 
其他属性
- 
Index: 确定字段是否被索引。如果设置为
false,该字段将不会被搜索,但仍可存储在文档中。 - 
Properties: 用于定义嵌套字段或子字段的结构。
 - 
Analyzer: 指定用于分词的分词器类型,适用于
Text类型的字段。 
索引库
索引库的创建与映射设置
- 创建索引库并设置映射
- 在Elasticsearch中,创建索引库的同时可以定义映射(mappings),映射用于描述索引库中文档的结构,包括字段的类型等信息。以下是创建索引库并设置映射的基本操作:
 - 使用
PUT请求来创建索引库并设置映射。例如,创建一个名为my_index的索引库:- 在上述示例中:
- 对于
title字段,它被定义为text类型,并且还定义了一个子字段keyword,这个子字段也是keyword类型。这种结构很常见,text类型适合用于全文搜索,而keyword类型适合用于精确匹配,比如过滤或者排序。 - 对于
price字段,它是double类型,并且设置了\"index\": false。这意味着这个字段不会被索引,不能用于搜索,但可以在查询结果中返回该字段的值。 
 - 对于
 
 - 在上述示例中:
 
PUT /my_index{ \"mappings\": { \"properties\": { \"title\": { \"type\": \"text\", \"fields\": { \"keyword\": { \"type\": \"keyword\" } } }, \"price\": { \"type\": \"double\", \"index\": false } } }} 
获取索引库
- 查询索引库是否存在及基本信息
- 使用
GET请求可以获取索引库的信息。例如,要获取名为my_index的索引库的信息:- 如果索引库存在,Elasticsearch会返回该索引库的各种元数据信息,如索引的设置(包括分片数量、副本数量等)、映射等信息。如果索引库不存在,则会返回相应的错误提示。
 
 
GET /my_index - 使用
 
添加新字段
- 注意事项
- 在Elasticsearch中,如果直接在已经存在的索引库中进行字段修改,可能会导致倒排索引重新排列。当索引库中的数据量非常庞大时,这个操作可能会消耗大量的资源,甚至可能使服务器瘫痪。
 
 - 正确的添加新字段方式
- 要添加新字段,应该使用
PUT请求针对索引库的_mapping端点进行操作。例如,要在my_index索引库中添加一个名为description的新字段,类型为text:- 这种方式相对比较安全,并且Elasticsearch会根据新的映射调整索引结构,尽量减少对现有数据和搜索性能的影响。
 
 
PUT /my_index/_mapping{ \"properties\": { \"description\": { \"type\": \"text\" } }} - 要添加新字段,应该使用
 
删除索引库
- 执行删除操作
- 如果要删除一个名为
my_index的索引库,可以使用DELETE请求:- 在执行删除操作之前,需要谨慎考虑,因为一旦索引库被删除,其中所有的数据都将永久丢失,并且无法恢复。
 
 
DELETE /my_index - 如果要删除一个名为
 
文档操作
新增文档
- 基本语法
- 使用
POST请求向指定索引库的_doc路径下添加文档,并可指定文档ID。 - 格式:
POST /索引库名/_doc/文档id - 示例:
 
POST /my_index/_doc/1{ \"title\": \"示例文档\", \"content\": \"这是新增文档的内容\"}- 若不指定文档ID,Elasticsearch会自动生成一个唯一ID。
 
POST /my_index/_doc/{ \"title\": \"自动生成ID的文档\", \"content\": \"内容\"} - 使用
 
获取文档
- 基本语法
- 使用
GET请求通过索引库名、_doc路径和文档ID来获取特定文档。 - 格式:
GET /索引库名/_doc/{id} - 示例:
- 若文档存在,将返回该文档的详细信息;若不存在,会返回相应错误提示。
 
 
GET /my_index/_doc/1 - 使用
 
删除文档
- 基本语法
- 使用
DELETE请求通过索引库名、_doc路径和文档ID来删除特定文档。 - 格式:
DELETE /索引库名/_doc/{id} - 示例:
- 成功删除会返回相应成功提示,若文档不存在则返回错误提示。
 
 
DELETE /my_index/_doc/1 - 使用
 
修改文档
一、全量修改
- 操作原理
- 全量修改意味着对文档进行较为彻底的更新操作。如果目标文档在索引库中已经存在,那么会先删除原来的文档,然后再新增一个文档。如果该文档在索引库中原本就不存在,则会直接重新创建这个文档。
 
 - 操作示例
- 假设我们有一个名为“my_index”的索引库,并且要修改或者创建一个id为“1”的文档。其操作如下:
 - 使用的API为:PUT /my_index/_doc/1
 - 文档内容示例:
 
{ \"title\": \"新的标题\", \"content\": \"这是全新的内容\"} 
二、局部修改
- 操作原理
- 局部修改主要是针对文档的部分属性进行修改。这种修改方式不需要删除整个文档再重新创建,而是只更新指定的部分属性,从而节省资源和时间。
 
 - 操作示例
- 同样以“my_index”索引库中id为“1”的文档为例,如果只想修改其中“title”这个属性。
 - 使用的API为:POST /my_index/_update/1
 - 文档内容示例:
 
{ \"doc\": { \"title\": \"修改后的标题\" }} 


