Skip to content

Elasticsearch 基础

Elasticsearch 是一个基于 Apache Lucene 的分布式、RESTful 风格的搜索和分析引擎,能够解决不断涌现的数据用例。作为 Elastic Stack(ELK)的核心组件,它以 JSON 文档形式存储数据,提供近实时的全文搜索能力,广泛应用于日志分析、搜索引擎、数据分析等场景。

前置知识

阅读本节前,建议先了解:HTTP 协议基础数据库基础概念

核心概念

什么是 Elasticsearch

Elasticsearch(简称 ES)是一个高度可扩展的开源全文搜索和分析引擎。它允许用户快速地存储、搜索和分析大量数据。ES 的核心特点包括:

  • 分布式架构:天然支持横向扩展,数据自动分布到多个节点
  • RESTful API:使用标准 HTTP 方法(GET、POST、PUT、DELETE)操作数据
  • 近实时搜索:数据写入后通常在 1 秒内即可被搜索到
  • JSON 文档模型:数据以 JSON 格式存储,结构灵活,支持动态映射
  • 全文搜索:基于 Lucene 提供强大的全文检索、模糊搜索、高亮等功能
  • 多语言支持:内置分词器支持中文、英文等多种语言的文本分析

与传统数据库对比

为了便于理解,可以将 Elasticsearch 与关系型数据库做如下类比:

ElasticsearchMySQL说明
IndexDatabase索引(数据库)
TypeTable类型(表),ES 7.x 后已废弃,8.x 中完全移除
DocumentRow文档(行),JSON 格式
FieldColumn字段(列)
MappingSchema映射(表结构定义)
Query DSLSQL查询语言
NodeServer instance节点(服务器实例)
ShardPartition分片(数据分区)
ReplicaReplica副本(数据备份)

注意

从 Elasticsearch 7.x 开始,Type 概念已被标记为废弃。在 8.x 中完全移除了 Type 相关 API。每个 Index 只能包含一个 Mapping 类型(_doc),类似于每个数据库只有一张表的概念被取消。设计索引时需要合理规划,避免将不同类型的文档混在同一个索引中。

倒排索引原理

正排索引与倒排索引

理解 Elasticsearch 的搜索能力,核心在于理解倒排索引(Inverted Index)

正排索引(Forward Index)是传统数据库使用的方式——以文档为基本单位,存储每个文档包含哪些词:

文档ID → [词1, 词2, 词3, ...]

倒排索引(Inverted Index)以词为基本单位,记录哪些文档包含该词:

词 → [文档ID1, 文档ID2, 文档ID3, ...]

倒排索引结构

倒排索引由以下三个核心部分组成:

  1. Term Dictionary(词典):存储经过分词器处理后的所有唯一词项(Term),并按字典序排列
  2. Term Index(词典索引):为词典建立的前缀索引(类似字典的目录页),帮助快速定位词项在词典中的位置,通常存储在内存中
  3. Posting List(倒排列表):记录包含某个词项的所有文档 ID、词频(TF)、位置信息等
用户搜索 "PHP 教程"

1. 分词器将查询拆分为 ["php", "教程"]

2. 在 Term Dictionary 中查找 "php" 和 "教程"

3. Term Index 辅助快速定位

4. 获取对应的 Posting List
   "php" → [Doc1, Doc3, Doc5, Doc7]
   "教程" → [Doc3, Doc5, Doc8]

5. 取交集:[Doc3, Doc5]

6. 根据相关性评分排序,返回结果

分词器(Analyzer)

分词器是文本分析的核心组件,负责将一段文本拆分成独立的词项。一个完整的分词器包含三个部分:

  1. Character Filters(字符过滤器):在分词之前对文本进行预处理,如去除 HTML 标签、替换特殊字符
  2. Tokenizer(分词器):将文本按照规则切分为词项
  3. Token Filters(词项过滤器):对分词结果进行后处理,如转小写、去除停用词、同义词转换
json
// 分词器处理流程示例
原文:"Hello World, PHP教程"
  ↓ Character Filter(去除标点)
"Hello World PHP教程"
  ↓ Tokenizer(按空格和字符切分)
