Skip to content

全文检索深入

全文检索是搜索引擎的核心技术,相比传统的 SQL LIKE 查询,它支持分词、相关性评分、高亮显示、模糊匹配。Elasticsearch 是最流行的全文检索引擎,本节深入讨论其高级特性和调优方法。

中文分词

分词原理

中文分词是将连续的汉字序列切分成有意义的词语。与英文不同,中文没有天然的词边界,需要通过算法识别词语。分词算法包括基于词典的算法、基于统计的算法、基于深度学习的算法。

基于词典的算法通过匹配词典中的词语进行切分,包括正向最大匹配、逆向最大匹配、双向最大匹配。正向最大匹配从左到右扫描,每次匹配最长的词语。这种算法简单高效,但无法处理未登录词。

基于统计的算法通过统计词语共现频率进行切分,包括 HMM、CRF、深度学习序列标注。jieba 分词结合了词典和统计算法,对于词典中的词语直接使用词典匹配,对于未登录词使用 HMM 分词。

IK 分词器

IK 分词器是 Elasticsearch 最常用的中文分词插件,支持智能分词和细粒度分词两种模式。智能分词模式进行最粗粒度的切分,细粒度分词模式进行最细粒度的切分。

json
{
  "analyzer": "ik_smart",
  "text": "中华人民共和国国歌"
}
// 输出:中华人民共和国、国歌

{
  "analyzer": "ik_max_word",
  "text": "中华人民共和国国歌"
}
// 输出:中华人民共和国、中华、人民、共和、国、国歌

IK 分词器支持自定义词典,可以添加行业术语、专有名词、新词。自定义词典文件放在 config 目录,每行一个词,支持词频设置。修改词典后需要重启 Elasticsearch。

自定义分词器

Elasticsearch 支持自定义分词器,由字符过滤器、分词器、词项过滤器组成。字符过滤器处理原始文本,例如 HTML 清理、字符替换。分词器将文本切分为词项。词项过滤器处理分词结果,例如小写化、同义词、停用词。

json
{
  "settings": {
    "analysis": {
      "char_filter": {
        "html_strip": {
          "type": "html_strip"
        }
      },
      "filter": {
        "my_synonym": {
          "type": "synonym",
          "synonyms": ["电脑,计算机,PC"]
        }
      },
      "analyzer": {
        "my_analyzer": {
          "type": "custom",
          "char_filter": ["html_strip"],
          "tokenizer": "ik_max_word",
          "filter": ["lowercase", "my_synonym"]
        }
      }
    }
  }
}

相关性评分

BM25 算法

BM25 是 Elasticsearch 默认的相关性评分算法,它基于词频和逆文档频率计算文档相关性。BM25 公式考虑词频、文档长度、平均文档长度、词频饱和度。

BM25 的特点是词频有饱和效应,高频词的贡献不会无限增长。BM25 有两个可调参数:k1 控制词频饱和度,b 控制文档长度归一化。默认值 k1 = 1.2,b = 0.75。对于长文档,可以增加 b 值;对于短文档,可以减少 b 值。

json
{
  "query": {
    "match": {
      "content": {
        "query": "Elasticsearch 教程",
        "boost": 2.0
      }
    }
  }
}

评分调优

评分调优通过查询上下文和过滤上下文实现。查询上下文计算相关性评分,过滤上下文不计算评分但缓存结果。对于精确匹配条件,使用过滤上下文可以提高性能。

json
{
  "query": {
    "bool": {
      "must": {
        "match": { "content": "搜索" }
      },
      "filter": {
        "term": { "status": "published" }
      }
    }
  }
}

function_score 允许自定义评分函数,支持字段值、衰减函数、脚本评分。例如根据文档时间、点赞数、评论数调整评分。

json
{
  "query": {
    "function_score": {
      "query": { "match": { "content": "搜索" } },
      "field_value_factor": {
        "field": "popularity",
        "factor": 1.2,
        "modifier": "sqrt",
        "missing": 1
      }
    }
  }
}

ES 集群规划

节点规划

