第一章:Elasticsearch 概述
1.1 什么是 Elasticsearch
| 概念名称 | 说明 | 注意事项 |
|---|---|---|
| Elasticsearch | 一个开源的分布式搜索和分析引擎,基于 Apache Lucene 构建,专为全文检索、结构化搜索、聚合分析等场景设计。支持近实时(Near Real-Time)数据索引与查询。 | 不是传统关系型数据库,不支持事务(ACID),也不适合频繁更新单条记录的 OLTP 场景。 |
| 近实时(NRT) | 文档索引后通常在 1 秒内可被搜索到,而非立即可见。这是通过 refresh 机制实现的。 | 默认 refresh_interval 为 1s,可通过配置调整,但会影响性能。 |
| 分布式架构 | 数据自动分片(Shard)并分布到集群多个节点,支持水平扩展、高可用和负载均衡。 | 初次使用单机模式时仍具备分布式能力,只是所有分片运行在同一节点。 |
1.2 Elasticsearch 的核心优势
| 优势名称 | 说明 | 注意事项 |
|---|---|---|
| 全文检索能力强 | 基于 Lucene,支持复杂的文本分析、相关性评分(TF-IDF / BM25)、高亮、同义词、模糊匹配等。 | 需合理配置 analyzer 和 mapping,否则中文等语言效果不佳。 |
| 高性能与低延迟 | 支持毫秒级响应的海量数据查询,尤其适合日志、监控、电商等场景。 | 性能依赖硬件、分片数、查询复杂度,不当配置可能导致 OOM 或慢查询。 |
| 水平扩展性 | 可通过增加节点线性扩展存储容量与查询吞吐量。 | 主分片数量在索引创建后不可更改,需提前规划。 |
| 聚合分析能力 | 提供丰富的聚合(Aggregations)功能,如指标聚合、桶聚合、管道聚合,适用于 BI 和数据分析。 | 深度聚合可能消耗大量内存,建议限制 size 或使用 composite 聚合分页。 |
| RESTful API | 所有操作通过 HTTP/JSON 接口完成,易于集成和调试。 | 生产环境务必启用安全认证(如 TLS、RBAC),避免未授权访问。 |
1.3 Elasticsearch 与传统数据库对比
| 对比维度 | Elasticsearch | 传统关系型数据库(如 MySQL) |
|---|---|---|
| 数据模型 | 无固定 schema,文档为 JSON 格式,字段可动态添加(动态映射)。 | 严格 schema,表结构需预先定义,修改成本高。 |
| 查询能力 | 擅长全文检索、模糊匹配、相关性排序;不支持 JOIN(除 join 类型字段外)。 | 擅长精确查询、事务处理、多表 JOIN;全文检索能力弱(如 MySQL 的 LIKE 效率低)。 |
| 一致性 | 最终一致性(写入后短暂延迟才可查),非强一致性。 | 通常提供强一致性(ACID),适合金融等关键业务。 |
| 事务支持 | 不支持事务,单文档写入是原子的,但跨文档无事务。 | 支持完整 ACID 事务。 |
| 扩展方式 | 水平扩展(分片 + 副本),天然分布式。 | 垂直扩展为主,水平分库分表需应用层支持,复杂度高。 |
| 适用场景 | 日志分析、商品搜索、推荐系统、监控告警等读多写少、高并发查询场景。 | 订单系统、用户账户、财务系统等需要强一致性和事务的 OLTP 场景。 |
⚠️ 注意:Elasticsearch 不能替代数据库,常作为数据库的”搜索加速层”或”分析副仓”。
1.4 Elastic Stack 生态简介(ELK)
| 组件名称 | 说明 | 注意事项 |
|---|---|---|
| Elasticsearch | 核心搜索引擎,负责存储、索引、搜索和分析数据。 | 是整个栈的数据中枢,资源消耗最大。 |
| Logstash | 数据采集与处理管道,支持从多种源(文件、Kafka、DB 等)输入,经 filter 处理后输出到 ES 或其他目标。 | 资源占用较高,轻量级场景可用 Filebeat 替代。 |
| Kibana | 可视化平台,提供仪表盘、图表、Dev Tools、监控、告警等功能。 | 需与 ES 版本严格兼容,建议使用相同主版本号。 |
| Beats | 轻量级数据采集器家族(如 Filebeat、Metricbeat、Auditbeat),直接发送数据到 ES 或 Logstash。 | 每个 Beat 专注一类数据,部署简单,适合边缘采集。 |
| Elastic Agent + Fleet | 新一代统一代理管理方案(8.x 推荐),替代部分 Beats + Logstash 场景,支持集中策略管理。 | 需配合 Kibana 的 Fleet 界面使用,学习曲线略陡。 |
✅ 典型数据流:Filebeat → Kafka(可选)→ Logstash(清洗)→ Elasticsearch ← Kibana(可视化)
🔒 安全组件(X-Pack)已集成到基础发行版中,包含认证、授权、加密、审计等功能。
第二章:环境搭建与快速入门
2.1 安装 Elasticsearch(Linux / Windows / macOS)
| 步骤名称 | 操作细节 | 注意事项 |
|---|---|---|
| 系统要求确认 | 确保系统已安装 Java 17 或 Java 21(Elasticsearch 8.x 要求)。可通过 java -version 验证。 | 不支持 Java 8/11(8.x 版本起)。建议使用官方 JDK(如 Temurin)。 |
| 下载安装包 | 访问 https://www.elastic.co/downloads/elasticsearch ,选择对应操作系统版本(.tar.gz / .zip / .deb / .rpm / MSI)。 | 生产环境推荐使用 .tar.gz 或包管理器(如 yum/apt);Windows 可用 MSI 图形化安装。 |
| Linux 安装(tar.gz) | 解压:tar -xzf elasticsearch-8.x.x-linux-x86_64.tar.gz进入目录: cd elasticsearch-8.x.x | 默认以普通用户运行,禁止 root 启动(需新建 es 用户并授权目录权限)。 |
| Windows 安装(MSI) | 双击 MSI 安装包,按向导完成安装,自动注册为 Windows 服务。 | 安装过程中会生成默认密码和 Kibana enrollment token,务必保存。 |
| macOS 安装(Homebrew) | 执行:brew install elasticsearch或使用 tar.gz 包手动解压。 | Homebrew 安装路径通常为 /opt/homebrew/Cellar/elasticsearch/...,需手动配置环境变量。 |
| 配置文件位置 | 主配置文件:config/elasticsearch.ymlJVM 配置: config/jvm.options | 初次安装无需修改即可启动;生产环境需调整 heap size(建议不超过 31GB 且 ≤ 物理内存 50%)。 |
2.2 启动与验证服务
| 步骤名称 | 操作细节 | 注意事项 |
|---|---|---|
| 启动服务(Linux/macOS) | 在 Elasticsearch 目录下执行:./bin/elasticsearch(前台运行)或 ./bin/elasticsearch -d(后台运行) | 首次启动会自动生成证书、密码和 enrollment token,注意记录控制台输出。 |
| 启动服务(Windows) | 若使用 MSI 安装,服务自动启动;否则运行:.\bin\elasticsearch.bat | 可通过”服务”管理器查看 “Elasticsearch” 服务状态。 |
| 验证服务是否运行 | 打开终端执行:curl -X GET "http://localhost:9200/"或浏览器访问 http://localhost:9200 | 默认启用安全功能(8.x),首次请求需提供用户名密码(如 elastic + 自动生成的密码)。 |
| 获取初始密码 | 若未记录初始密码,可重置:./bin/elasticsearch-reset-password -u elastic | 仅限本地运行且有文件系统访问权限时可用。 |
| 关闭服务 | Linux/macOS:kill $(cat pid) 或 Ctrl+C(前台)Windows:停止服务或关闭命令行窗口 | 强制 kill 可能导致数据未刷盘,建议正常关闭。 |
2.3 安装与配置 Kibana
| 步骤名称 | 操作细节 | 注意事项 |
|---|---|---|
| 下载 Kibana | 从 https://www.elastic.co/downloads/kibana 下载与 Elasticsearch 相同版本的安装包。 | 版本必须严格匹配,否则可能无法连接或功能异常。 |
| Linux/macOS 安装 | 解压:tar -xzf kibana-8.x.x-linux-x64.tar.gz进入目录: cd kibana-8.x.x | 配置文件位于 config/kibana.yml。 |
| Windows 安装 | 使用 MSI 安装包,图形化向导完成安装,自动配置基本参数。 | 安装过程中会提示输入 Elasticsearch 地址和 enrollment token。 |
| 配置连接 ES | 编辑 kibana.yml,设置:elasticsearch.hosts: ["http://localhost:9200"]server.port: 5601 | 若 ES 启用了 HTTPS(8.x 默认),需使用 https:// 并配置证书信任。 |
| 使用 enrollment token(8.x) | 在 Kibana 首次启动时,需在终端执行:./bin/kibana --enrollment-token <your_token> | token 在 ES 首次启动日志中生成,格式如:AAAABBBBCCCC… |
| 启动 Kibana | 执行:./bin/kibana(Linux/macOS)或 .\bin\kibana.bat(Windows) | 启动后访问 http://localhost:5601,使用 elastic 用户登录。 |
2.4 使用 Dev Tools 执行 REST API
| 步骤名称 | 操作细节 | 注意事项 |
|---|---|---|
| 访问 Dev Tools | 登录 Kibana → 左侧菜单 → “Developer” → “Dev Tools” | 需要具备 kibana_admin 或 superuser 角色权限。 |
| 基本 API 格式 | 在 Console 输入区输入:GET /点击 ▶️ 或按 Ctrl+Enter 执行 | 支持自动补全、语法高亮、历史记录。 |
| 创建索引示例 | 输入:PUT /my_index{ "settings": { "number_of_shards": 1 }} | 索引名必须小写,不能含特殊字符(如 _、- 仅限特定位置)。 |
| 插入文档示例 | 输入:POST /my_index/_doc/1{ "title": "Hello ES", "content": "First document"} | _doc 是文档类型(8.x 已废弃类型概念,统一用 _doc)。 |
| 查询文档示例 | 输入:GET /my_index/_doc/1 | 返回结果包含 _source 字段(原始 JSON 文档)。 |
| 删除索引示例 | 输入:DELETE /my_index | 删除不可逆,生产环境务必谨慎。 |
| 多行请求支持 | 可在同一 Console 中连续写多个请求,用空行分隔:GET /_cluster/healthGET /_cat/indices?v | 每次只能执行一个请求(点击光标所在请求块)。 |
✅ 提示:Dev Tools 是学习和调试 Elasticsearch 最便捷的工具,所有 REST API 均可通过它测试。
第三章:索引与文档管理
3.1 索引(Index)的基本操作(创建、查看、删除)
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 创建索引 | PUT /<index_name> | 创建一个新索引,可指定 settings 和 mappings | PUT /products{ "settings": { "number_of_shards": 2, "number_of_replicas": 1 }} | 索引名必须全小写;主分片数(shards)创建后不可更改;若未指定 settings,使用默认值(通常 shards=1, replicas=1)。 |
| 查看索引信息 | GET /<index_name> | 获取索引的 mappings 和 settings | GET /products | 若索引不存在,返回 404 错误。 |
| 列出所有索引 | GET /_cat/indices?v | 以表格形式列出集群中所有索引的状态、文档数、存储大小等 | GET /_cat/indices?v | ?v 表示显示列标题;也可用 GET /_all 获取 JSON 格式元数据。 |
| 检查索引是否存在 | HEAD /<index_name> | 快速判断索引是否存在(无响应体,仅状态码) | HEAD /products | 返回 200 表示存在,404 表示不存在;适合程序判断。 |
| 删除索引 | DELETE /<index_name> | 删除整个索引及其所有数据 | DELETE /products | 不可逆操作;支持通配符(如 DELETE /log-*),但需谨慎;生产环境建议关闭自动创建索引(action.auto_create_index: false)。 |
3.2 文档(Document)的 CRUD 操作
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 创建文档(指定 ID) | PUT /<index>/_doc/<id> | 创建或全量替换指定 ID 的文档 | PUT /products/_doc/101{ "name": "Laptop", "price": 5999} | 若 ID 已存在,则覆盖原文档(版本号 +1);ID 可为数字或字符串。 |
| 创建文档(自动生成 ID) | POST /<index>/_doc | 插入新文档,由 ES 自动生成唯一 ID | POST /products/_doc{ "name": "Mouse", "price": 99} | 返回结果中包含 _id 字段;适用于日志等无需业务 ID 的场景。 |
| 获取文档 | GET /<index>/_doc/<id> | 根据 ID 获取完整文档 | GET /products/_doc/101 | 返回包含 _index、_id、_version、_source 等字段;若文档不存在,返回 404。 |
| 更新文档(全量替换) | PUT /<index>/_doc/<id> | 同创建文档,用于覆盖更新 | PUT /products/_doc/101{ "name": "Gaming Laptop", "price": 8999} | 实际是”删除+重建”,旧版本保留用于并发控制。 |
| 更新文档(部分更新) | POST /<index>/_update/<id> | 仅更新指定字段,保留其他字段 | POST /products/_update/101{ "doc": { "price": 7999 }} | 需包裹在 "doc" 对象中;若字段不存在则新增;不支持对数组整体替换(需 script)。 |
| 删除文档 | DELETE /<index>/_doc/<id> | 删除指定 ID 的文档 | DELETE /products/_doc/101 | 文档标记为删除,实际物理删除在段合并时完成;返回 _version 和 result: deleted。 |
| 检查文档是否存在 | HEAD /<index>/_doc/<id> | 快速判断文档是否存在 | HEAD /products/_doc/101 | 返回 200 存在,404 不存在;无响应体,节省带宽。 |
3.3 批量操作(Bulk API)
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 执行批量操作 | POST /_bulk 或 POST /<index>/_bulk | 一次请求执行多个 index/create/update/delete 操作 | POST /_bulk{ "index": { "_index": "logs", "_id": "1" } }{ "timestamp": "2025-01-01", "msg": "info" }{ "delete": { "_index": "logs", "_id": "2" } } | 每行必须是独立 JSON;动作行(如 index)和数据行交替出现;末尾必须有换行符;单次 bulk 建议 5–15 MB 数据或 1000–5000 条记录。 |
| 批量索引(指定索引) | POST /<index>/_bulk | 省略动作中的 _index 字段 | POST /products/_bulk{ "index": { "_id": "201" } }{ "name": "Keyboard", "price": 199 }{ "index": { "_id": "202" } }{ "name": "Monitor", "price": 1299 } | 所有操作作用于同一索引,简化请求体。 |
| 批量更新 | 使用 _update 动作 | 批量执行部分更新 | POST /products/_bulk{ "update": { "_id": "201" } }{ "doc": { "price": 179 } } | update 动作的数据行必须包含 "doc" 或 "script"。 |
| 批量创建(仅当不存在) | 使用 create 动作 | 仅当文档 ID 不存在时才插入 | POST /products/_bulk{ "create": { "_id": "301" } }{ "name": "Tablet", "price": 2999 } | 若 ID 已存在,该操作失败(不会覆盖),但其他操作继续执行。 |
| 错误处理 | 查看响应中的 errors 字段 | 判断哪些操作失败 | 响应示例:{ "errors": true, "items": [...] } | 即使部分失败,其他操作仍会执行;需遍历 items 检查每个操作状态。 |
3.4 索引别名(Alias)管理
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 创建别名 | POST /_aliases | 为一个或多个索引添加别名 | POST /_aliases{ "actions": [ { "add": { "index": "products_v1", "alias": "products" } } ]} | 别名可指向多个索引(用于搜索聚合);但写入时只能指向一个索引(除非使用 is_write_index)。 |
| 删除别名 | POST /_aliases | 移除索引的别名 | POST /_aliases{ "actions": [ { "remove": { "index": "products_v1", "alias": "products" } } ]} | 别名删除不影响原索引数据。 |
| 查看别名 | GET /_alias/<alias_name> | 获取别名指向的索引列表 | GET /_alias/products | 若别名不存在,返回 404。 |
| 创建带写入标识的别名 | POST /_aliases | 指定哪个索引接收写入请求 | POST /_aliases{ "actions": [ { "add": { "index": "products_v2", "alias": "products", "is_write_index": true } } ]} | 在滚动更新(rollover)场景中必需;写入请求(如 POST /products/_doc)会路由到 is_write_index=true 的索引。 |
| 别名与过滤器结合 | 在创建别名时添加 filter | 使别名仅暴露满足条件的文档 | POST /_aliases{ "actions": [ { "add": { "index": "orders", "alias": "paid_orders", "filter": { "term": { "status": "paid" } } } } ]} | 查询 paid_orders 时自动应用 filter,相当于视图;filter 使用 query DSL 语法。 |
✅ 提示:别名是实现零停机索引切换(如 reindex 后切换流量)的核心机制。
第四章:数据建模与映射(Mapping)
4.1 字段类型详解(text、keyword、date、numeric 等)
| 字段类型 | 说明 | 适用场景 | 注意事项 |
|---|---|---|---|
| text | 用于全文检索的字符串字段,会被分词器(analyzer)拆分为词条(terms)。 | 文章内容、商品描述、日志消息等需要模糊/相关性搜索的文本。 | 不可用于聚合、排序或脚本访问原始值;若需同时支持搜索和聚合,应配合 keyword 多字段使用。 |
| keyword | 用于精确匹配的字符串字段,不被分词,整体作为一个 term 存储。 | 标签、状态码、邮箱、URL、国家名等枚举或标识类字段。 | 支持聚合、排序、脚本;默认最大长度 32766 字节(可调),超长会报错。 |
| date | 存储日期时间,支持多种格式(ISO8601、epoch_millis 等)。 | 日志时间戳、订单创建时间、用户注册时间等。 | 默认格式为 strict_date_optional_time |
| long / integer / short / byte | 有符号整数类型,分别占 64/32/16/8 位。 | 用户 ID、数量、版本号等整型数值。 | 超出范围会报错;优先使用最小满足需求的类型以节省存储。 |
| double / float | 浮点数类型,double 精度更高(64 位),float 为 32 位。 | 价格、评分、地理坐标等小数。 | 存在精度误差,不适合金融计算(建议用 scaled_float 或应用层处理)。 |
| boolean | 布尔类型,接受 true/false、"true"/"false"、1/0 等。 | 是否有效、是否已读、开关状态等。 | 写入非标准值(如 "yes")会报错。 |
| object | 表示嵌套 JSON 对象(扁平化存储)。 | 地址(含省市区)、用户信息(含姓名、年龄)等结构化对象。 | 内部字段通过点号访问(如 address.city);不保留对象边界,无法独立查询完整子对象。 |
| nested | 特殊对象类型,保留数组中每个对象的独立性。 | 商品属性(颜色+尺寸组合)、评论列表(用户+内容)等对象数组。 | 查询需用 nested 查询;性能低于 object,仅在必要时使用。 |
| geo_point | 存储经纬度坐标。 | 门店位置、用户 GPS 轨迹等。 | 支持距离计算、地理围栏、地理聚合;格式可为字符串("lat,lon")、数组 [lon, lat] 或对象 { "lat": ..., "lon": ... }。 |
| ip | 存储 IPv4 或 IPv6 地址。 | 客户端 IP、服务器地址等。 | 支持 CIDR 范围查询(如 192.168.0.0/16);自动标准化格式。 |
| completion | 专用于自动补全(suggester)的字段类型。 | 搜索框下拉提示、标签推荐等。 | 需配合 suggest API 使用;不支持常规查询。 |
4.2 动态映射 vs 显式映射
| 概念名称 | 说明 | 配置方式 | 注意事项 |
|---|---|---|---|
| 动态映射(Dynamic Mapping) | 当索引不存在或文档包含新字段时,Elasticsearch 自动推断字段类型并添加到 mapping。 | 默认开启;可通过 dynamic: true(默认)、runtime、strict 控制行为。 | 自动推断可能不准确(如数字字符串被识别为 text);可能导致 mapping 膨胀(字段爆炸)。 |
| 显式映射(Explicit Mapping) | 在创建索引时预先定义所有字段的类型和属性。 | 创建索引时在 mappings 中声明字段:PUT /my_index{ "mappings": { "properties": { "name": { "type": "text" } } } } | 推荐生产环境使用;可避免意外字段注入;mapping 一旦创建,字段类型不可更改(需 reindex)。 |
| dynamic 参数取值 | 控制动态字段处理策略: - true:自动添加新字段- runtime:新字段作为 runtime field(8.x)- strict:拒绝包含未知字段的文档 | 在 mapping 中设置:"dynamic": "strict" | strict 模式可防止脏数据写入;runtime 字段不索引,查询时计算,适合临时分析。 |
| 禁用动态映射 | 完全禁止自动添加字段 | "mappings": { "dynamic": false, "properties": { ... } } | 已定义字段仍可写入;未定义字段会被忽略(不报错也不索引)。 |
4.3 多字段(fields)与 analyzer 配置
| 概念/操作 | 说明 | 配置示例 | 注意事项 |
|---|---|---|---|
| 多字段(fields) | 同一字段以不同方式索引多次(如 text + keyword)。 | "title": { "type": "text", "fields": { "keyword": { "type": "keyword" } }} | 查询全文用 title,聚合/排序用 title.keyword;常见于日志 message、商品 name 等字段。 |
| 自定义 analyzer | 组合 tokenizer 和 filter 实现特定分词逻辑。 | 在 settings 中定义:"analysis": { "analyzer": { "my_analyzer": { "tokenizer": "standard", "filter": ["lowercase", "stop"] } }} | 需在索引创建时定义;不能修改已存在索引的 analyzer(需 close → update → open)。 |
| 指定字段 analyzer | 为 text 字段指定自定义或内置 analyzer。 | "content": { "type": "text", "analyzer": "my_analyzer"} | 支持 analyzer(索引+搜索)、search_analyzer(仅搜索时覆盖);中文需用 IK 等插件。 |
| 内置 analyzer 示例 | - standard:通用分词 - whitespace:按空格切分 - keyword:不分词 - pattern:正则分词 | 无需额外配置,直接引用名称 | keyword analyzer 通常用于模拟 keyword 类型效果(但仍是 text 字段)。 |
4.4 嵌套对象与父子文档(nested / join)
| 类型 | 说明 | 映射配置示例 | 查询方式 | 注意事项 |
|---|---|---|---|---|
| nested 类型 | 将对象数组中的每个元素作为独立文档索引,保持内部字段关联性。 | "comments": { "type": "nested", "properties": { "user": { "type": "keyword" }, "message": { "type": "text" } }} | 使用 nested 查询:{ "nested": { "path": "comments", "query": { "match": { "comments.user": "alice" } } }} | 性能开销大;更新整个 nested 数组;不支持跨 nested 对象的 join 查询。 |
| join 类型 | 在同一索引内实现父子关系(类似 SQL 外键),支持 has_child / has_parent 查询。 | "my_join_field": { "type": "join", "relations": { "question": "answer" }} | 父文档:POST /qa/_doc/1?routing=1{ "text": "Q1", "my_join_field": "question" }子文档: POST /qa/_doc/2?routing=1{ "text": "A1", "my_join_field": { "name": "answer", "parent": "1" } } | 必须指定 routing(且父子文档 routing 值相同);写入和查询复杂度高;仅适用于一对多关系。 |
| 选择建议 | - 用 nested:子对象生命周期与父文档一致,查询需保持内部字段关联 - 用 join:父子文档独立更新,需跨文档关联查询 | — | join 在高基数(大量父文档)场景性能差;Elasticsearch 官方建议优先考虑 denormalization(冗余数据)替代 join。 |
第五章:基本查询与全文检索
5.1 Match 查询
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| match 查询 | POST /<index>/_search{ "query": { "match": { "<field>": "<value>" } } } | 对 text 字段执行全文检索,输入文本会被 analyzer 分词后匹配。 | POST /products/_search{ "query": { "match": { "description": "wireless keyboard" } }} | 默认使用字段定义的 analyzer;若字段为 keyword,则整体匹配(不推荐用于 keyword)。 |
| match with operator | 在 match 中指定 operator: "and" 或 "or" | 控制分词后词条的逻辑关系(默认为 or) | { "match": { "title": { "query": "blue wireless", "operator": "and" } } } | and 要求所有词都出现,提高精度;or(默认)任一词出现即可,提高召回。 |
| match_phrase | match_phrase 查询 | 精确短语匹配,要求词条按顺序且相邻(或在 slop 范围内) | { "match_phrase": { "content": "quick brown fox" } } | 比 match 更严格;适用于标题、人名等固定短语搜索。 |
| match_phrase with slop | 添加 slop 参数允许词条间有间隔 | 允许短语中词语顺序不变但可插入其他词 | { "match_phrase": { "content": { "query": "quick fox", "slop": 2 } } } | slop=0 表示完全紧邻;slop 值越大,匹配越宽松。 |
5.2 Term 与 Terms 精确查询
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| term 查询 | POST /<index>/_search{ "query": { "term": { "<field>": "<value>" } } } | 精确匹配单个值,不经过 analyzer,适用于 keyword、date、numeric 等类型。 | { "query": { "term": { "status": "published" } } } | 不能用于 text 字段(因 text 被分词,存储的是词条而非原值);大小写敏感。 |
| terms 查询 | { "terms": { "<field>": ["val1", "val2", ...] } } | 匹配字段值在给定列表中的文档(相当于 SQL 的 IN) | { "query": { "terms": { "category": ["electronics", "books"] } } } | 列表长度无硬性限制,但过长会影响性能;支持从其他索引动态获取(terms lookup)。 |
| term vs match | — | term 用于精确值(keyword/数字/日期),match 用于全文文本 | term: { "user.id": 123 }match: { "user.name": "张三" } | 常见错误:对 text 字段用 term 查询(应改用 keyword 子字段或 match)。 |
| 大小写处理 | 若需对 keyword 字段做大小写不敏感查询 | 应在 mapping 中配置 normalizer(如 lowercase) | mapping 中:"email": { "type": "keyword", "normalizer": "lowercase" } | normalizer 是 keyword 的 analyzer,仅支持预定义 filter(如 lowercase)。 |
5.3 Bool 组合查询(must / should / must_not / filter)
| 子句名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| must | { "bool": { "must": [ {query1}, {query2} ] } } | 所有子查询必须匹配(AND),参与评分。 | { "bool": { "must": [ { "match": { "title": "elasticsearch" } }, { "term": { "status": "active" } }] } } | 结果文档的 _score 是各子查询得分的综合(通常为乘积或加权和)。 |
| should | { "bool": { "should": [ {query1}, {query2} ] } } | 至少满足一个子查询(OR),参与评分;若与 must 共存,则变为”加分项”。 | { "bool": { "should": [ { "term": { "tag": "tutorial" } }, { "term": { "tag": "beginner" } }] } } | 默认 minimum_should_match=1;可通过参数调整最低匹配数。 |
| must_not | { "bool": { "must_not": [ {query} ] } } | 排除匹配子查询的文档(NOT),不参与评分。 | { "bool": { "must_not": { "term": { "status": "deleted" } } } } | 即使文档被排除,仍可能因其他查询返回(只要不违反 must_not)。 |
| filter | { "bool": { "filter": [ {query} ] } } | 过滤文档(必须满足),不计算相关性得分,结果可被缓存。 | { "bool": { "filter": { "range": { "price": { "gte": 100 } } } } } | 性能优于 must(无打分开销);适用于结构化条件(如时间范围、状态筛选)。 |
| 组合使用 | 四种子句可任意组合 | 构建复杂业务查询逻辑 | { "bool": { "must": [ { "match": { "content": "search" } } ], "filter": [ { "term": { "category": "tech" } } ], "must_not": [ { "term": { "status": "draft" } } ]} } | 推荐将结构化条件放入 filter,全文条件放入 must/should,以优化性能。 |
5.4 高亮(Highlighting)与分页(from/size)
| 功能 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 高亮(Highlighting) | 在 search 请求中添加 highlight 字段 | 在搜索结果中标记匹配关键词,提升用户体验 | POST /articles/_search{ "query": { "match": { "body": "elasticsearch" } }, "highlight": { "fields": { "body": {} } }} | 默认使用 <em> 和 </em> 包裹匹配词;可自定义 pre_tags / post_tags;仅对 text 字段有效。 |
| 自定义高亮标签 | "pre_tags": ["<strong>"], "post_tags": ["</strong>"] | 使用自定义 HTML 标签包裹高亮文本 | "highlight": { "fields": { "title": { "pre_tags": ["<mark>"], "post_tags": ["</mark>"] } }} | 需确保前端能安全渲染(防 XSS);支持多组标签(轮询使用)。 |
| 分页(from/size) | "from": <offset>, "size": <count> | 控制返回结果的起始位置和数量(类似 SQL LIMIT) | { "from": 10, "size": 5, "query": { ... } } | from + size 不能超过 index.max_result_window(默认 10000);深度分页建议用 search_after。 |
| 默认分页 | 若未指定,默认 from=0, size=10 | 返回前 10 条结果 | — | 可通过集群设置修改默认 size,但不推荐。 |
| 高亮与分页结合 | 同时使用 highlight 和 from/size | 实现带高亮的分页搜索 | { "from": 0, "size": 5, "query": { "match": { "content": "query" } }, "highlight": { "fields": { "content": {} } }} | 高亮信息位于响应的 hits.hits[n].highlight 中;不影响分页逻辑。 |
✅ 提示:生产环境深度分页(from > 10000)应使用
search_after+ 排序字段替代,避免性能问题。
第六章:高级搜索功能
6.1 聚合分析(Aggregations)
| 聚合类型 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
指标聚合(Metric)avg / sum / min / max / cardinality | "aggs": { "agg_name": { "<metric>": { "field": "..." } } } | 对数值字段计算统计指标 | { "aggs": { "avg_price": { "avg": { "field": "price" } } } } | cardinality 用于去重计数(近似值);不支持 text 字段(需用 keyword)。 |
桶聚合(Bucket)terms | "aggs": { "by_category": { "terms": { "field": "category.keyword", "size": 10 } } } | 按字段值分组(类似 SQL GROUP BY) | { "aggs": { "tags": { "terms": { "field": "tag.keyword", "size": 5 } } } } | 默认返回前 10 个桶;size=0 可禁用(但不推荐);text 字段需用 .keyword。 |
| 范围聚合(Range) | "range": { "field": "price", "ranges": [ { "from": 0, "to": 100 }, ... ] } | 按数值区间分组 | { "aggs": { "price_ranges": { "range": { "field": "price", "ranges": [ { "key": "cheap", "to": 50 }, { "from": 50 } ] } } } } | 支持 key 自定义桶名;from 包含,to 不包含。 |
| 日期直方图(Date Histogram) | "date_histogram": { "field": "timestamp", "calendar_interval": "1d" } | 按时间间隔分组(如每天、每月) | { "aggs": { "daily_sales": { "date_histogram": { "field": "order_date", "calendar_interval": "1d" } } } } | 推荐用 calendar_interval(如 “1d”, “1M”)而非 interval(已弃用);时区可通过 time_zone 参数设置。 |
| 嵌套聚合 | 在桶聚合内嵌套其他聚合 | 实现多维分析(如”每个类别的平均价格”) | { "aggs": { "by_category": { "terms": { "field": "category.keyword" }, "aggs": { "avg_price": { "avg": { "field": "price" } } } } } } | 聚合可任意嵌套;注意性能随深度增加而下降。 |
| 全局聚合(Global) | "global": {} | 忽略查询条件,对全部文档聚合 | { "aggs": { "all_docs": { "global": {}, "aggs": { "total": { "value_count": { "field": "_id" } } } } } } | 常用于计算总数 vs 过滤后数量对比。 |
⚠️ 注意:聚合默认不返回原始文档(
size=0可显式关闭 hits);大量唯一值的 terms 聚合可能 OOM,建议限制 size 或使用 composite 聚合分页。
6.2 自定义评分(Function Score)
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| function_score 查询 | "query": { "function_score": { "query": {...}, "functions": [...], "score_mode": "...", "boost_mode": "..." } } | 根据自定义函数调整文档相关性得分 | { "query": { "function_score": { "query": { "match": { "title": "search" } }, "functions": [ { "random_score": {} } ], "boost_mode": "multiply" } } } | 是实现个性化排序(如热度+相关性)的核心手段。 |
| weight 函数 | { "filter": {...}, "weight": 2.0 } | 对满足条件的文档乘以固定权重 | { "function_score": { "functions": [ { "filter": { "term": { "is_featured": true } }, "weight": 3 } ] } } | weight 是常数,不依赖字段值。 |
| field_value_factor | { "field_value_factor": { "field": "view_count", "factor": 1.2, "modifier": "log1p" } } | 基于字段数值动态调整得分 | { "field_value_factor": { "field": "likes", "modifier": "sqrt" } } | modifier 可选:none, log, log1p, sqrt, reciprocal;避免字段为 0 或负值(可设 missing)。 |
| random_score | { "random_score": { "seed": 12345 } } | 引入随机性(用于打散排序) | { "random_score": { "seed": "user123" } } | 相同 seed 返回相同随机顺序;适合推荐场景防呆板。 |
| script_score | { "script_score": { "script": { "source": "doc['price'].value * params.factor", "params": { "factor": 0.1 } } } } | 使用 Painless 脚本完全自定义评分逻辑 | { "script_score": { "script": { "source": "1.0 / (1 + doc['distance'].value)" } } } | 性能开销大;需启用 script(默认允许);避免复杂计算。 |
| score_mode 与 boost_mode | - score_mode: multiply, sum, avg, first, max, min - boost_mode: multiply, replace, sum, avg, max, min | 控制函数得分如何合并及与原查询得分结合 | boost_mode: “multiply”(默认)表示 最终得分 = query_score × function_score | 常用组合:boost_mode=multiply(加权),replace(完全由函数决定)。 |
6.3 模糊查询(Fuzzy)、前缀查询(Prefix)、通配符(Wildcard)
| 查询类型 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| fuzzy 查询 | { "fuzzy": { "field": { "value": "text", "fuzziness": "AUTO" } } } | 匹配拼写相近的词(基于编辑距离) | { "fuzzy": { "name": { "value": "elastiksearch", "fuzziness": 2 } } } | fuzziness 默认 AUTO(短词=1,长词=2);仅适用于 text/keyword;性能较差,慎用于大字段。 |
| prefix 查询 | { "prefix": { "field": "pre" } } | 匹配字段值以指定前缀开头的文档 | { "prefix": { "product_code": "XJ-" } } | 不分析文本;对 keyword 高效,对 text 需确保未被分词(通常不用于 text)。 |
| wildcard 查询 | { "wildcard": { "field": "te*t" } } | 使用通配符 *(任意字符)和 ?(单字符)匹配 | { "wildcard": { "filename": "*.log" } } | 性能差(需扫描倒排索引);避免以 * 或 ? 开头(如 *test 无法利用索引)。 |
| regexp 查询 | { "regexp": { "field": "foo.*bar" } } | 使用正则表达式匹配 | { "regexp": { "sku": "[A-Z]{2}\\d{4}" } } | 性能最差;需转义反斜杠(JSON 中写为 \\\\);仅用于必要场景。 |
| 性能建议 | — | 优先使用 match_phrase_prefix 或 ngram tokenizer 替代 wildcard/prefix | — | 对高频前缀搜索,建议在 mapping 中使用 edge_ngram 分词器预处理。 |
6.4 地理位置搜索(Geo Queries)
| 查询类型 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| geo_distance | { "geo_distance": { "distance": "10km", "location": { "lat": 40.71, "lon": -74.01 } } } | 查找距离某点一定范围内的文档 | { "query": { "geo_distance": { "distance": "5km", "pin.location": { "lat": 39.9, "lon": 116.4 } } } } | 字段必须为 geo_point 类型;支持多种距离单位(km, mi, m 等)。 |
| geo_bounding_box | { "geo_bounding_box": { "location": { "top_left": {...}, "bottom_right": {...} } } } | 查找位于矩形区域内的文档 | { "geo_bounding_box": { "pin.location": { "top_left": { "lat": 40.0, "lon": -75.0 }, "bottom_right": { "lat": 39.0, "lon": -74.0 } } } } | 坐标顺序:top_left(左上),bottom_right(右下);经度范围 [-180, 180],纬度 [-90, 90]。 |
| geo_polygon | { "geo_polygon": { "location": { "points": [ {lat,lon}, ... ] } } } | 查找位于多边形区域内的文档 | { "geo_polygon": { "pin.location": { "points": [ { "lat": 40, "lon": -75 }, { "lat": 40, "lon": -74 }, { "lat": 39, "lon": -74 } ] } } } | 多边形需闭合(首尾点可相同或自动闭合);点数不宜过多(影响性能)。 |
| geo_shape(高级) | 支持更复杂的地理形状(如 LineString、MultiPolygon) | 用于 GIS 系统、行政区划等 | 需预先定义 geo_shape mapping;查询结构复杂 | 性能开销大;需启用 geo_shape 模块;一般场景用 geo_point 足够。 |
| 地理聚合 | geohash_grid / geo_bounds / geo_centroid | 对地理位置进行聚合分析 | { "aggs": { "zoomed_in": { "geohash_grid": { "field": "location", "precision": 5 } } } } | geohash_grid 将地图划分为网格;precision 越高,网格越小(1–12,默认 5)。 |
✅ 提示:地理查询需确保数据写入时格式正确(如
{ "lat": 39.9, "lon": 116.4 }),且字段 mapping 为 geo_point。
第七章:中文分词与自定义分析器
7.1 内置分词器(standard、whitespace、keyword)
| 分词器名称 | 说明 | 中文处理效果 | 适用场景 | 注意事项 |
|---|---|---|---|---|
| standard | Elasticsearch 默认分词器,基于 Unicode 文本分割规则。 | 将中文按单字切分(如”Elasticsearch教程” → [“e”, “l”, “a”, …, “教”, “程”]),无法识别词语。 | 英文全文检索;不适用于中文搜索。 | 对中文基本无效,需替换为中文分词器(如 IK)。 |
| whitespace | 按空白字符(空格、制表符等)切分文本。 | 中文无空格,整句作为一个 term(如”你好世界” → [“你好世界”])。 | 日志字段(如 user-agent)、已预分词的文本。 | 无法对中文进行词语级切分。 |
| keyword | 不分词,将整个输入作为单个 term。 | “北京天气” → [“北京天气”] | 精确匹配字段(如标签、ID、URL);配合多字段用于聚合。 | 不能用于全文检索;适合 keyword 类型字段。 |
| pattern | 使用正则表达式切分文本(默认 \W+,即非单词字符)。 | 类似 standard,中文仍按单字或整句处理,取决于正则。 | 自定义分隔符场景(如 CSV 字段)。 | 需谨慎编写正则,避免性能问题。 |
⚠️ 结论:内置分词器均不适合中文全文检索,必须使用第三方中文分词插件(如 IK、jieba、THULAC)。
7.2 安装 IK 分词器插件
| 步骤名称 | 操作细节 | 注意事项 |
|---|---|---|
| 下载插件 | 访问 GitHub 仓库:https://github.com/medcl/elasticsearch-analysis-ik 下载与 Elasticsearch 版本严格匹配的 release 包(如 elasticsearch-analysis-ik-8.11.0.zip) | 插件版本必须与 ES 主版本一致(如 ES 8.11.0 → IK 8.11.0),否则启动失败。 |
| 安装插件(Linux/macOS) | 在 ES 根目录执行:./bin/elasticsearch-plugin install file:///path/to/elasticsearch-analysis-ik-8.x.x.zip或在线安装: ./bin/elasticsearch-plugin install https://.../elasticsearch-analysis-ik-8.x.x.zip | 需保证网络可访问(若用在线 URL);安装后插件位于 plugins/analysis-ik/。 |
| 安装插件(Windows) | 以管理员身份运行 CMD,进入 ES 目录:.\bin\elasticsearch-plugin install file:///C:/path/to/elasticsearch-analysis-ik-8.x.x.zip | 路径中的空格或特殊字符需转义;建议使用短路径。 |
| 验证安装 | 启动 ES 后,执行:curl -X POST "localhost:9200/_analyze?pretty" -H 'Content-Type: application/json' -d'{ "analyzer": "ik_smart", "text": "Elasticsearch中文分词" }' | 若返回分词结果(如 [“elasticsearch”, “中文”, “分词”]),表示安装成功。 |
| 重启服务 | 安装插件后必须重启 Elasticsearch | 否则插件不会加载;Kibana 无需重启。 |
7.3 自定义 Analyzer 配置
| 配置项 | 语法位置 | 用途 | 配置示例 | 注意事项 |
|---|---|---|---|---|
| 定义 analyzer | 在索引的 settings.analysis.analyzer 中 | 组合 tokenizer 和 filter 构建自定义分析器 | "settings": { "analysis": { "analyzer": { "my_ik_analyzer": { "type": "custom", "tokenizer": "ik_max_word", "filter": ["lowercase"] } } }} | type: custom 表示自定义;tokenizer 必须指定(如 ik_smart/ik_max_word);filter 可选。 |
| 指定字段使用自定义 analyzer | 在 mapping 的字段定义中 | 使特定 text 字段使用自定义分词逻辑 | "mappings": { "properties": { "content": { "type": "text", "analyzer": "my_ik_analyzer" } }} | 创建索引时一次性定义;mapping 一旦创建,analyzer 不能修改(需 reindex)。 |
| IK 分词模式 | - ik_smart:最少切分(粗粒度) - ik_max_word:最细切分(细粒度) | 控制分词粒度 | tokenizer: "ik_smart" → [“中文”, “分词”]tokenizer: "ik_max_word" → [“中文”, “分词”, “词”] | ik_max_word 召回率高但噪音多;ik_smart 精准但可能漏词;可根据业务选择或同时配置多字段。 |
| 多字段 + IK 示例 | 同一字段配置多个 analyzer | 兼顾不同搜索需求 | "title": { "type": "text", "analyzer": "ik_max_word", "fields": { "smart": { "type": "text", "analyzer": "ik_smart" } }} | 查询全文用 title,精确短语用 title.smart。 |
7.4 热更新词典(扩展词、停用词)
| 操作名称 | 操作细节 | 注意事项 |
|---|---|---|
| 扩展词典(main.dic) | 编辑 plugins/analysis-ik/config/IKAnalyzer.cfg.xml,确认 <entry key="ext_dict">custom/mydict.dic</entry> 已启用在 config/custom/ 下创建 mydict.dic,每行一个词(如”人工智能”、“大模型”) | 文件编码必须为 UTF-8 无 BOM;词典文件需可读;重启 ES 或触发 reload 才生效(见下)。 |
| 停用词典(stopword.dic) | 同上,在 IKAnalyzer.cfg.xml 中启用 <entry key="ext_stopwords">custom/mystop.dic</entry>在 mystop.dic 中添加停用词(如”的”、“了”) | 停用词在分词时会被过滤;慎用,避免过度过滤影响召回。 |
| 热更新机制(无需重启) | 方式一:配置远程 HTTP 词典(推荐) 在 IKAnalyzer.cfg.xml 中设置:<entry key="remote_ext_dict">http://your-server/dict.txt</entry>方式二:发送 POST 请求触发 reload 或依赖 IK 插件内置的监控线程(默认每 60 秒检查文件修改时间) | 本地文件修改后,IK 会自动 reload(默认 60 秒间隔);可通过 ik.config.reload.interval 调整(单位秒);远程词典需返回纯文本,每行一词,支持 HTTPS。 |
| 验证热更新 | 使用 _analyze API 测试新词是否生效 | POST /_analyze{ "analyzer": "ik_max_word", "text": "大模型技术" } |
| 权限与路径 | 确保 ES 进程对 config/custom/ 目录有读权限 | Linux 下若以 es 用户运行,需 chown -R es:es config/custom/ |
✅ 最佳实践:生产环境使用远程 HTTP 词典实现动态更新,避免频繁操作服务器文件。
第八章:集群架构与运维
8.1 节点角色(master、data、ingest、coordinating)
| 节点角色 | 说明 | 配置方式(elasticsearch.yml) | 注意事项 |
|---|---|---|---|
| Master-eligible | 参与主节点选举,管理集群状态(索引创建、分片分配等)。 | node.roles: [ master ](或旧版 node.master: true) | 至少部署 3 个专用 master 节点实现高可用;避免与 data 节点混用(防 OOM 导致集群失稳)。 |
| Data | 存储数据分片,执行 CRUD、搜索、聚合等数据操作。 | node.roles: [ data ](或 node.data: true) | 可细分为: - data_hot(高频读写) - data_warm(只读温数据) - data_cold / data_frozen(归档) 需根据硬件配置角色。 |
| Ingest | 执行预处理管道(如 grok 解析、字段转换),在索引前处理文档。 | node.roles: [ ingest ](或 node.ingest: true) | 若使用 Logstash 做清洗,可关闭 ingest 角色;否则建议独立 ingest 节点或与 coordinating 合并。 |
| Coordinating-only | 接收客户端请求,转发到 data 节点并合并结果,不存数据、不参与选举。 | node.roles: [ ](即不设置任何角色) | 所有节点默认具备 coordinating 能力;高并发场景建议部署专用协调节点,避免 data 节点过载。 |
| All roles(开发用) | 单节点同时承担所有角色(默认行为)。 | 不配置 node.roles 或设为 [ master, data, ingest ] | 仅限开发/测试环境;生产环境必须分离角色以保障稳定性。 |
✅ 生产推荐架构:3 master + N data + M coordinating(可选 ingest)
8.2 分片(Shard)与副本(Replica)机制
| 概念 | 说明 | 配置方式 | 注意事项 |
|---|---|---|---|
| 主分片(Primary Shard) | 索引数据的物理分割单元,写入时路由到特定主分片。 | 创建索引时指定:"settings": { "number_of_shards": 3 } | 数量创建后不可更改;影响写入吞吐和存储扩展性;建议单分片 ≤ 50GB。 |
| 副本分片(Replica Shard) | 主分片的拷贝,提供高可用和读取负载均衡。 | "settings": { "number_of_replicas": 1 }(默认 1) | 可动态调整(PUT /index/_settings);副本数 ≥1 才能容忍节点故障;增加副本提升查询性能。 |
| 分片分配 | ES 自动将分片分布到不同节点,避免主副在同一节点。 | 由集群自动管理;可通过 allocation filtering 控制 | 使用 _cluster/allocation/explain 诊断未分配分片原因。 |
| 分片恢复 | 节点重启或新增时,自动从副本或 translog 恢复数据。 | 自动进行;可配置恢复速率:cluster.routing.allocation.node_concurrent_recoveries | 恢复期间可能影响性能;建议限制并发恢复数(默认 2)。 |
| 分片大小建议 | 单个分片理想大小为 10–50 GB | 通过预估数据量反推分片数: 总数据量 ÷ 30 GB ≈ 分片数 | 过小 → 分片过多(集群开销大);过大 → 恢复慢、负载不均。 |
8.3 集群部署与高可用配置
| 配置项 | 操作细节 | 注意事项 |
|---|---|---|
| 最小高可用集群 | 3 台服务器,每台部署一个 master-eligible 节点(专用) | 必须为奇数(3/5/7),避免脑裂;3 节点可容忍 1 节点故障。 |
| discovery.seed_hosts | 在 elasticsearch.yml 中配置初始主机列表:discovery.seed_hosts: ["es-node1", "es-node2", "es-node3"] | 使用主机名或 IP;确保节点间 9300 端口互通(transport 通信)。 |
| cluster.initial_master_nodes | 首次启动集群时指定主节点候选列表:cluster.initial_master_nodes: ["node1", "node2", "node3"] | 仅首次启动需要;集群形成后可注释掉;节点名需与 node.name 一致。 |
| 网络绑定 | 设置绑定地址:network.host: 0.0.0.0(或具体内网 IP) | 默认仅 localhost;生产环境必须绑定内网 IP;禁止暴露公网(应通过 LB 或 Kibana 访问)。 |
| JVM 堆内存 | 编辑 jvm.options:-Xms4g-Xmx4g | 堆大小 ≤ 31GB(避免压缩指针失效);≤ 物理内存 50%;主副分片所在节点需足够内存。 |
| 快照备份(Snapshot) | 配置共享存储(如 NFS、S3)作为仓库:PUT /_snapshot/my_backup{ "type": "fs", "settings": { "location": "/mnt/backups" } } | 定期备份是灾难恢复的关键;快照增量且可跨集群恢复。 |
| 滚动重启 | 依次关闭节点 → 升级 → 启动,等待集群 green 再继续 | 避免同时停多个节点导致数据不可用;副本数 ≥1 是前提。 |
8.4 监控与日志(使用 Kibana / Prometheus)
| 监控方式 | 操作细节 | 注意事项 |
|---|---|---|
| Kibana Stack Monitoring | 登录 Kibana → Management → Stack Monitoring | 需启用监控收集:xpack.monitoring.collection.enabled: true(ES 配置)免费版支持基础指标(CPU、内存、分片状态)。 |
| 关键监控指标 | - 集群状态(green/yellow/red) - JVM 内存使用率 - 线程池队列(如 bulk/rejected) - 分片未分配数 - 查询/索引延迟 | 状态 red 表示主分片丢失;yellow 表示副本未分配;rejected 表示线程池满(需扩容或限流)。 |
| Prometheus + Elastic Exporter | 部署 elasticsearch_exporter(第三方)或使用 ES 8.x 内置 Prometheus endpoint:GET /_prometheus/metrics | 需在 elasticsearch.yml 启用:http.port: 9200xpack.monitoring.exporters.prometheus.enabled: true |
| 日志位置 | 日志文件位于 logs/ 目录:- elasticsearch.log(主日志) - gc.log(GC 日志) | 日志级别可在 log4j2.properties 调整;OOM 问题需分析 GC 日志。 |
| 慢查询日志 | 在索引 settings 中开启:"index.search.slowlog.threshold.query.warn": "5s" | 记录耗时超过阈值的查询,用于性能优化;日志输出到 logs/slowlog.log。 |
| 审计日志(Audit Logging) | 启用安全审计(需白金许可):xpack.security.audit.enabled: true | 记录用户操作(登录、权限变更等),满足合规要求。 |
✅ 建议:生产环境必须配置集群健康告警(如通过 Watcher 或外部监控系统),及时发现 yellow/red 状态或节点离线。
第九章:性能调优与生产实践
9.1 索引性能优化(refresh_interval、translog、bulk size)
| 优化项 | 配置方式 / 方法 | 用途 | 代码示例 / 参数值 | 注意事项 |
|---|---|---|---|---|
| refresh_interval | 索引 settings 中设置 | 控制索引数据变为可搜索的频率;默认 1s | "settings": { "refresh_interval": "30s" } | 值越大,写入吞吐越高,但搜索延迟增加;批量导入时可设为 -1(禁用 refresh),完成后手动 _refresh。 |
| translog 配置 | 调整 translog 持久化策略 | 平衡数据安全与写入性能 | "index.translog.durability": "async""index.translog.sync_interval": "30s" | durability: async 提升性能但可能丢最近数据;sync_interval 控制 fsync 频率;默认 request(每次请求刷盘)。 |
| Bulk 批次大小 | 客户端控制单次 bulk 请求的数据量 | 减少网络往返,提升吞吐 | 单次 bulk 数据量建议 5–15 MB 或 1000–5000 文档 | 过大会导致 ES 节点 GC 压力或超时;过小则网络开销占比高;需压测确定最佳值。 |
| 禁用副本临时写入 | 写入前关闭副本,写完再开启 | 避免副本同步开销 | PUT /my_index/_settings{ "number_of_replicas": 0 }(写入完成后再设为 1) | 仅适用于离线批量导入;在线服务需保持副本以保障可用性。 |
| 自动刷新控制 | 使用 _bulk?refresh=false | 避免 bulk 请求触发 refresh | POST /_bulk?refresh=false{ "index": { ... } }{ ... } | 默认 refresh=wait_for(等待下一次 refresh);设为 false 可提升吞吐。 |
9.2 查询性能优化(filter 缓存、doc_values、避免 script)
| 优化项 | 说明 | 配置 / 使用方式 | 注意事项 |
|---|---|---|---|
| 使用 filter 上下文 | filter 子句结果可被缓存,且不计算评分 | 在 bool 查询中将结构化条件放入 filter | 避免将范围、term 等条件放在 must(会打分);filter 适用于状态、时间、分类等固定条件。 |
| 启用 doc_values | 列式存储字段值,用于排序、聚合、脚本 | mapping 中默认启用(除 text 外) | 若确定某字段永不用于排序/聚合,可禁用:"my_field": { "type": "keyword", "doc_values": false } → 节省磁盘和内存。 |
| 避免使用 script | Painless 脚本执行开销大 | 用预计算字段或 runtime field 替代 | 如需动态评分,优先用 function_score;必须用 script 时,确保逻辑简单且缓存结果。 |
| 预计算字段 | 在索引时计算派生字段(如”是否热门”) | 写入前由应用或 ingest pipeline 计算 | 避免查询时用 script 判断 view_count > 1000,改为索引时写入 is_popular: true。 |
| 限制返回字段 | 减少网络和内存开销 | 使用 _source 过滤:"_source": ["title", "price"] | 避免返回大文本字段(如 content)除非必要;可结合 stored fields 优化。 |
| 分页深度控制 | 避免 from + size > 10000 | 改用 search_after + 排序字段 | search_after 基于上一页最后一条的 sort 值,无深度限制且高效;需有唯一排序字段(如 _id + timestamp)。 |
9.3 温热架构(Hot-Warm Architecture)
| 组件 | 说明 | 配置方式 | 注意事项 |
|---|---|---|---|
| Hot 节点 | 高性能 SSD,处理实时写入和高频查询 硬件:NVMe SSD、高 CPU、大内存 | 节点角色:node.roles: [ data_hot ] | 通常保留最近 1–7 天数据;索引模板指定 index.routing.allocation.require.data: hot |
| Warm 节点 | 大容量 HDD,存储只读历史数据 硬件:SATA HDD、较低 CPU | 节点角色:node.roles: [ data_warm ] | 数据迁移后设为只读:"index.blocks.write": true;关闭 refresh 和副本(可选)以节省资源。 |
| 索引生命周期管理(ILM) | 自动化索引滚动、降冷、删除 | 创建 ILM 策略:PUT _ilm/policy/logs_policy{ "policy": { "phases": { "hot": {...}, "warm": {...} } } } | 需配合 rollover alias 使用;warm 阶段可执行 force merge(减少 segment 数)、shrink(合并分片)。 |
| Rollover | 当索引达到大小/时间/文档数阈值时创建新索引 | POST /my-alias/_rollover{ "conditions": { "max_age": "1d", "max_docs": 1000000 } } | 写入始终指向 alias;旧索引自动进入 warm 阶段。 |
| Shrink | 将多分片索引压缩为单分片(warm 阶段) | ILM 中配置 "shrink": { "number_of_shards": 1 } | 原索引分片数必须可被目标整除;需先设为只读。 |
✅ 典型流程:
- 写入 logs-write alias → 指向 logs-000001(hot)
- 达到 50GB → rollover → logs-000002(hot),logs-000001 进入 warm
- warm 阶段:force merge + shrink + 移至 warm 节点
9.4 容量规划与分片策略
| 规划维度 | 建议 | 计算公式 / 方法 | 注意事项 |
|---|---|---|---|
| 单分片大小 | 10–50 GB | 总数据量 ÷ 单分片目标大小 = 主分片数 | 避免 < 1GB(分片过多)或 > 100GB(恢复慢、负载不均);日志类可放宽至 50GB。 |
| 主分片数量 | 创建时确定,不可更改 | 例如:预计 300GB 数据 → 6–30 个分片 | 考虑未来 6–12 个月增长;可通过 reindex 扩容(成本高)。 |
| 副本数量 | 默认 1,高可用场景 ≥1 | 副本数 = N → 可容忍 N 节点故障 | 副本提升读吞吐和可用性,但增加存储成本(总存储 = (1+replicas) × 数据量)。 |
| JVM 堆内存 | ≤ 31GB,≤ 物理内存 50% | 64GB 机器 → 堆 31GB,剩余给 OS 文件缓存 | 堆过大导致 GC 停顿长;建议 16–31GB。 |
| 节点数据容量 | 单 data 节点 ≤ 20TB(HDD)或 ≤ 10TB(SSD) | 总数据量 ÷ 单节点容量 = 最小 data 节点数 | 需预留 20% 空间用于段合并、快照等;SSD 节点更注重 IOPS 而非容量。 |
| 分片总数限制 | 每节点 ≤ 20× heap(GB) 个分片 | 31GB 堆 → ≤ 620 分片/节点 | 分片是轻量级”指针”,但过多会导致集群状态庞大、选举变慢。 |
| 索引模板预定义 | 通过 template 统一 settings/mappings | PUT _template/logs_template{ "index_patterns": ["logs-*"], "settings": { "number_of_shards": 6 } } | 避免手动创建索引导致配置不一致;配合 ILM 实现自动化。 |
✅ 示例: 日均日志 100GB,保留 30 天 → 总量 3TB 单分片 30GB → 需 100 个主分片 副本 1 → 总分片 200 每节点 620 分片上限 → 至少 1 个 data 节点(实际建议 3+ 节点分散负载)
第十章:ELK 日志系统实战
10.1 Filebeat / Logstash 数据采集
| 工具 | 用途 | 配置方式 | 代码示例(关键片段) | 注意事项 |
|---|---|---|---|---|
| Filebeat | 轻量级日志 shipper,直接从文件采集并发送到 ES 或 Logstash | 编辑 filebeat.yml | filebeat.inputs: - type: filestream paths: - /var/log/nginx/*.logoutput.elasticsearch: hosts: ["http://es-node:9200"] | 资源占用低(<50MB 内存);支持模块(如 nginx、mysql)自动解析;适合边缘部署。 |
| Logstash | 强大的数据处理管道,支持复杂过滤、转换、 enrichment | 编写 .conf 管道文件 | input { file { path => "/logs/app.log" } }filter { grok { match => { "message" => "%{TIMESTAMP_ISO8601:ts} %{LOGLEVEL:level}" } } }output { elasticsearch { hosts => ["es:9200"] } } | 资源消耗高(建议 ≥4GB 堆内存);适合中心化清洗;可替代 Filebeat 的 filter 功能。 |
| Filebeat → Logstash | Filebeat 采集,Logstash 清洗 | Filebeat output 设为 logstash:output.logstash: { hosts: ["ls:5044"] }Logstash input 启用 beats 插件 | input { beats { port => 5044 } } | 推荐架构:Filebeat(边缘)→ Kafka(缓冲)→ Logstash(清洗)→ ES |
| 多行日志处理 | 合并 Java 异常堆栈等跨行日志 | Filebeat 配置 multiline.patternmultiline.type: patternmultiline.pattern: '^\s*at\s'multiline.negate: truemultiline.match: after | Logstash 用 multiline codec 实现类似功能;需根据日志格式调整正则。 |
10.2 日志管道配置(Pipeline)
| 组件 | 用途 | 配置语法 | 示例 | 注意事项 |
|---|---|---|---|---|
| Input | 定义数据来源 | input { ... } | file { path => "/app/logs/*.json" }beats { port => 5044 }kafka { topics => ["logs"] } | 支持多种源;生产环境建议用 Kafka 解耦。 |
| Filter(Grok) | 解析非结构化日志为结构化字段 | grok { match => { "message" => "..." } } | grok { match => { "message" => "%{IP:client} %{WORD:method} %{URIPATHPARAM:request}" } } | 使用 Grok Debugger(Kibana Dev Tools)测试模式;内置 150+ 模式(如 %{COMBINEDAPACHELOG})。 |
| Filter(Date) | 将字符串时间转为 timestamp | date { match => [ "log_timestamp", "yyyy-MM-dd HH:mm:ss" ] } | date { match => [ "ts", "ISO8601" ] } | 转换后字段名为 @timestamp(默认);确保时区正确(timezone => "Asia/Shanghai")。 |
| Filter(Mutate) | 字段重命名、删除、类型转换 | mutate { rename => { "old" => "new" } } | mutate { convert => { "bytes" => "integer" } remove_field => [ "message" ]} | 避免保留原始 message 字段以节省存储。 |
| Output | 定义数据目的地 | output { ... } | elasticsearch { hosts => ["es:9200"], index => "app-logs-%{+YYYY.MM.dd}"} | 支持动态索引名(基于字段或日期);可同时输出到多个目标(如 ES + S3)。 |
| Pipeline 管理 | 多管道隔离不同日志类型 | 在 pipelines.yml 中定义 | - pipeline.id: nginx_logs path.config: "/etc/logstash/conf.d/nginx.conf"- pipeline.id: app_logs path.config: "/etc/logstash/conf.d/app.conf" | 避免单管道过载;便于维护和资源分配。 |
10.3 在 Kibana 中可视化日志
| 操作步骤 | 操作细节 | 注意事项 |
|---|---|---|
| 创建 Index Pattern | Kibana → Management → Stack Management → Kibana → Index Patterns → Create | 必须与 ES 中的索引名匹配(如 filebeat-*);选择时间字段(如 @timestamp)以启用时间筛选。 |
| Discover 查看日志 | Kibana → Discover → 选择 Index Pattern | 支持 Lucene 或 KQL 查询(如 level: "ERROR");可添加/隐藏字段列;点击文档查看原始 JSON。 |
| 创建 Lens 可视化 | Kibana → Analytics → Lens | 拖拽字段生成柱状图、折线图等;例如:X 轴 @timestamp(按天),Y 轴 count(),分组 level。 |
| 创建 Dashboard | Kibana → Dashboard → Create → Add from library | 将多个可视化(错误率、QPS、Top URL)组合到一个面板 |
| 使用 Logs 应用 | Kibana → Observability → Logs | 专为日志设计的界面,支持流式查看、高亮、上下文跳转 |
| 保存搜索与可视化 | 在 Discover 或 Lens 中点击 “Save” | 保存后可在 Dashboard 或 Alerts 中复用 |
10.4 告警与异常检测(Alerting)
| 告警类型 | 配置方式 | 触发条件示例 | 通知渠道 | 注意事项 |
|---|---|---|---|---|
| Threshold Alert | Kibana → Observability → Rules and connectors → Create rule | 当过去 5 分钟 ERROR 日志数 > 100 | Email、Slack、Webhook、PagerDuty | 基于已保存的搜索或可视化;需先配置 connector(通知通道)。 |
| Anomaly Detection | Machine Learning → Create job → 日志事件计数 | 自动检测 ERROR 日志突增(无需阈值) | 同上 | 需白金许可;适合无固定阈值的场景(如业务低谷期少量错误即异常)。 |
| Log Threshold(Elastic 8.x) | Logs → Streams → Create alert | log.level: "FATAL" 出现即告警 | 同上 | 专为日志设计的简化告警流程。 |
| 配置 Connector | Stack Management → Rules and connectors → Actions → Create | Slack webhook URL、SMTP 邮箱配置 | — | 测试通知是否可达;敏感信息(如 token)用 Kibana 密钥库加密。 |
| 告警恢复通知 | 在 rule 配置中启用 “Notify when alerts recover” | 错误率回落至正常水平 | 同上 | 避免”只报不消”导致告警疲劳。 |
| 抑制重复告警 | 设置 “Throttle notifications”(如每小时最多 1 次) | 同一问题持续存在时减少通知频率 | — | 平衡及时性与打扰度。 |
✅ 最佳实践:
- 关键服务设置多级告警(WARN → ERROR → FATAL)
- 所有告警必须关联 On-Call 响应流程
- 定期 review 告警有效性,关闭”僵尸告警”
第十一章:客户端集成与应用开发
11.1 Java High Level REST Client(已弃用)
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 创建客户端 | RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(...)) | 初始化连接 ES 的客户端 | RestHighLevelClient client = new RestHighLevelClient( RestClient.builder(new HttpHost("localhost", 9200, "http"))); | Elasticsearch 7.15+ 已弃用,8.x 完全移除;仅用于维护旧项目。 |
| 索引文档 | client.index(new IndexRequest(...), RequestOptions.DEFAULT) | 插入或更新文档 | IndexRequest request = new IndexRequest("products");request.id("101");request.source(Map.of("name", "Laptop", "price", 5999));client.index(request, RequestOptions.DEFAULT); | 需手动构建 IndexRequest;支持同步/异步(带 listener)。 |
| 搜索文档 | client.search(new SearchRequest(...), RequestOptions.DEFAULT) | 执行查询 | SearchRequest searchRequest = new SearchRequest("products");SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();sourceBuilder.query(QueryBuilders.matchQuery("name", "laptop"));searchRequest.source(sourceBuilder);SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT); | 使用 QueryBuilders 构建 DSL;结果需解析 SearchHit。 |
| 关闭客户端 | client.close() | 释放资源 | client.close(); | 必须在应用关闭时调用,否则连接泄漏。 |
| 异常处理 | 捕获 ElasticsearchException | 处理 ES 返回错误 | try { ... } catch (ElasticsearchException e) { if (e.status() == RestStatus.NOT_FOUND) { ... }} | 常见状态码:404(NOT_FOUND)、409(VERSION_CONFLICT)。 |
⚠️ 警告:新项目禁止使用此客户端,应迁移到 11.2 节介绍的 Java API Client。
11.2 Java API Client(Elasticsearch 8.x 官方推荐)
| 方法名称 | 语法 | 用途 | 代码示例 | 注意事项 |
|---|---|---|---|---|
| 创建客户端 | ElasticsearchClient client = new ElasticsearchClient(transport) | 初始化新客户端(基于 Java API) | ElasticsearchTransport transport = new RestClientTransport( RestClient.builder(new HttpHost("localhost", 9200)).build(), new JacksonJsonpMapper());ElasticsearchClient client = new ElasticsearchClient(transport); | 依赖 co.elastic.clients:elasticsearch-java 和 Jackson;自动处理 JSON 序列化。 |
| 索引文档 | client.index(b -> b.index("products").id("101").document(product)) | 插入文档(泛型安全) | Product product = new Product("Laptop", 5999);IndexResponse resp = client.index(b -> b .index("products") .id("101") .document(product)); | document() 接受 POJO;需有 getter/setter 或 Jackson 注解。 |
| 搜索文档 | client.search(s -> s.index("products").query(q -> q.match(t -> t.field("name").query("laptop"))), Product.class) | 类型安全查询 | SearchResponse<Product> response = client.search(b -> b .index("products") .query(q -> q.match(m -> m.field("name").query("laptop"))), Product.class);List<Product> hits = response.hits().hits().stream().map(h -> h.source()).collect(Collectors.toList()); | 查询 DSL 通过 lambda 构建;返回强类型结果;支持高亮、聚合等。 |
| 批量操作 | client.bulk(b -> b.operations(op -> op.index(i -> i.index("logs").document(log)))) | 执行 bulk 请求 | BulkResponse bulkResp = client.bulk(b -> b .operations(op -> op.index(i -> i.index("logs").document(log1))), op -> op.index(i -> i.index("logs").document(log2))); | 操作列表可动态构建;检查 bulkResp.items() 获取每项结果。 |
| 关闭客户端 | transport.close() | 释放底层连接 | transport.close(); | ElasticsearchClient 无 close 方法,需关闭 transport。 |
| 启用安全认证 | 在 RestClient 中配置 credentials | 连接启用了安全的 ES 集群 | RestClient.builder(...) .setHttpClientConfigCallback(hc -> hc.setDefaultCredentialsProvider( new BasicCredentialsProvider() {{ setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("elastic", "password")); }} )) | 生产环境必须启用 HTTPS + 认证;密码建议从配置中心读取。 |
11.3 Python / Go / Node.js 客户端示例
| 语言 | 安装方式 | 索引文档示例 | 搜索文档示例 | 注意事项 |
|---|---|---|---|---|
| Python | pip install elasticsearch | from elasticsearch import Elasticsearches = Elasticsearch([{'host': 'localhost', 'port': 9200}])es.index(index="products", id="101", document={"name": "Laptop", "price": 5999}) | resp = es.search(index="products", query={"match": {"name": "laptop"}})for hit in resp['hits']['hits']: print(hit['_source']) | 官方客户端;8.x 支持类型提示;连接需处理 SSL(若启用)。 |
| Go | go get github.com/elastic/go-elasticsearch/v8 | es, _ := elasticsearch.NewDefaultClient()res, _ := es.Index("products", strings.NewReader({“name”:“Laptop”,“price”:5999}), es.Index.WithDocumentID("101")) | res, _ := es.Search(es.Search.WithIndex("products"), es.Search.WithBody(strings.NewReader({“query”:{“match”:{“name”:“laptop”}}})))var r map[string]interface{}json.NewDecoder(res.Body).Decode(&r) | 需手动处理 JSON;支持 context 控制超时;连接池自动管理。 |
| Node.js | npm install @elastic/elasticsearch | const client = new Client({ node: 'http://localhost:9200' });await client.index({ index: 'products', id: '101', document: { name: 'Laptop', price: 5999 } }); | const result = await client.search({ index: 'products', query: { match: { name: 'laptop' } } });result.hits.hits.forEach(hit => console.log(hit._source)); | 官方 TypeScript 支持;Promise/async 友好;支持 AWS SigV4 认证。 |
✅ 通用建议:所有客户端均支持连接池、重试、超时配置;生产环境务必启用 TLS 和身份验证。
11.4 与 Spring Boot 集成
| 集成方式 | 说明 | 配置示例 | 注意事项 |
|---|---|---|---|
| Spring Data Elasticsearch(推荐) | 基于 Repository 抽象,自动生成 CRUD 方法 | application.yml:spring: elasticsearch: uris: http://localhost:9200@Document(indexName = "products")public class Product { ... }public interface ProductRepository extends ElasticsearchRepository<Product, String> {} | 仅支持部分 ES 功能;复杂查询需自定义方法或使用 ElasticsearchRestTemplate。 |
| ElasticsearchRestTemplate | 提供底层操作,支持完整 DSL | @AutowiredElasticsearchRestTemplate template;SearchHits<Product> hits = template.search( Query.findAll(), Product.class); | 需手动构建 Query 对象;适合高级搜索和聚合。 |
| Java API Client + Spring Bean | 直接注入官方 Java API Client | @Beanpublic ElasticsearchClient elasticsearchClient() { RestClient restClient = RestClient.builder(...).build(); return new ElasticsearchClient(new RestClientTransport(restClient, new JacksonJsonpMapper()));} | 最灵活,完全控制请求;需自行处理连接和异常。 |
| 自动配置安全认证 | 在 application.yml 中配置用户名密码 | spring: elasticsearch: username: elastic password: your_password | 密码不应硬编码;建议使用 Spring Cloud Config 或 Vault。 |
| 索引初始化 | 应用启动时自动创建索引和 mapping | @PostConstructpublic void initIndex() { if (!elasticsearchOperations.indexExists(Product.class)) { elasticsearchOperations.createIndex(Product.class); }} | 仅适用于开发环境;生产环境应通过 CI/CD 管理索引模板。 |
✅ 最佳实践:
- 简单 CRUD → Spring Data Elasticsearch
- 复杂查询/聚合 → ElasticsearchRestTemplate 或 Java API Client
- 避免在 Repository 中写复杂业务逻辑
第十二章:项目实战
12.1 博客文章全文搜索系统
| 模块 | 实现细节 | 配置/代码示例 | 注意事项 |
|---|---|---|---|
| 索引设计 | 字段:title(text + keyword)、content(text)、tags(keyword 数组)、publish_date(date) | PUT /blog_posts{ "mappings": { "properties": { "title": { "type": "text", "analyzer": "ik_max_word", "fields": { "keyword": { "type": "keyword" } } }, "content": { "type": "text", "analyzer": "ik_max_word" }, "tags": { "type": "keyword" }, "publish_date": { "type": "date" } } }} | 使用 IK 分词器处理中文;title 多字段兼顾搜索与排序。 |
| 高亮显示 | 在搜索结果中标记关键词 | POST /blog_posts/_search{ "query": { "match": { "content": "Elasticsearch" } }, "highlight": { "fields": { "content": {}, "title": {} } }} | 前端需安全渲染高亮 HTML(防 XSS);可自定义标签如 <mark>。 |
| 分页与排序 | 支持按发布时间倒序、相关性排序 | { "sort": [ { "publish_date": "desc" } ] }// 或{ "sort": [ { "_score": "desc" } ] } | 默认按 _score 排;时间排序时相关性失效,可结合 function_score。 |
| 前端集成 | 使用 Axios 调用 ES REST API(或通过后端代理) | // 后端 Spring Boot 示例@GetMapping("/search")public SearchResponse search(@RequestParam String q) { return client.search(s -> s .index("blog_posts") .query(qb -> qb.match(m -> m.field("content").query(q))) .highlight(h -> h.fields(Map.of("content", HighlightField.of()))), BlogPost.class );} | 禁止前端直连 ES(安全风险);应由后端封装 API 并做权限控制。 |
| 性能优化 | 冷热分离:新文章在 hot 节点,旧文章归档到 warm | ILM 策略:hot(7天)→ warm(只读,force merge)→ delete(180天) | 博客数据增长慢,可简化为单 tier;若访问量大,启用副本提升查询并发。 |
12.2 电商商品搜索(仿京东)
| 模块 | 实现细节 | 配置/代码示例 | 注意事项 |
|---|---|---|---|
| 复杂 mapping | 字段:name(text + ik_smart)、brand(keyword)、category_path(keyword 数组)、price(scaled_float)、attrs(nested:[{name, value}]) | "attrs": { "type": "nested", "properties": { "name": { "type": "keyword" }, "value": { "type": "keyword" } }} | nested 保证属性组合查询准确(如”颜色=红色 AND 尺寸=XL”)。 |
| 多条件筛选 | 品牌、价格区间、属性值组合过滤 | { "bool": { "filter": [ { "term": { "brand": "Apple" } }, { "range": { "price": { "gte": 5000, "lte": 10000 } } }, { "nested": { "path": "attrs", "query": { "term": { "attrs.name": "颜色" } } }}, { "nested": { "path": "attrs", "query": { "term": { "attrs.value": "银色" } } }} ]} } | nested 查询必须独立;多个 nested 条件需分别包裹。 |
| 聚合分析 | 获取品牌分布、价格区间统计、属性值枚举 | { "aggs": { "by_brand": { "terms": { "field": "brand", "size": 20 } }, "price_hist": { "histogram": { "field": "price", "interval": 1000 } }, "color_options": { "nested": { "path": "attrs" }, "aggs": { "colors": { "filter": { "term": { "attrs.name": "颜色" } }, "aggs": { "values": { "terms": { "field": "attrs.value" } } } } } }} } | nested 聚合需先声明 nested 聚合上下文;避免返回过多桶(限制 size)。 |
| 个性化排序 | 综合销量、评分、上新时间加权 | { "function_score": { "query": { ... }, "functions": [ { "field_value_factor": { "field": "sales_count", "modifier": "log1p" } }, { "gauss": { "publish_date": { "origin": "now", "scale": "30d" } } } ], "boost_mode": "sum"} } | 避免直接用原始销量(数值过大),用 log1p 平滑;时间衰减函数提升新品曝光。 |
| 搜索建议 | 输入”iph”提示”iPhone 15” | 使用 completion suggester:"suggest_field": { "type": "completion" }POST /products/_search{ "suggest": { "product-suggest": { "prefix": "iph", "completion": { "field": "suggest_field" } } } } | 需在索引时预填充 suggest 字段;支持权重(weight)控制排序。 |
12.3 金融产品信息检索平台
| 模块 | 实现细节 | 配置/代码示例 | 注意事项 |
|---|---|---|---|
| 高精度数值 | 利率、收益率等使用 scaled_float 避免浮点误差 | "annualized_return": { "type": "scaled_float", "scaling_factor": 10000 } | 存储 5.25% 为整数 52500;查询时仍用小数(ES 自动转换)。 |
| 严格权限控制 | 不同用户只能看到所属机构的产品 | 应用层实现:查询时注入 term 条件{ "term": { "institution_id": "user_org_id" } } | ES 无行级安全;必须由应用在查询中强制添加过滤条件。 |
| 多语言支持 | 产品名称含中英文,需分别分词 | "name": { "type": "text", "fields": { "zh": { "type": "text", "analyzer": "ik_max_word" }, "en": { "type": "text", "analyzer": "standard" } }} | 查询时根据用户语言选择字段:name.zh(中文用户)或 name.en(英文用户)。 |
| 合规性字段 | 风险等级、起购金额、锁定期等结构化字段 | "risk_level": { "type": "keyword" },"min_investment": { "type": "long" },"lock_period_days": { "type": "integer" } | 所有筛选条件基于 keyword/numeric,确保精确匹配。 |
| 审计日志 | 记录谁在何时搜索了哪些产品 | 应用层记录:Logger.info("User {} searched for {}", userId, query);→ 发送至独立 audit 索引 | 满足金融监管要求;日志需不可篡改(可写入只追加索引)。 |
12.4 微服务日志分析平台(ELK)
| 模块 | 实现细节 | 配置/代码示例 | 注意事项 |
|---|---|---|---|
| 采集架构 | Filebeat(各服务节点) → Kafka(缓冲) → Logstash(清洗) → Elasticsearch | filebeat.yml:output.kafka: hosts: ["kafka:9092"] topic: "app-logs"logstash.conf:input { kafka { topics => ["app-logs"] } }filter { json { source => "message" } }output { elasticsearch { index => "app-logs-%{+YYYY.MM.dd}" } } | Kafka 解耦,防 Logstash 故障导致日志丢失;Filebeat 使用 json codec 避免二次解析。 |
| 日志格式规范 | 所有微服务输出 JSON 格式日志 | {"@timestamp":"2025-01-01T12:00:00Z","level":"INFO","service":"order","trace_id":"abc123","message":"Order created"} | 必须包含 @timestamp、service、trace_id;便于关联和追踪。 |
| Kibana 可视化 | - 按服务统计错误率 - Top 异常堆栈 - Trace ID 全链路追踪 | Discover 中筛选 service: "payment" AND level: "ERROR"Lens 图表:X=时间,Y=count(),Split by=service | 使用 Logs 应用查看流式日志;APM 应用(需 Elastic APM Agent)实现分布式追踪。 |
| 告警规则 | 5 分钟内某服务 ERROR 日志突增 200% | Kibana Alerting: Rule type: “Log threshold” Index: “app-logs-*“ Criteria: count() > previous 5m * 3 | 结合 Anomaly Detection 自动学习基线;避免固定阈值误报。 |
| 冷热分层 | 热数据(7天)SSD,温数据(30天)HDD,冷数据(>30天)归档 | ILM 策略: hot → (7d) → warm(shrink to 1 shard, force merge) → (30d) → cold(frozen) → (365d) → delete | frozen tier 查询慢但成本极低;适合合规保留场景。 |
✅ 通用原则:
- 所有项目均需禁用动态 mapping(防止字段爆炸)
- 生产环境必须启用安全认证(TLS + RBAC)
- 关键业务索引定期备份(Snapshot to S3/NFS)