["Hello", "World", "PHP教程"]
  ↓ Token Filter(转小写)
["hello", "world", "php教程"]

Elasticsearch 内置了多种分词器:

分词器说明适用场景
standard默认分词器,基于 Unicode 文本分段算法通用英文文本
simple按非字母字符分词,转小写简单英文处理
whitespace仅按空格分词需要精确匹配的场景
keyword不分词,整个文本作为一个词项ID、精确值字段
pattern使用正则表达式分词特殊分隔符文本
icu_collationICU 排序规则分词多语言排序
ik_max_wordIK 分词器(插件),最细粒度切分中文搜索
ik_smartIK 分词器(插件),智能切分中文粗粒度搜索

中文分词

Elasticsearch 默认的分词器对中文支持不好(会将每个汉字作为独立词项)。生产环境中,中文分词通常使用 IK Analysis Plugin

bash
# 安装 IK 分词器(版本需与 ES 版本一致)
./bin/elasticsearch-plugin install analysis-ik

ik_max_word 会将文本做最细粒度的拆分,ik_smart 会做最粗粒度的拆分。

集群架构

节点类型

Elasticsearch 集群由多个节点组成,不同节点承担不同角色:

节点角色配置职责
Master-eligible nodenode.master: true(默认)参与集群管理、索引创建、分片分配
Data nodenode.data: true(默认)存储数据、执行 CRUD 和搜索操作
Coordinating nodenode.master: false, node.data: false协调节点,分发请求、合并结果
Ingest nodenode.ingest: true(默认)数据预处理管道
Machine learning nodexpack.ml.enabled: true运行机器学习任务
yaml
# elasticsearch.yml 节点角色配置示例

# 专用 Master 节点(不存数据)
node.master: true
node.data: false
node.ingest: false
node.ml: false

# 专用 Data 节点
node.master: false
node.data: true
node.ingest: false

# 专用 Coordinating 节点
node.master: false
node.data: false
node.ingest: false

生产环境建议

  • 至少 3 个 Master-eligible 节点,避免脑裂(Split-brain)
  • 数据节点和主节点分离部署
  • 使用 Coordinating 节点分担查询压力
  • 每个节点单一角色,避免职责混乱

分片与副本

Elasticsearch 将每个索引划分为多个分片(Shard),每个分片是一个独立的 Lucene 实例。

  • Primary Shard(主分片):数据的原始存储,索引创建后主分片数不可更改
  • Replica Shard(副本分片):主分片的复制品,用于提高查询吞吐量和容灾
Index: products (3 primary shards, 1 replica)

Node 1          Node 2          Node 3
┌───────────┐  ┌───────────┐  ┌───────────┐
│ P0    R1   │  │ P1    R0   │  │ P2    R2   │
│           │  │           │  │           │
│ data shard│  │ data shard│  │ data shard│
│           │  │           │  │           │
└───────────┘  └───────────┘  └───────────┘

P0-P2 = Primary Shards (主分片)
R0-R2 = Replica Shards (副本分片)

路由机制:文档通过 routing 参数决定存储到哪个分片,默认使用文档 _id 进行路由计算:

shard = hash(routing) % num_primary_shards

集群状态

Elasticsearch 集群有三种健康状态:

状态颜色说明
Green绿色所有主分片和副本分片都正常分配
Yellow黄色所有主分片正常,但有副本分片未分配
Red红色有主分片未分配,部分数据不可用
bash
# 查看集群健康状态
curl -X GET "localhost:9200/_cluster/health?pretty"

# 响应示例
{
  "cluster_name" : "my-application",
  "status" : "green",
  "number_of_nodes" : 3,
  "number_of_data_nodes" : 2,
  "active_primary_shards" : 10,
  "active_shards" : 20,
  "relocating_shards" : 0,
  "initializing_shards" : 0,
  "unassigned_shards" : 0,
  "delayed_unassigned_shards" : 0,
  "number_of_pending_tasks" : 0
}

安装与配置

系统要求

在部署 Elasticsearch 之前,需要确认系统满足以下要求:

项目最低要求推荐配置
JDKOpenJDK 17+OpenJDK 21(ES 8.x 内置,无需单独安装)
内存最低 2GB每个节点 16GB+
CPU2 核8 核+
磁盘SSD 推荐NVMe SSD,预留 50% 空间
操作系统Linux / macOS / WindowsLinux(CentOS / Ubuntu LTS)

内存配置关键原则

  • 将 JVM 堆内存(-Xmx)设置为物理内存的 50%,但不超过 32GB
  • 不要超过物理内存的 50%,因为 ES 还需要内存给 Lucene 的文件系统缓存
  • 如果堆内存恰好是 31GB,JVM 会启用压缩指针(Compressed Oops),比 32GB 更高效
  • 使用 swapoff -a 禁用交换分区,或设置 vm.swappiness=1

安装方式

方式一:使用 Docker 安装(推荐开发环境)

bash
# 创建 Docker 网络
docker network create es-network

# 单节点模式
docker run -d \
  --name elasticsearch \
  --network es-network \
  -p 9200:9200 \
  -p 9300:9300 \
  -e "discovery.type=single-node" \
  -e "ES_JAVA_OPTS=-Xms1g -Xmx1g" \
  -e "xpack.security.enabled=false" \
  -e "xpack.security.enrollment.enabled=false" \
  docker.elastic.co/elasticsearch/elasticsearch:8.12.0

# 验证安装
curl http://localhost:9200

方式二:Docker Compose 三节点集群

yaml
# docker-compose.yml
version: '3.8'

services:
  es-node1:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.12.0
    container_name: es-node1
    environment:
      - node.name=es-node1
      - cluster.name=php-es-cluster
      - discovery.seed_hosts=es-node2,es-node3
      - cluster.initial_master_nodes=es-node1,es-node2,es-node3
      - ES_JAVA_OPTS=-Xms1g -Xmx1g
      - xpack.security.enabled=false
      - bootstrap.memory_lock=true
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - es-data1:/usr/share/elasticsearch/data
    ports:
      - "9200:9200"
    networks:
      - es-network

  es-node2:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.12.0
    container_name: es-node2
    environment:
      - node.name=es-node2
      - cluster.name=php-es-cluster
      - discovery.seed_hosts=es-node1,es-node3
      - cluster.initial_master_nodes=es-node1,es-node2,es-node3
      - ES_JAVA_OPTS=-Xms1g -Xmx1g
      - xpack.security.enabled=false
      - bootstrap.memory_lock=true
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - es-data2:/usr/share/elasticsearch/data
    networks:
      - es-network

  es-node3:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.12.0
    container_name: es-node3
    environment:
      - node.name=es-node3
      - cluster.name=php-es-cluster
      - discovery.seed_hosts=es-node1,es-node2
      - cluster.initial_master_nodes=es-node1,es-node2,es-node3
      - ES_JAVA_OPTS=-Xms1g -Xmx1g
      - xpack.security.enabled=false
      - bootstrap.memory_lock=true
    ulimits:
      memlock:
        soft: -1
        hard: -1
    volumes:
      - es-data3:/usr/share/elasticsearch/data
    networks:
      - es-network

  kibana:
    image: docker.elastic.co/kibana/kibana:8.12.0
    container_name: kibana
    environment:
      - ELASTICSEARCH_HOSTS=http://es-node1:9200
    ports:
      - "5601:5601"
    networks:
      - es-network
    depends_on:
      - es-node1

volumes:
  es-data1:
  es-data2:
  es-data3:

networks:
  es-network:
    driver: bridge
bash
# 启动集群
docker compose up -d

# 查看集群状态
curl -X GET "http://localhost:9200/_cluster/health?pretty"

方式三:Linux 直接安装

bash
# 1. 下载并安装 GPG 密钥
rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch

# 2. 添加 Elasticsearch 仓库
cat > /etc/yum.repos.d/elasticsearch.repo << 'EOF'
[elasticsearch]
name=Elasticsearch repository for 8.x packages
baseurl=https://artifacts.elastic.co/packages/8.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=1
autorefresh=1
type=rpm-md
EOF

# 3. 安装
yum install -y elasticsearch

# 4. 配置 JVM 内存
vim /etc/elasticsearch/jvm.options
# 修改以下行
# -Xms4g
# -Xmx4g