Elasticsearch 集群包含多种角色节点:master-eligible 节点负责集群状态管理、data 节点存储数据、coordinating 节点协调查询、ingest 节点预处理数据。生产环境建议角色分离,避免单一节点过载。

master-eligible 节点建议至少 3 个,避免脑裂。这些节点应该只做 master,不存储数据,不处理查询,保持轻量。data 节点根据数据量规划,每个节点存储 500GB-2TB 数据,过多会导致恢复慢。coordinating 节点部署在应用层或独立节点,分担查询压力。

分片规划

分片是索引的水平切分,每个分片是一个独立的 Lucene 索引。分片数量创建后不能修改,主分片数量需要提前规划。分片大小建议 10GB-50GB,过小导致分片数量多,过大会导致恢复慢。

分片数量 = 数据总量 / 分片大小。例如 1TB 数据,每个分片 20GB,需要 50 个主分片。副本分片数量可以动态调整,建议至少 1 个副本,保证高可用。时间序列索引可以按日期创建,每天一个索引,定期删除旧索引。

硬件规划

内存是 Elasticsearch 最重要的资源。JVM 堆内存建议不超过 31GB,因为超过 31GB 后指针压缩失效,内存效率下降。堆内存设置为系统内存的 50%,留出 50% 给文件系统缓存。例如 64GB 内存的服务器,堆内存设置为 31GB,文件系统缓存自动使用剩余 33GB。

磁盘选择 SSD 而非 HDD,SSD 的随机 I/O 性能是 HDD 的百倍。对于日志场景,可以考虑使用 HDD 降低成本,但查询性能会受影响。磁盘容量规划需要考虑数据增长、保留周期、副本占用,建议预留 50% 空间用于索引合并和 segment 操作。

CPU 对于写入和聚合查询重要,对于简单查询影响较小。建议至少 8 核,对于高并发或复杂聚合场景,建议 16 核或更多。

性能优化

写入优化

批量写入比单条写入快得多,Elasticsearch 支持批量 API。每批建议 1000-5000 条文档,总大小 5MB-15MB。过大的批次会消耗内存,过小的批次增加网络开销。

json
POST _bulk
{ "index": { "_index": "logs", "_id": "1" } }
{ "timestamp": "2023-01-01", "message": "..." }
{ "index": { "_index": "logs", "_id": "2" } }
{ "timestamp": "2023-01-01", "message": "..." }

refresh 间隔控制数据可搜索的延迟,默认 1 秒。对于日志场景,可以增加到 30 秒或更长,减少 segment 数量和合并压力。translog 间隔控制数据持久化的延迟,默认每次请求都刷盘。对于可以容忍数据丢失的场景,可以设置为异步,每 5 秒刷盘。

查询优化

查询优化首先使用过滤上下文而非查询上下文,避免不必要的评分计算。其次避免深度分页,使用 scroll 或 search_after 进行大量数据遍历。再次避免 wildcard 前缀通配符,这种查询会扫描所有文档。

json
{
  "query": {
    "bool": {
      "filter": [
        { "term": { "status": "published" } },
        { "range": { "created_at": { "gte": "2023-01-01" } } }
      ]
    }
  }
}

聚合查询可以使用 doc_values 而非 fielddata,避免堆内存溢出。对于高基数聚合,可以使用 cardinality 聚合的 HyperLogLog 算法,减少内存占用。

索引设计

索引设计是性能优化的基础。字段类型选择很重要:text 类型适合全文检索,keyword 类型适合精确匹配和聚合。避免过多字段,可以使用 nested 或 object 类型减少字段数量。动态映射可能推断错误,建议显式定义 mapping。

索引模板是管理索引的利器,可以预先定义 settings 和 mappings,新创建的索引自动应用模板。索引生命周期管理(ILM)可以自动管理索引的滚动、删除、收缩,适合时间序列索引。

全文检索是搜索和日志分析的基础技术,Elasticsearch 提供了完整的全文检索解决方案。通过合理的分词、评分、集群规划、性能优化,可以构建高效、可靠的搜索引擎。