elasticsearch基础
song

https://artifacts.elastic.co/releases/stack.json
https://artifacts.elastic.co/downloads/8.17.4.json
https://www.elastic.co/downloads/past-releases

索引别名(Aliases)

索引别名是 Elasticsearch 中指向一个或多个索引的逻辑名称。类似于数据库中的视图或快捷方式,它允许用户通过别名操作索引数据,而无需直接引用物理索引名称。

基本用法

  • 创建别名
  • 切换别名
  • 删除别名
  • 查询别名
  • 别名指向多个索引
  • 别名过滤数据
  • 别名路由优化

使用场景

1. 抽象化索引名称:解耦应用层与物理索引结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 创建别名并绑定到索引
POST /_aliases
{
"actions": [
{
"add": {
"index": "my_index_v1", // 物理索引名称
"alias": "my_alias" // 别名
}
}
]
}

// 应用层直接使用别名操作数据(如查询)
GET /my_alias/_search
{
"query": { "match_all": {} }
}

2.零停机维护:通过切换别名实现索引重建或迁移,无需停服。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 1. 创建新索引
PUT /my_index_v2
{
"settings": { "number_of_shards": 3 }
}

// 2. 数据迁移(将旧索引数据复制到新索引)
POST /_reindex
{
"source": { "index": "my_index_v1" },
"dest": { "index": "my_index_v2" }
}

// 3. 原子操作切换别名(删除旧索引别名,添加新索引别名)
POST /_aliases
{
"actions": [
{ "remove": { "index": "my_index_v1", "alias": "my_alias" }},
{ "add": { "index": "my_index_v2", "alias": "my_alias" }}
]
}

3.读写分离:区分写入和读取的索引目标。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 定义写别名(指向单一索引)
POST /_aliases
{
"actions": [
{
"add": {
"index": "logs_current",
"alias": "logs_write",
"is_write_index": true // 标记为写入目标
}
}
]
}

// 定义读别名(指向多个索引)
POST /_aliases
{
"actions": [
{
"add": {
"index": "logs_current",
"alias": "logs_read"
}
},
{
"add": {
"index": "logs_archive_2023",
"alias": "logs_read"
}
}
]
}

// 写入时使用写别名
POST /logs_write/_doc
{ "message": "New log entry" }

// 查询时使用读别名
GET /logs_read/_search
{ "query": { "match_all": {} }}

4.数据过滤与路由:为别名附加过滤条件或路由规则,优化查询性能。

  • 过滤条件(Filter)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    // 创建别名时附加过滤条件(仅返回 status=active 的文档)
    POST /_aliases
    {
    "actions": [
    {
    "add": {
    "index": "user_data",
    "alias": "active_users",
    "filter": { // 过滤条件
    "term": { "status": "active" }
    }
    }
    }
    ]
    }

    // 查询时自动应用过滤
    GET /active_users/_search
  • 路由规则(Routing)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    // 创建别名时指定路由规则
    POST /_aliases
    {
    "actions": [
    {
    "add": {
    "index": "orders",
    "alias": "orders_by_user",
    "routing": "user_id" // 按 user_id 路由分片
    }
    }
    ]
    }

    // 写入时指定路由值(自动路由到对应分片)
    POST /orders_by_user/_doc?routing=123
    {
    "user_id": 123,
    "product": "Laptop"
    }

    // 查询时强制路由(提升性能)
    GET /orders_by_user/_search?routing=123
    {
    "query": { "term": { "user_id": 123 }}
    }

索引设置(Index Settings)

用于配置索引的运行参数,例如分片数量、刷新间隔、缓存策略、合并策略、事务日志等。它主要关注的是索引的性能、存储、分布式特性等底层参数,与数据内容无关。

1. 分片与分配相关

  • number_of_shards:主分片数,决定数据分片方式,创建后不可更改。
  • number_of_replicas:副本分片数,影响查询性能和高可用,可动态调整。
  • index.routing.allocation:控制分片分配到特定节点。
    1
    2
    3
    4
    5
    6
    7
    8
    PUT /my_index
    {
    "settings": {
    "index.number_of_shards": 3, // 主分片数
    "index.number_of_replicas": 2, // 副本分片数
    "index.routing.allocation.include._tag": "hot" // 分配到特定标签节点
    }
    }
    动态调整分副本分片数量
    1
    2
    3
    4
    PUT /my_index/_settings
    {
    "index.number_of_replicas": 1
    }