# 5. 启动并设置开机自启
systemctl daemon-reload
systemctl enable elasticsearch
systemctl start elasticsearch

# 6. 验证
curl http://localhost:9200

核心配置文件

Elasticsearch 的主配置文件是 elasticsearch.yml

yaml
# ==================================== 集群配置 ====================================
# 集群名称,同一集群的所有节点必须相同
cluster.name: php-es-cluster

# 节点名称
node.name: node-1

# 节点角色
node.roles: [ master, data ]

# 数据和日志存储路径
path.data: /var/lib/elasticsearch
path.logs: /var/log/elasticsearch

# ==================================== 网络配置 ====================================
# 绑定的网络地址
network.host: 0.0.0.0

# HTTP 端口
http.port: 9200

# 传输端口(节点间通信)
transport.port: 9300

# ==================================== 发现与集群管理 ====================================
# 集群中发现其他节点
discovery.seed_hosts: ["host1:9300", "host2:9300"]
cluster.initial_master_nodes: ["node-1", "node-2", "node-3"]

# ==================================== 内存配置 ====================================
# 启动时锁定内存(防止被 swap)
bootstrap.memory_lock: true

# ==================================== 索引配置 ====================================
# 默认分片数
index.number_of_shards: 3
index.number_of_replicas: 1

# ==================================== 安全配置 ====================================
# 开启安全认证(生产环境必须)
xpack.security.enabled: true
xpack.security.transport.ssl.enabled: true

JVM 配置

properties
# /etc/elasticsearch/jvm.options

# 堆内存大小(建议不超过物理内存的 50%,且不超过 32GB)
-Xms4g
-Xmx4g

# GC 配置(ES 8.x 默认使用 G1GC)
# G1GC 适用于大堆内存场景
-XX:+UseG1GC

# GC 日志
-Xlog:gc*,gc+age=trace,safepoint:file=logs/gc.log:utctime,uptime,pid,tags:filecount=32,filesize=64m

# 堆外内存限制
-XX:MaxDirectMemorySize=2g

核心操作 API

索引操作

bash
# 创建索引
curl -X PUT "localhost:9200/products" -H 'Content-Type: application/json' -d '{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1
  },
  "mappings": {
    "properties": {
      "title": { "type": "text", "analyzer": "ik_max_word" },
      "description": { "type": "text", "analyzer": "ik_smart" },
      "price": { "type": "float" },
      "category": { "type": "keyword" },
      "created_at": { "type": "date" },
      "is_active": { "type": "boolean" }
    }
  }
}'

# 查看索引信息
curl -X GET "localhost:9200/products?pretty"

# 查看索引映射
curl -X GET "localhost:9200/products/_mapping?pretty"

# 删除索引
curl -X DELETE "localhost:9200/products"

# 关闭 / 打开索引
curl -X POST "localhost:9200/products/_close"
curl -X POST "localhost:9200/products/_open"

# 查看所有索引
curl -X GET "localhost:9200/_cat/indices?v"

文档操作

bash
# 创建文档(指定 ID)
curl -X PUT "localhost:9200/products/_doc/1" -H 'Content-Type: application/json' -d '{
  "title": "PHP 高级编程",
  "description": "深入理解 PHP 核心特性与设计模式",
  "price": 89.90,
  "category": "编程书籍",
  "created_at": "2024-01-15",
  "is_active": true
}'

# 创建文档(自动生成 ID)
curl -X POST "localhost:9200/products/_doc" -H 'Content-Type: application/json' -d '{
  "title": "Laravel 从入门到精通",
  "description": "Laravel 框架全面教程",
  "price": 69.90,
  "category": "编程书籍",
  "created_at": "2024-02-20",
  "is_active": true
}'

# 获取文档
curl -X GET "localhost:9200/products/_doc/1?pretty"

# 更新文档(部分更新)
curl -X POST "localhost:9200/products/_update/1" -H 'Content-Type: application/json' -d '{
  "doc": {
    "price": 79.90
  }
}'

# 删除文档
curl -X DELETE "localhost:9200/products/_doc/1"

# 批量操作(_bulk API)
curl -X POST "localhost:9200/_bulk" -H 'Content-Type: application/json' -d '
{"index": {"_index": "products", "_id": "2"}}
{"title": "Redis 实战", "description": "Redis 深度学习指南", "price": 59.90, "category": "编程书籍"}
{"index": {"_index": "products", "_id": "3"}}
{"title": "Docker 容器化部署", "description": "Docker 实践教程", "price": 49.90, "category": "运维"}
{"delete": {"_index": "products", "_id": "2"}}
'

基础查询

bash
# 匹配所有文档
curl -X GET "localhost:9200/products/_search" -H 'Content-Type: application/json' -d '{
  "query": {
    "match_all": {}
  }
}'

# 全文搜索(match)
curl -X GET "localhost:9200/products/_search" -H 'Content-Type: application/json' -d '{
  "query": {
    "match": {
      "title": "PHP 编程"
    }
  }
}'

# 精确匹配(term)
curl -X GET "localhost:9200/products/_search" -H 'Content-Type: application/json' -d '{
  "query": {
    "term": {
      "category": "编程书籍"
    }
  }
}'

# 范围查询
curl -X GET "localhost:9200/products/_search" -H 'Content-Type: application/json' -d '{
  "query": {
    "range": {
      "price": {
        "gte": 50.0,
        "lte": 100.0
      }
    }
  }
}'

# 布尔查询
curl -X GET "localhost:9200/products/_search" -H 'Content-Type: application/json' -d '{
  "query": {
    "bool": {
      "must": [
        { "match": { "description": "教程" } }
      ],
      "filter": [
        { "term": { "category": "编程书籍" } },
        { "range": { "price": { "gte": 50 } } }
      ]
    }
  }
}'

数据类型详解

Elasticsearch 支持丰富的数据类型,正确选择数据类型对性能和功能至关重要。

核心数据类型

json
{
  "mappings": {
    "properties": {
      "string_text":    { "type": "text" },
      "string_keyword": { "type": "keyword" },
      "integer_field":  { "type": "integer" },
      "long_field":     { "type": "long" },
      "float_field":    { "type": "float" },
      "double_field":   { "type": "double" },
      "boolean_field":  { "type": "boolean" },
      "date_field":     { "type": "date" },
      "binary_field":   { "type": "binary" }
    }
  }
}

text vs keyword

这是 Elasticsearch 中最容易混淆的两个字符串类型:

特性textkeyword
用途全文搜索精确匹配、排序、聚合
分词会分词不分词
索引建立倒排索引建立正排索引(Doc Values)
查询match、match_phraseterm、terms
长度限制无严格限制默认最大 256 字符(可调整 ignore_above
json
// 同时支持全文搜索和精确匹配
{
  "title": {
    "type": "text",
    "fields": {
      "keyword": {
        "type": "keyword",
        "ignore_above": 256
      },
      "autocomplete": {
        "type": "search_as_you_type"
      }
    },
    "analyzer": "ik_max_word",
    "search_analyzer": "ik_smart"
  }
}
// 搜索全文:title
// 精确匹配:title.keyword
// 自动补全:title.autocomplete

数值类型

类型范围字节数
byte-128 ~ 1271
short-32768 ~ 327672
integer-2^31 ~ 2^31-14
long-2^63 ~ 2^63-18
float单精度 32 位 IEEE 7544
double双精度 64 位 IEEE 7548
half_float半精度 16 位 IEEE 7542
scaled_float缩放浮点数(如价格)可变

价格字段建议

对于价格字段,推荐使用 scaled_float 配合 scaling_factor,或直接使用 long 类型存储分为单位(如 ¥89.90 存为 8990),避免浮点精度问题:

json
{ "price_cents": { "type": "long" } }
// 或
{ "price": { "type": "scaled_float", "scaling_factor": 100 } }

日期类型

json
{
  "created_at": {
    "type": "date",
    "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
  }
}

支持的日期格式:

  • epoch_millis:毫秒时间戳(推荐,最精确)
  • epoch_second:秒时间戳
  • 自定义格式,用 || 分隔多种格式
  • 内置格式:strict_date_optional_timebasic_date

复杂数据类型

json
{
  "properties": {
    // 对象类型(嵌套 JSON 对象)
    "author": {
      "properties": {
        "name": { "type": "keyword" },
        "email": { "type": "keyword" }
      }
    },
    // 数组类型(同类型数组,直接使用即可)
    "tags": { "type": "keyword" },
    // 嵌套类型(独立对象,解决对象数组扁平化问题)
    "comments": {
      "type": "nested",
      "properties": {
        "content": { "type": "text" },
        "rating": { "type": "integer" },
        "user": { "type": "keyword" }
      }
    },
    // 地理坐标类型
    "location": { "type": "geo_point" },
    // 地理形状类型
    "area": { "type": "geo_shape" },
    // IP 类型
    "ip_address": { "type": "ip" },
    // 自动补全类型
    "suggest": { "type": "completion" }
  }
}

nested vs object

  • object 类型会将嵌套对象扁平化存储,导致对象间的关联关系丢失
  • 例如 comments 数组中两条评论的 usercontent 会失去关联
  • nested 类型将每个嵌套对象作为独立文档存储,可保持对象内部字段的关系
  • nested 查询性能较差,非必要不要使用

实战示例:构建商品搜索引擎

以下是一个完整的商品搜索引擎初始化示例,展示如何从零开始设计索引。

bash
# Step 1: 创建商品索引
curl -X PUT "localhost:9200/products" -H 'Content-Type: application/json' -d '{
  "settings": {
    "number_of_shards": 5,
    "number_of_replicas": 1,
    "analysis": {
      "analyzer": {
        "pinyin_analyzer": {
          "type": "custom",
          "tokenizer": "ik_max_word",
          "filter": ["lowercase", "my_pinyin"]
        }
      },
      "filter": {
        "my_pinyin": {
          "type": "pinyin",
          "keep_first_letter": true,
          "keep_separate_first_letter": false,
          "keep_full_pinyin": true,
          "keep_original": true,
          "limit_first_letter_length": 16,
          "lowercase": true
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "product_id": {
        "type": "keyword"
      },
      "title": {
        "type": "text",
        "analyzer": "ik_max_word",
        "search_analyzer": "ik_smart",
        "fields": {
          "keyword": { "type": "keyword", "ignore_above": 128 },
          "pinyin": {
            "type": "text",
            "analyzer": "pinyin_analyzer"
          },
          "completion": {
            "type": "completion",
            "analyzer": "ik_max_word"
          }
        }
      },
      "description": {
        "type": "text",
        "analyzer": "ik_max_word",
        "search_analyzer": "ik_smart"
      },
      "brand": {
        "type": "keyword"
      },
      "category_path": {
        "type": "keyword"
      },
      "price": {
        "type": "scaled_float",
        "scaling_factor": 100
      },
      "original_price": {
        "type": "scaled_float",
        "scaling_factor": 100
      },
      "sales_count": {
        "type": "integer"
      },
      "rating": {
        "type": "float"
      },
      "stock": {
        "type": "integer"
      },
      "is_on_sale": {
        "type": "boolean"
      },
      "tags": {
        "type": "keyword"
      },
      "attributes": {
        "type": "nested",
        "properties": {
          "name": { "type": "keyword" },
          "value": { "type": "keyword" }
        }
      },
      "shop_id": {
        "type": "keyword"
      },
      "location": {
        "type": "geo_point"
      },
      "created_at": {
        "type": "date",
        "format": "yyyy-MM-dd HH:mm:ss||epoch_millis"
      },
      "updated_at": {
        "type": "date",
        "format": "yyyy-MM-dd HH:mm:ss||epoch_millis"
      }
    }
  }
}'

# Step 2: 插入测试数据
curl -X POST "localhost:9200/products/_bulk" -H 'Content-Type: application/json' -d '
{"index":{"_id":"1001"}}
{"product_id":"SKU-1001","title":"PHP高级编程指南","description":"涵盖PHP8新特性、设计模式、性能优化的全面指南","brand":"技术出版社","category_path":["编程","PHP"],"price":89.90,"original_price":129.90,"sales_count":1523,"rating":4.8,"stock":500,"is_on_sale":true,"tags":["PHP","编程","后端"],"shop_id":"SHOP-001","location":{"lat":39.9087,"lon":116.3975},"created_at":"2024-01-15 10:30:00","updated_at":"2024-06-01 15:20:00"}
{"index":{"_id":"1002"}}
{"product_id":"SKU-1002","title":"Laravel框架实战教程","description":"从零开始学习Laravel框架,包含项目实战案例","brand":"技术出版社","category_path":["编程","Laravel"],"price":69.90,"original_price":99.90,"sales_count":2341,"rating":4.9,"stock":800,"is_on_sale":true,"tags":["Laravel","PHP","框架"],"shop_id":"SHOP-001","location":{"lat":39.9087,"lon":116.3975},"created_at":"2024-02-20 14:00:00","updated_at":"2024-05-28 09:10:00"}
{"index":{"_id":"1003"}}
{"product_id":"SKU-1003","title":"MySQL数据库优化手册","description":"MySQL索引优化、SQL调优、主从复制全解析","brand":"数据库出版社","category_path":["编程","数据库","MySQL"],"price":79.90,"original_price":109.90,"sales_count":987,"rating":4.7,"stock":300,"is_on_sale":true,"tags":["MySQL","数据库","优化"],"shop_id":"SHOP-002","location":{"lat":31.2304,"lon":121.4737},"created_at":"2024-03-10 08:45:00","updated_at":"2024-06-05 11:30:00"}
'

# Step 3: 测试搜索
curl -X GET "localhost:9200/products/_search" -H 'Content-Type: application/json' -d '{
  "query": {
    "bool": {
      "must": [
        { "match": { "title": "PHP" } }
      ],
      "filter": [
        { "range": { "price": { "gte": 50, "lte": 100 } } }
      ],
      "should": [
        { "term": { "tags": "编程" } },
        { "range": { "rating": { "gte": 4.5 } } }
      ]
    }
  },
  "sort": [
    { "_score": "desc" },
    { "sales_count": "desc" }
  ],
  "highlight": {
    "fields": {
      "title": {}
    }
  }
}'

注意事项

近实时机制

Elasticsearch 默认每 1 秒执行一次 refresh 操作,将内存中的索引数据写入新的 Lucene Segment。这意味着写入的数据不会立刻被搜索到:

bash
# 修改 refresh 间隔(需要权衡搜索实时性和写入性能)
# 创建索引时设置
curl -X PUT "localhost:9200/products" -H 'Content-Type: application/json' -d '{
  "settings": {
    "index.refresh_interval": "30s"
  }
}'