2. 性能与写入相关

  • refresh_interval:控制数据刷新频率,影响新写入数据何时可被搜索。
  • translog:事务日志设置,控制数据持久性。
  • merge:段合并策略,优化存储和查询性能。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    PUT /my_index
    {
    "settings": {
    "index.refresh_interval": "30s", // 每30秒刷新
    "index.translog.durability": "async", // 异步持久化
    "index.translog.sync_interval": "5s", // 事务日志同步间隔
    "index.translog.flush_threshold_size": "512mb", //设置触发 translog 刷新的大小阈值。
    "index.merge.policy.max_merge_at_once": 10 // 最大合并段数
    }
    }
    禁用刷新以提升写入性能:
    1
    2
    3
    4
    PUT /my_index/_settings
    {
    "index.refresh_interval": "-1"
    }

3. 分析器与搜索相关

  • analysis:定义分词器、过滤器等,影响字段的索引和查询方式。
    • analyzer: 自定义分析器,通常由分词器和过滤器构成,用于文本预处理。
    • tokenizer 与 token filters:
      • 使用 standard 分词器实现基本分词;
      • 配置 lowercase 过滤器进行小写转换,以及 stop 过滤器进行停用词过滤;
      • char_filter 使用字符过滤器对原始的字符进行处理
  • 归一化器
    自定义分词器,并使用自定义的分词器
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    PUT my-analysis-index
    {
    "settings": {
    "analysis": {
    "analyzer": { // 定义分析器
    "my_custom_analyzer": {
    "type": "custom",
    "tokenizer": "standard", // 使用标准分词器
    "char_filter": [ // 使用字符过滤器
    "html_strip" // 移除HTML标签
    ],
    "filter": [ // 使用词元过滤器
    "lowercase", // 转小写
    "stop" // 移除停用词
    ]
    }
    },
    // 可以继续定义 tokenizer, char_filter, filter 等
    }
    },
    "mappings": {
    "properties": {
    "my_text_field": {
    "type": "text",
    "analyzer": "my_custom_analyzer" // 字段使用上面定义的分析器
    }
    }
    }
    }

4. 生命周期管理相关

索引生命周期管理(ILM)用于自动管理索引的阶段性操作。

  • index.lifecycle.name:关联 ILM 策略,控制索引滚动、删除等。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    // 定义 ILM 策略
    PUT /_ilm/policy/my_policy
    {
    "policy": {
    "phases": {
    "hot": {
    "actions": {
    "rollover": {
    "max_size": "50gb",
    "max_age": "30d"
    }
    }
    },
    "delete": {
    "min_age": "90d",
    "actions": {
    "delete": {}
    }
    }
    }
    }
    }
    // 应用到索引
    PUT /my_index
    {
    "settings": {
    "index.lifecycle.name": "my_policy"
    }
    }

5.其他核心设置

  • index.blocks:控制索引的读写权限。
  • index.priority:设置索引恢复优先级。
  • index.max_result_window:控制 from + size 的最大值,即通过分页(from, size)可以获取的最大文档数量。默认是 10000
  • index.mapping.total_fields.limit:限制一个索引中可以包含的最大字段数量(包括嵌套字段、对象字段等)。默认是 1000
    1
    2
    3
    4
    5
    6
    7
    PUT /my_index/_settings
    {
    "index.blocks.read_only": true, // 设置只读
    "index.priority": 10, // 高优先级恢复
    "index.max_result_window": 20000, // 提高窗口上限到20000
    "index.mapping.total_fields.limit": 1500 // 提高字段数量上限
    }

6.完整的settings

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
PUT /example_index
{
"settings": {
"index": {
"number_of_shards": 3,
"number_of_replicas": 1,
"refresh_interval": "1s",
"translog": {
"durability": "async",
"flush_threshold_size": "512mb"
},
"merge": {
"policy": {
"max_merge_at_once": 10,
"max_merged_segment": "5gb"
}
},
"analysis": {
"analyzer": {
"custom_analyzer": {
"tokenizer": "standard",
"filter": [
"lowercase",
"stop"
]
}
},
"tokenizer": {
"ngram_tokenizer": {
"type": "ngram",
"min_gram": 2,
"max_gram": 3,
"token_chars": [
"letter",
"digit"
]
}
}
}
}
},
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "custom_analyzer"
},
"content": {
"type": "text",
"analyzer": "custom_analyzer"
},
"publish_date": {
"type": "date"
}
}
}
}

映射(Mapping)

用于定义数据结构和字段属性,主要关注文档中数据如何存储、分析和查询。mappings 决定了字段的类型、分析方式、格式化规则等,它直接影响查询结果的准确性和搜索行为。

  • Mapping:类似数据库的 Schema,定义索引中每个字段的 数据类型分词规则索引方式等。
  • Dynamic Mapping:自动推断字段类型(如未手动定义 Mapping,Elasticsearch 会根据写入的数据自动创建字段类型)。
  • Explicit Mapping:手动明确定义字段类型和规则。

字段类型(Field Types)