# 临时禁用 refresh(大批量导入时使用)
curl -X PUT "localhost:9200/products/_settings" -H 'Content-Type: application/json' -d '{
  "index": {
    "refresh_interval": "-1"
  }
}'

# 导入完成后恢复
curl -X PUT "localhost:9200/products/_settings" -H 'Content-Type: application/json' -d '{
  "index": {
    "refresh_interval": "1s"
  }
}'

写入优化

bash
# 批量写入时的优化配置
curl -X PUT "localhost:9200/products/_settings" -H 'Content-Type: application/json' -d '{
  "index": {
    "refresh_interval": "-1",
    "number_of_replicas": 0,
    "translog.durability": "async",
    "translog.sync_interval": "5s"
  }
}'

# 写入完成后恢复
curl -X PUT "localhost:9200/products/_settings" -H 'Content-Type: application/json' -d '{
  "index": {
    "refresh_interval": "1s",
    "number_of_replicas": 1,
    "translog.durability": "request"
  }
}'

最佳实践

  1. 索引设计:根据业务场景合理规划分片数量,单个分片建议不超过 50GB
  2. 数据类型:精确匹配使用 keyword,全文搜索使用 text;价格使用 scaled_floatlong
  3. 映射预定义:生产环境禁止使用动态映射(dynamic: falsestrict
  4. 批量操作:写入时使用 _bulk API,每批 1000~5000 条文档
  5. 资源规划:JVM 堆内存不超过物理内存 50%,预留空间给文件系统缓存

下一节

继续学习:PHP 客户端操作

参考链接