1. 核心字段类型(Core Data Types)

类型名称 描述 适用场景 示例
text 全文检索字段,默认会被分词 需要分词搜索的文本(如文章内容、日志) "content": { "type": "text" }
keyword 精确值字段,不分词 精确匹配、聚合、排序(如状态码、标签) "status": { "type": "keyword" }
long 64位有符号整数 大范围整数(如订单ID) "id": { "type": "long" }
integer 32位有符号整数 中等范围整数(如年龄、数量) "age": { "type": "integer" }
short 16位有符号整数 小范围整数(如状态码) "error_code": { "type": "short" }
byte 8位有符号整数 极小范围整数(如开关状态) "flag": { "type": "byte" }
double 64位双精度浮点数 高精度数值(如科学计算) "price": { "type": "double" }
float 32位单精度浮点数 常规精度数值(如评分) "rating": { "type": "float" }
boolean 布尔值(true/false 逻辑判断字段 "is_active": { "type": "boolean" }
date 日期类型(需指定格式) 时间戳、日期范围查询 "created_at": { "type": "date", "format": "yyyy-MM-dd" }
binary 二进制数据(Base64编码) 存储加密数据或二进制文件 "file": { "type": "binary" }

2. 复杂类型(Complex Data Types)

类型名称 描述 适用场景 示例
object 嵌套的 JSON 对象 结构化数据(如用户信息对象) "author": { "type": "object", "properties": { ... } }
nested 独立存储的嵌套对象数组(解决对象数组扁平化问题) 多对一关系数据(如评论列表) "comments": { "type": "nested", "properties": { ... } }
flattened 将嵌套字段扁平化为关键字 处理未知或动态嵌套字段(避免映射爆炸) "metadata": { "type": "flattened" }
join 定义父子文档关系 一对多关系(如博客与评论) "relation": { "type": "join", "relations": { "parent": "child" } }

3. 地理位置类型(Geo Data Types)

类型名称 描述 适用场景 示例
geo_point 经纬度坐标点 地理位置搜索(如附近的人) "location": { "type": "geo_point" }
geo_shape 复杂地理形状(多边形、线等) 地理围栏、区域覆盖分析 "area": { "type": "geo_shape" }

4. 特殊类型(Specialized Data Types)

类型名称 描述 适用场景 示例
ip IPv4/IPv6 地址 分析网络日志中的 IP "client_ip": { "type": "ip" }
completion 自动补全建议字段 搜索建议功能(如输入提示) "suggest": { "type": "completion" }
token_count 统计文本字段的分词数量 分析文本长度(如统计文章字数) "word_count": { "type": "token_count", "analyzer": "standard" }
percolator 存储查询条件,用于反向匹配文档 订阅特定条件的文档(如预警系统) "query": { "type": "percolator" }
alias 字段别名(指向实际字段的虚拟字段) 简化复杂查询中的字段引用 "user": { "type": "alias", "path": "user.name" }
histogram 预聚合直方图数据(如数值分布) 存储预计算的直方图数据(提升聚合性能) "price_histogram": { "type": "histogram" }
constant_keyword 所有文档中值相同的 keyword 类型 固定分类标识(如日志来源) "env": { "type": "constant_keyword", "value": "production" }

5. 范围类型(Range Data Types)

类型名称 描述 适用场景 示例
integer_range 整数范围(如 10-20 时间段、年龄范围 "age_range": { "type": "integer_range" }
float_range 浮点数范围 价格区间、温度范围 "price_range": { "type": "float_range" }
long_range 长整数范围 ID 范围、大数值区间 "id_range": { "type": "long_range" }
double_range 双精度浮点数范围 高精度数值区间 "score_range": { "type": "double_range" }
date_range 日期范围 时间段查询(如活动有效期) "event_period": { "type": "date_range", "format": "yyyy-MM-dd" }
ip_range IP 地址范围(CIDR格式) 网络 IP 段分析 "ip_blocks": { "type": "ip_range" }

6. 其他类型(Others)

类型名称 描述 适用场景 示例
wildcard 高性能通配符搜索的 keyword 类型(ES 7.9+) 日志中的模糊匹配(如 *error* "log_message": { "type": "wildcard" }
annotated-text 支持注释的文本类型(需插件) 包含元数据的文本分析 "annotated": { "type": "annotated-text" }
search_as_you_type 支持逐字符搜索优化的文本类型(ES 7.9+) 实时搜索建议(如即时搜索框) "instant_search": { "type": "search_as_you_type" }
1
2
3
4
5
6
7
8
9
10
11
12
PUT /my_index
{
"mappings": {
"properties": {
"title": { "type": "text" },
"ip": { "type": "ip" },
"location": { "type": "geo_point" },
"price": { "type": "scaled_float", "scaling_factor": 100 },
"metadata": { "type": "flattened" }
}
}
}

动态映射(Dynamic Mapping)

Dynamic 参数

控制Elasticsearch是否自动推断新字段的类型并添加到映射中。它有三种模式:

  • true:自动推断新字段类型(默认)。
  • false:忽略新字段(不索引、不存储)。
  • strict:遇到新字段时抛出异常(严格模式,适合生产环境)。
    1
    2
    3
    4
    5
    6
    {
    "mappings": {
    "dynamic": "strict", // 禁止自动添加字段
    "properties": { ... }
    }
    }

dynamic_templates

允许用户自定义规则,动态映射新字段时根据条件(如字段名、数据类型)指定其类型和参数。每个模板包含以下部分:

  • 名称:模板的唯一标识。
  • 匹配条件:如match(字段名匹配)、match_mapping_type(匹配Elasticsearch推断的类型)、path_match(嵌套字段路径匹配)。
  • 映射规则:定义字段的最终类型和参数。

    [!Note] 提示

    • 模板按顺序匹配,第一个满足条件的模板生效
    • dynamic设为falsestrict,动态模板不会生效。

所有字符串字段设为text,并添加子字段keyword

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
PUT my-index-000001/
{
"mappings": {
"dynamic_templates": [
{
"strings_as_text_with_keyword": {
"match_mapping_type": "string",
"mapping": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
]
}
}

特定前缀的字段设为text并分词

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"dynamic_templates": [
{
"text_fields": {
"match": "txt_*",
"mapping": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
]
}

长数字字段映射为keyword

1
2
3
4
5
6
7
8
9
10
11
12
{
"dynamic_templates": [
{
"longs_as_keywords": {
"match_mapping_type": "long",
"mapping": {
"type": "keyword"
}
}
}
]
}

嵌套字段的特殊处理

1
2
3
4
5
6
7
8
9
10
11
12
 {
"dynamic_templates": [
{
"nested_fields": {
"path_match": "user.*",
"mapping": {
"type": "keyword"
}
}
}
]
}

索引选项(Indexing Options)

全局参数

  • enable
  • coerce

字段参数

  • **index**:是否索引字段(true/false)。禁用索引可节省存储,但无法搜索。
  • **doc_values**:是否启用列式存储(默认 true,用于排序、聚合)。
  • **store**:是否独立存储原始值(默认 false,仅在需要独立提取字段时启用)。
  • **analyzer**:定义全文检索字段的分词规则(如 standardik_smart)。
  • **search_analyzer**:指定搜索时的分词器(默认与 analyzer 一致)。
  • **fields**:多字段特性(允许一个字段同时以不同方式索引,如 text + keyword)。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    {
    "mappings": {
    "properties": {
    "log": {
    "type": "text",
    "index": false // 不索引,仅存储
    }
    }
    }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"mappings": {
"properties": {
"content": {
"type": "text",
"analyzer": "ik_max_word", // 写入时使用细粒度分词
"search_analyzer": "ik_smart", // 搜索时使用粗粒度分词
"fields": {
"keyword": { // 同时保留不分词的版本
"type": "keyword"
}
}
}
}
}
}

元字段(Meta Fields)

  • _id:文档唯一标识。
  • _source:存储原始 JSON 数据(禁用可节省空间,但无法直接获取原始数据)。
  • _routing:自定义路由规则(控制文档存储到特定分片)。
  • _all:已废弃,替代方案为 copy_to 字段。
    1
    2
    3
    4
    5
    6
    7
    {
    "mappings": {
    "_source": {
    "enabled": false // 禁用原始数据存储
    }
    }
    }

总结

Elasticsearch Mapping 的设计直接影响数据的 存储效率查询性能 和 功能实现。核心关注点包括:

  • 明确字段类型(如 text vs keyword)。
  • 合理控制动态映射(dynamic)。
  • 优化分词规则(analyzer 和 fields)。
  • 利用高级特性(如 nestedcopy_to、模板)。
  • 对不搜索的字段设置 "index": false
  • 避免过度使用 nested 类型(增加查询复杂度)。
  • 对需聚合或排序的字段启用 doc_values(默认开启)。

索引模板(Index Template)

索引模板是一个预定义的“蓝图”,它规定了当一个新创建的索引名称与模板中定义的模式(index_patterns)匹配时,应该自动应用哪些设置(Settings)映射(Mappings)别名(Aliases)

核心目的:

  • 自动化: 自动配置新索引,无需每次手动指定。
  • 一致性: 确保同类型的索引(如所有日志索引、所有指标索引)具有统一的结构和配置。
  • 简化管理: 集中管理索引的通用配置。

基础模板

  • 索引匹配模式(index_patterns) 定义模板作用的索引名称模式(例如 "log-*" 表示所有以 log- 开头的索引)。
  • 设置(settings) 如分片数、副本数、刷新间隔等。
  • 映射(mappings) 定义字段的数据类型、分词器、动态模板等。
  • 别名(aliases) 为新建索引自动配置别名,便于后续操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
PUT _index_template/my_template
{
    "index_patterns": [
        "log-*"
    ],
    "template": {
        "settings": {
            "number_of_shards": 1,
            "number_of_replicas": 0
        },
        "aliases": {
            "logs": {
            }
        },
        "mappings": {
            "properties": {
                "timestamp": {
                    "type": "date"
                },
                "message": {
                    "type": "text",
                    "analyzer": "standard"
                },
                "user": {
                    "type": "keyword"
                }
            }
        }
    }
}

当你向一个不存在但名称匹配 "log-*" 的索引写入数据时,Elasticsearch 会自动根据此模板创建索引。

组件模板(Component Templates)

将可复用的配置模块化,多个组件模板可组合成一个索引模板。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 1. 创建组件模板(定义公共设置)
PUT /_component_template/common_settings
{
"template": {
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1
}
}
}

// 2. 创建组件模板(定义日志字段映射)
PUT /_component_template/logs_mappings
{
"template": {
"mappings": {
"properties": {
"@timestamp": { "type": "date" },
"message": { "type": "text" }
}
}
}
}

// 3. 组合组件模板生成索引模板
PUT /_index_template/logs_template
{
"index_patterns": ["logs-*"],
"priority": 100,
"composed_of": ["common_settings", "logs_mappings"], // 引用组件模板
"template": {
"aliases": { "logs-all": {} }
}
}

动态模板(Dynamic Templates)

动态模板允许你针对符合特定条件的字段定义统一的映射规则,而不必对每个字段单独定义。比如,将所有自动检测为字符串的字段映射为 keyword 类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
PUT _index_template/dynamic_template_example
{
  "index_patterns": ["log1-*"],
  "template": {
    "mappings": {
      "dynamic_templates": [
        {
          "strings_as_keyword": {
            "match_mapping_type": "string",
            "mapping": {
              "type": "text",
              "fields":{
                "keyword":{
                  "type":"keyword"
                }
              }
            }
          }
        }
      ]
    }
  }
}

此模板会使得所有索引名称匹配 "log1-*" 的索引中,自动映射的字符串字段都采用 text 类型,并添加子字段keyword便于精确匹配和聚合。

  • match_mapping_type: "string" 表示匹配所有自动检测为字符串的字段。
  • mapping:
    • "type": "text":默认将字段映射为 text 类型(用于全文检索)。
    • "fields":为该字段添加子字段:
      • 子字段 "keyword" 映射为 keyword 类型,常用于排序、聚合和精确匹配。

创建log1-20250414索引,并添加数据

1
2
3
4
5
POST log1-20250414/_doc/1
{
  "title": " test title",
  "desc": "xxxxxxxxxxxxxx"
}

GET log1-20250414/_mapping 后返回

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
{  
    "log1-20250414": {
        "mappings": {
            "dynamic_templates": [
                {
                    "strings_as_keyword": {
                        "match_mapping_type": "string",
                        "mapping": {
                            "fields": {
                                "keyword": {
                                    "type": "keyword"
                                }
                            },
                            "type": "text"
                        }
                    }
                }
            ],
            "properties": {
                "desc": {
                    "type": "text",
                    "fields": {
                        "keyword": {
                            "type": "keyword"
                        }
                    }
                },
                "title": {
                    "type": "text",
                    "fields": {
                        "keyword": {
                            "type": "keyword"
                        }
                    }
                }
            }
        }
    }
}

分词器(Analyzer)

分词器(Analyzer)是 Elasticsearch 中用于将文本转换为可搜索词条(Tokens)的核心组件,作用于文档的索引和搜索阶段。它负责将原始文本(如句子、段落)拆解成有意义的词项,以便构建倒排索引,从而实现高效的全文检索。

分词器的组成

分词器由三个层级组件构成,按顺序处理文本:

  • 字符过滤器(Character Filters)
    处理原始文本的字符,例如移除HTML标签(html_strip)、替换特殊字符(如将 & 转为 and)。
  • 分词器(Tokenizer)
    将文本切分为词条,例如按空格分割(whitespace)或按词语边界分割(standard)。
  • 词条过滤器(Token Filters)
    对分词后的词条进一步处理,如转小写(lowercase)、删除停用词(stop)、添加同义词(synonym)等。

字符过滤器(Character Filters)

分词器(Tokenizer)

词条过滤器(Token Filters)

自定义分词器

归一化器(Normalizer)

  • Normalizer 只能用于 keyword 类型的字段,不能用于 text 字段。

Normalizer的组成

  • Char Filters:(可选)先处理原始字符串(比如删掉某些字符)
  • Token Filters:(必需)在字符级别继续处理,比如转小写、去除重音符号等。

Normalizer工作原理

当你把一个字符串存到带 Normalizer 的 keyword 字段时:

  1. 先用 char_filter 预处理原字符串(可选)。
  2. 然后用 filter 处理(比如小写化)。
  3. 最后存进去一整个处理好的字符串。

自定义normalizer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
PUT my_index
{
"settings": {
"analysis": {
"normalizer": {
"my_lowercase_normalizer": {
"type": "custom",
"char_filter": [],
"filter": ["lowercase"]
}
}
}
},
"mappings": {
"properties": {
"email": {
"type": "keyword",
"normalizer": "my_lowercase_normalizer"
}
}
}
}

其他

在linux上快速启动测试模式es单节点

  1. 用户准备

    1
    2
    adduser es
    password es
  2. 为用户添加sudo权限 在/etc/sudoers添加

    1
    es      ALL=(ALL)       ALL
  3. 下载elasticsearch

    1
    2
    3
    4
    5
    6
    7
    mkdir /software
    cd /software
    wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.17.4-linux-x86_64.tar.gz
    tar -zxvf elasticsearch-8.17.4-linux-x86_64
    chown -R es:es elasticsearch-8.17.4
    su es ## 切换到es用户

  4. 调整elasticsearch.yml配置

    1
    2
    3
    4
    network.host: 0.0.0.0
    discovery.type: single-node
    xpack.security.enabled: false
    bootstrap.memory_lock: true
  5. jvm.options

    1
    2
    3
    4
    -Xms512m
    -Xmx512m
    # 强制开启bootstrap checks
    -Des.enforce.bootstrap.checks=true
  6. 运行启动命令

    1
    2
    cd /software/elasticsearch-8.17.4
    ./bin/elasticsearch

下载配置kibana

1
2
3
4
5
6
su root ##切换到root用户
cd /software
wget https://artifacts.elastic.co/downloads/kibana/kibana-8.17.4-linux-x86_64.tar.gz
tar -zxvf kibana-8.17.4-linux-x86_64.tar.gz
cd kibana-8.17.4-linux-x86_64.tar.gz
vim config/kibana.yml
1
2
3
4
5
6
7
8
server.port: 5601
server.host: "0.0.0.0"
elasticsearch.hosts: ["http://localhost:9200"]
i18n.locale: "zh-CN"

xpack.encryptedSavedObjects.encryptionKey: 959531dc87926c3477e33f3aafa81c7d
xpack.reporting.encryptionKey: 28712e8728f370c7e658088c5bde8bee
xpack.security.encryptionKey: d2af5cf0c81be1eb5d9ff4f1e169fc9c

启动kibana

1
2
3
cd ..
nohup ./bin/kibana --allow-root > nohup.out 2>&1 &
tail -f nohup.out

[!Note] nohup
nohup 是 Linux/Unix 系统中的一个命令,全称为 No Hang Up(不挂起)。它的作用是让程序在终端关闭(用户退出登录或断开连接)后依然保持运行,避免进程被系统终止(默认情况下,关闭终端会向相关进程发送 SIGHUP 信号,导致进程退出)。

1
>nohup <command> > nohup.out 2>&1 &
  • <command> 是你需要运行的命令(如脚本、程序等)。
  • & 表示将命令放到后台运行,释放当前终端。
  • > output.log:将标准输出重定向到 output.log
  • 2>&1:将标准错误也重定向到同一文件。

elasticsearch安装ik插件
https://github.com/infinilabs/analysis-ik
https://release.infinilabs.com/analysis-ik/stable/

1
2
3
4
cd /software/elasticsearch-8.17.4/plugins
wget https://release.infinilabs.com/analysis-ik/stable/elasticsearch-analysis-ik-8.17.4.zip
unzip elasticsearch-analysis-ik-8.17.4.zip -d analysis-ik
rm -rf ./elasticsearch-analysis-ik-8.17.4.zip

重启elasticsearch

elasticsearch 数组和普通对象的区别

数组
  • 数组是 同一字段的多个值的集合,例如:tags: ["search", "database", "elasticsearch"]
  • 无需显式声明:Elasticsearch 不要求字段显式定义为数组类型,任何字段都可以包含多个值,自动被视为数组。
  • 数组中的每个元素会被 独立索引(扁平化存储(Flattened)),但元素间的顺序和关联性会丢失。
  • 匹配任意元素:查询数组字段时,只要文档的数组中包含任意一个匹配值,就会被命中。
    1
    2
    3
    4
    {
    "user_id": 1,
    "tags": ["search", "database", "elasticsearch"]
    }
    实际存储的倒排索引类似于:
    1
    2
    3
    "search" → doc1  
    "database" → doc1
    "elasticsearch" → doc1
普通对象
  • 对象是 键值对的集合,用于表示结构化数据
  • 隐式映射:Elasticsearch 会自动将内层对象映射为 object 类型。
  • 对象的字段会被 扁平化(Flattened)存储,但保留层级关系。
  • 必须通过 完整路径 访问对象内的字段。
  • 如果对象字段的值是多个对象(即对象数组),Elasticsearch 会将其 隐式合并,导致数据丢失
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    {
    "user": [
    { "name": "Alice", "age": 30 },
    { "name": "Bob", "age": 25 }
    ]
    }
    ### 实际存储为:
    ### "user.name": ["Alice", "Bob"]
    ### "user.age": [30, 25]
    ### 问题:无法区分 `Alice` 对应 `age:30`,`Bob` 对应 `age:25`。
特性 数组(Array) 普通对象(Object)
数据结构 同一字段的多值集合(扁平化存储) 键值对的集合(层级化存储)
元素关系 元素独立,无关联性 字段间有关联性(如 user.name 和 user.age
多值对象处理 不适合(需用 nested 类型) 隐式合并字段值,导致数据关联性丢失
查询方式 直接匹配任意元素 需指定字段路径(如 user.name
适用场景 标签、分类等多值字段 结构化数据(如用户信息、地址)

关联关系数据实践

Object: (一对一) 优先考虑反范式化( Denormalization
  • 将关联数据冗余存储到主文档中,避免查询时进行跨文档关联。
  • 读多写少、数据更新频率低的场景(如商品信息中嵌入分类名称)。
  • 查询速度快,无需额外操作。数据冗余,更新时需要同步修改所有相关文档。
    1
    2
    3
    4
    5
    6
    7
    8
    {
    "product_id": 1,
    "name": "Laptop",
    "category": {
    "id": 10,
    "name": "Electronics" // 直接冗余分类名称
    }
    }
嵌套对象(Nested Objects): (一对多,数组)当数据包含多个数值对象,同时有查询需求
  • 将一对多关系的数据作为子对象嵌入主文档,使用 nested 类型存储。
  • 一对多关系且子对象需要独立查询(如订单中的多个商品项)。
  • 子对象可独立查询,避免数据扁平化冲突。更新嵌套数据需重建整个文档,性能较低。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    ### 定义 `nested` 类型的字段:
    {
    "mappings": {
    "properties": {
    "order_items": {
    "type": "nested" // 声明为嵌套类型
    }
    }
    }
    }

    ### 插入数据
    {
    "order_id": 100,
    "order_items": [
    { "product": "Keyboard", "price": 50 },
    { "product": "Mouse", "price": 20 }
    ]
    }

    ### 使用 `nested` 查询
    {
    "query": {
    "nested": {
    "path": "order_items",
    "query": {
    "term": { "order_items.product": "Keyboard" }
    }
    }
    }
    }
父子文档(Parent-Child Relationships):(一对多,数组)关联文档更新非常频繁
  • 通过 join 字段建立父子文档关联,子文档与父文档存储在同一个分片
  • 一对多关系且子文档频繁更新(如博客文章与评论)。
  • 父子文档可独立更新。查询性能较低,需谨慎设计分片。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
### 定义 `join` 字段:
{
"mappings": {
"properties": {
"post_comment": {
"type": "join",
"relations": {
"post": "comment" // post 是父,comment 是子
}
}
}
}
}
### 插入父文档
{
"post_id": 1,
"title": "Elasticsearch Guide",
"post_comment": {
"name": "post" // 标记为父文档
}
}
### 插入子文档(需指定父文档 ID)
{
"comment_id": 1,
"text": "Very helpful!",
"post_comment": {
"name": "comment",
"parent": 1 // 父文档 ID
}
}
### 查询子文档
{
"query": {
"has_parent": {
"parent_type": "post",
"query": { "match": { "title": "Elasticsearch" } }
}
}
}
应用层关联(Application-Side Joins)(多对多)在应用代码中通过多次查询 Elasticsearch(或结合其他数据源)实现多对多关系的数据关联。
  • 数据分散在不同索引或系统中。
  • 需要灵活控制查询逻辑(如分页、过滤、排序)。需多次查询,增加网络延迟和代码复杂度。
  • 多对多关系的数据量较大,不适合直接存储冗余。数据一致性需自行维护(如用户退群需更新两处)。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    ## 用户索引 users
    {
    "user_id": 101,
    "name": "Alice",
    "group_ids": [1, 2] // 用户加入的群组 ID 列表
    }
    ## 群组索引 groups
    {
    "group_id": 1,
    "name": "Elasticsearch Fans",
    "members": [101, 102] // 群组成员 ID 列表
    }
    ## 下查users索引 id=101的用户加入的群组,后根据结果再次查询groups信息

    ## **查询用户加入的群组 ID 列表**:
    GET /users/_search
    {
    "query": { "term": { "user_id": 101 } },
    "_source": ["group_ids"] // 仅返回 group_ids 字段
    }
    ## 响应结果: {"group_ids": [1, 2]}
    ## **根据群组 ID 列表查询群组详细信息**:
    GET /groups/_search
    {
    "query": {
    "terms": {
    "group_id": [1, 2] // 使用上一步的 group_ids
    }
    }
    }
Terms 查询(Terms Lookup)(多对多)通过 Elasticsearch 的 Terms Lookup 功能,直接从另一个文档中动态获取字段值,作为当前查询的条件。
  • 多对多关系的双方数据均在 Elasticsearch 中。Terms Lookup 需要从另一个索引实时读取数据,可能影响查询速度。
  • Elasticsearch 默认缓存 Terms Lookup 的结果,但频繁更新的字段会导致缓存频繁失效。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    GET /groups/_search
    {
    "query": {
    "terms": {
    "group_id": {
    "index": "users", // 从 users 索引获取数据
    "id": "101", // 用户 ID
    "path": "group_ids" // 提取用户文档的 group_ids 字段
    }
    }
    }
    }
    ## 此查询等价于:SELECT * FROM groups WHERE group_id IN (SELECT group_ids FROM users WHERE user_id = 101);
方法 适用场景 优点 缺点
反规范化 读多写少,数据更新不频繁 查询速度快 数据冗余,更新成本高
嵌套对象 一对多,子对象需独立查询 避免数据扁平化 更新需重建文档
父子文档 子文档频繁更新 父子独立更新 查询性能较低
应用层关联 数据分散在不同系统 灵活性高 网络开销大,延迟高
Terms 查询 动态关联少量数据 无需预存关联数据 性能依赖外部索引

文档字段问题

  • 避免过多字段
    • 默认最大字段数1000,可设置index.mapping.total_fields.limit限定最大字段数
  • 生产环境Dynamic问题
    • true: 未知字段会u被自动加入索引
    • false: 新字段不会被索引,但会保持在_source
    • strict: 新字段不会被索引,文档写入失败(报错)

乐观锁

倒排索引

Elasticsearch 的倒排索引通过将文档内容转换为词项(Term)并建立词项到文档的映射,实现快速搜索。

倒排索引实现步骤

  1. 文档分析与分词
    • 对文本进行分词、小写化、去除停用词(如 “is”, “a”)等处理。
  2. 生成词项字典
    • 存储所有唯一词项,使用高效数据结构(如 FST)加速查找。
  3. 构建倒排列表
    • 记录每个词项对应的文档 ID、词频(TF)、位置(Position)等信息。
  4. 存储优化
    • 使用压缩技术(如增量编码、位压缩)减少存储空间。

具体示例

假设有两个文档:

  • 文档 1(ID=1)"Elasticsearch is a distributed search engine."
  • 文档 2(ID=2)"Distributed systems are complex but interesting."

步骤 1:分析处理

  • 分词并小写化,去除停用词后:
    • 文档 1:["elasticsearch", "distributed", "search", "engine"]
    • 文档 2:["distributed", "systems", "complex", "interesting"]

步骤 2:构建倒排索引

Term 文档 ID 词频(TF) 位置(Positions)
elasticsearch 1 1 [0]
distributed 1 1 [1]
distributed 2 1 [0]
search 1 1 [2]
engine 1 1 [3]
systems 2 1 [1]
complex 2 1 [2]
interesting 2 1 [3]
步骤 3:存储结构优化
  • 词项字典:按字母排序(如 complexdistributedelasticsearch…),便于二分查找。
  • 倒排列表:合并相同词项的文档,例如 distributed 的倒排列表为:
    1
    2
    3
    Term: distributed
    -> Doc 1: TF=1, Positions: [1]
    -> Doc 2: TF=1, Positions: [0]

搜索过程示例

当搜索关键词 "distributed" 时:

  1. 在词项字典中找到 distributed
  2. 获取其倒排列表,得到文档 ID 1 和 2。
  3. 根据词频、位置等计算相关度(如 BM25 算法),返回结果。

相关性评分(boost)

  • Index Boost
  • boosting
  • negative_boost
  • function_socre
  • rescore_query
  • dis max query
  • best_fields
由 Hexo 驱动 & 主题 Keep