Elasticsearch 详解:从入门到上手
·
Elasticsearch 详解:从入门到上手
本文面向 ES 初学者,从安装配置讲起,通过 RESTful API 演示索引管理、文档 CRUD、简单查询、DSL 查询与过滤、映射配置等核心操作,帮助读者快速上手 Elasticsearch。
目录
一、ES 是什么
1.1 基本概念
Elasticsearch(ES)是一个分布式、RESTful 风格的全文搜索引擎,基于 Apache Lucene 构建。
1.2 ES 的八大特点
① 基于 Lucene → 底层是高性能的全文搜索库
② 使用简单 → 全部通过 HTTP JSON 操作
③ 分布式 → 天然支持集群、分片、复制
④ RESTful API → 使用 HTTP 方法(GET/POST/PUT/DELETE)
⑤ 多语言客户端 → Java、PHP、Python、Go 等
⑥ PB 级数据 → 擅长处理海量数据
⑦ 高性能近实时 → 写入后 1 秒可搜索
⑧ JSON 格式 → 所有数据以 JSON 存储和传输
1.3 ES 的应用场景
┌─ 全文搜索:电商商品搜索、博客内容搜索
├─ 日志分析:ELK 栈(ES + Logstash + Kibana)
├─ 指标监控:应用性能监控、业务指标聚合
├─ 推荐系统:基于搜索的相关推荐
└─ 安全分析:入侵检测、异常行为分析
二、安装与启动
2.1 安装 ES
# ===== 前置条件 =====
# 需要 JDK 8 或更高版本(ES 7.x 自带 JDK)
# ===== 下载 ES =====
# 方式 1:官网下载(推荐)
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.17.0-linux-x86_64.tar.gz
# 方式 2:Docker 安装(最方便)
docker pull elasticsearch:7.17.0
docker run -d --name es -p 9200:9200 -p 9300:9300 \
-e "discovery.type=single-node" \
elasticsearch:7.17.0
# ===== 解压安装 =====
tar -zxvf elasticsearch-7.17.0-linux-x86_64.tar.gz
cd elasticsearch-7.17.0
# ===== 启动 ES =====
# Linux/Mac
./bin/elasticsearch
# Windows
bin\elasticsearch.bat
# ===== 验证启动 =====
curl http://localhost:9200
# 返回示例
{
"name" : "node-1",
"cluster_name" : "elasticsearch",
"cluster_uuid" : "...",
"version" : {
"number" : "7.17.0",
"build_flavor" : "default",
"build_type" : "tar",
"lucene_version" : "8.11.1"
},
"tagline" : "You Know, for Search"
}
2.2 安装 Kibana(可视化工具)
# ===== Docker 安装 Kibana(推荐) =====
docker pull kibana:7.17.0
docker run -d --name kibana --link es:elasticsearch \
-p 5601:5601 kibana:7.17.0
# 访问:http://localhost:5601
# Kibana 的 Dev Tools 是 ES 学习的最佳工具!
# ===== 或者安装 elasticsearch-head =====
# Chrome 插件:直接搜索安装 Elasticsearch Head
# 或 Docker 安装:
docker run -d -p 9100:9100 mobz/elasticsearch-head:5
# 访问:http://localhost:9100
2.3 解决跨域问题
# 如果使用 Head 插件或前端直接访问,需要配置跨域
# elasticsearch.yml 中添加:
http.cors.enabled: true
http.cors.allow-origin: "*"
三、RESTful API 回顾
3.1 RESTful 风格
RESTful 是一种 API 设计风格,使用名词表示资源,使用HTTP 动词表示操作。
# ===== HTTP 动词在 ES 中的映射 =====
GET → 查询资源(查询文档/索引)
POST → 创建资源 / 更新局部
PUT → 创建/替换资源
DELETE → 删除资源
HEAD → 检查资源是否存在
3.2 ES 的 RESTful 路径格式
HTTP 方法 /索引名称/文档类型/文档ID
示例:
PUT /crm/employee/1 → 创建文档 ID=1
GET /crm/employee/1 → 查询文档 ID=1
POST /crm/employee → 创建文档(自动生成 ID)
POST /crm/employee/1/_update → 更新文档 ID=1
DELETE /crm/employee/1 → 删除文档 ID=1
四、ES 核心概念
4.1 ES vs 关系型数据库
ES MySQL 说明
────────── ─────────── ──────────────────────
Index(索引) Database 数据库
Type(类型) Table 表(ES 7.x 废弃,8.x 移除)
Document(文档) Row 行
Field(字段) Column 列
Mapping(映射) Schema 表结构
DSL SQL 查询语言
ES 7.x 开始,一个索引只有一种类型 _doc
4.2 核心术语详解
# ===== ① Index(索引) =====
# 类似数据库,存储具有相似特征的文档集合
# 命名规则:全部小写,不能包含特殊字符
# ===== ② Type(文档类型)=====
# ES 6.x 之前一个索引可以有多个类型
# ES 7.x 之后 type 被弱化,固定为 _doc
# ES 8.x 完全移除 type
# ===== ③ Document(文档) =====
# 最小数据单元,JSON 格式
# 每个文档有唯一的 _id
# ===== ④ Field(字段) =====
# 文档中的一个键值对,相当于列
# ===== ⑤ Cluster(集群) =====
# 包含多个节点,默认名称 "elasticsearch"
# 同一集群的节点具有相同的 cluster.name
# ===== ⑥ Node(节点) =====
# 一个 ES 实例(一个进程、一台服务器)
# 加入集群后自动分配角色
# ===== ⑦ Shard(分片) =====
# 一个索引分成多个分片,分散在不同节点
# 类似于数据库的分库分表
# 主分片数创建时指定,不可修改
# ===== ⑧ Replica(副本) =====
# 主分片的复制品,提供高可用
# 主分片和它的副本不能在同一节点
4.3 分片与副本的关系
索引:employee(3 个主分片,1 个副本)
node-1 node-2 node-3
────── ────── ──────
主分片: P1 P2 P3
副本: R2 R3 R1
特点:
├─ 主分片(P)和它的副本(R)不在同一节点
├─ 主分片数量创建后不能修改
├─ 副本数量可以动态调整
└─ 节点越多,数据分布越均衡
五、文档 CRUD 操作
5.1 创建索引
# ===== 创建索引 =====
PUT /crm
{
"settings": {
"number_of_shards": 3, # 主分片数
"number_of_replicas": 1 # 副本数
}
}
# 返回
{
"acknowledged": true,
"shards_acknowledged": true,
"index": "crm"
}
# ===== 查看索引 =====
GET /crm
# ===== 查看所有索引 =====
GET _cat/indices?v
# 返回
# health status index uuid pri rep docs.count ...
# yellow open crm ... 3 1 0
# ===== 删除索引 =====
DELETE /crm
5.2 创建文档(PUT)
# ===== 创建文档(指定 ID)=====
PUT /crm/employee/1
{
"id": 1,
"name": "张三",
"age": 25,
"city": "北京",
"salary": 15000,
"skills": ["Java", "Spring", "MySQL"]
}
# 返回
{
"_index": "crm",
"_type": "employee",
"_id": "1",
"_version": 1,
"result": "created",
"_shards": { "total": 2, "successful": 1, "failed": 0 }
}
# 注意:PUT 必须指定 ID,如果 ID 已存在则更新(全量替换)
5.3 创建文档(POST — 自动生成 ID)
# ===== 创建文档(不指定 ID,自动生成) =====
POST /crm/employee/
{
"id": 2,
"name": "李四",
"age": 30,
"city": "上海",
"salary": 20000
}
# 返回:自动生成了 _id(如:WQxAbXMB3d5x8WfUj2a_)
5.4 获取文档
# ===== 查询文档(根据 ID)=====
GET /crm/employee/1
# 返回
{
"_index": "crm",
"_type": "employee",
"_id": "1",
"_version": 2,
"_seq_no": 1,
"_primary_term": 1,
"found": true, # true=找到,false=不存在
"_source": { # 文档原始 JSON 内容
"id": 1,
"name": "张三",
"age": 25,
"city": "北京",
"salary": 15000,
"skills": ["Java", "Spring", "MySQL"]
}
}
# ===== 只返回 _source 字段 =====
GET /crm/employee/1/_source
# 返回:{"id":1,"name":"张三","age":25,"city":"北京","salary":15000}
5.5 更新文档
# ===== 方式 1:PUT 全量替换 =====
# 整个文档被替换(丢失未提供的字段)
PUT /crm/employee/1
{
"id": 1,
"name": "张三",
"salary": 18000 # 只传了这些字段,其他字段会丢失
}
# ===== 方式 2:POST 局部更新(推荐)=====
POST /crm/employee/1/_update
{
"doc": {
"salary": 18000, # 只更新 salary 字段
"age": 26 # 其他字段保持不变
}
}
# 返回
{
"_index": "crm",
"_type": "employee",
"_id": "1",
"_version": 3,
"result": "updated"
}
5.6 删除文档
# ===== 删除指定文档 =====
DELETE /crm/employee/1
# 返回
{
"_index": "crm",
"_type": "employee",
"_id": "1",
"_version": 4,
"result": "deleted"
}
# ===== 批量获取 =====
GET _mget
{
"docs": [
{ "_index": "crm", "_type": "employee", "_id": "1" },
{ "_index": "crm", "_type": "employee", "_id": "2" },
{ "_index": "crm", "_type": "employee", "_id": "3" }
]
}
5.7 CRUD 速查表
操作 方法 路径 说明
───── ───── ──────────────────────────────────── ────────────────
创建索引 PUT /index settings + mappings
删除索引 DELETE /index
创建文档 PUT /index/type/id 指定 ID
创建文档 POST /index/type 自动生成 ID
查询文档 GET /index/type/id
局部更新 POST /index/type/id/_update {"doc": {...}}
全量更新 PUT /index/type/id 替换整个文档
删除文档 DELETE /index/type/id
批量获取 GET _mget {"docs": [...]}
六、简单查询
6.1 准备数据
# 先准备测试数据
PUT /crm/employee/1
{"id":1,"name":"张三","age":25,"city":"北京","salary":15000,"skills":["Java","Spring"]}
PUT /crm/employee/2
{"id":2,"name":"李四","age":30,"city":"上海","salary":20000,"skills":["Python","Django"]}
PUT /crm/employee/3
{"id":3,"name":"王五","age":28,"city":"北京","salary":18000,"skills":["Java","Elasticsearch"]}
PUT /crm/employee/4
{"id":4,"name":"赵六","age":35,"city":"深圳","salary":25000,"skills":["Go","Docker"]}
PUT /crm/employee/5
{"id":5,"name":"陈七","age":22,"city":"广州","salary":12000,"skills":["Java","Vue"]}
6.2 查询所有
# ===== 空查询(查询所有文档)=====
GET _search
6.3 字符串查询(URI Search)
# ===== 基本语法 =====
# GET _search?q=字段:值
# 按名称查询
GET /crm/employee/_search?q=name:张三
# 按条件查询 + 分页
GET /crm/employee/_search?q=city:北京&from=0&size=2
# 按范围查询
GET _search?q=age:[25 TO 35]
# 多条件组合
GET _search?q=city:北京 AND salary>=15000
# 缺点:复杂查询不适合用 URI 方式
6.4 分页查询
# ===== 分页查询 =====
# size:每页条数
# from:偏移量(从第几条开始)
# 第 1 页(每页 2 条)
GET /crm/employee/_search?size=2&from=0
# 第 2 页
GET /crm/employee/_search?size=2&from=2
# 第 3 页
GET /crm/employee/_search?size=2&from=4
七、DSL 查询与过滤
7.1 什么是 DSL
DSL(Domain Specific Language)是 ES 的基于 JSON 的查询语言,比 URI 方式更强大、更灵活。
# DSL 的基本语法结构
GET /索引/类型/_search
{
"query": {
"查询类型": {
"字段名": "条件"
}
},
"from": 0,
"size": 10,
"_source": ["字段1", "字段2"],
"sort": [...]
}
7.2 match_all(查询所有)
# ===== match_all =====
GET /crm/employee/_search
{
"query": {
"match_all": {}
},
"from": 0,
"size": 10,
"_source": ["name", "age", "salary"],
"sort": [
{ "salary": { "order": "desc" } },
{ "age": { "order": "asc" } }
]
}
7.3 match(标准查询 — 分词匹配)
# ===== match 查询 =====
# 会对查询值进行分词,任何词匹配都算匹配
# 类似 MySQL 的 LIKE,但 ES 的精确度更高
GET /crm/employee/_search
{
"query": {
"match": {
"name": "张三"
}
}
}
# 多词匹配(只要匹配一个词就返回)
GET /crm/employee/_search
{
"query": {
"match": {
"skills": "Java Python"
}
}
}
# 匹配"Java"或"Python"的文档都会返回
7.4 term(单词查询 — 精确匹配)
# ===== term 查询 =====
# 不会分词,精确匹配整个词
# 适合 keyword 类型、数字、日期、布尔值
GET /crm/employee/_search
{
"query": {
"term": {
"city": {
"value": "北京"
}
}
}
}
# ===== terms(多值精确匹配)=====
GET /crm/employee/_search
{
"query": {
"terms": {
"city": ["北京", "上海"]
}
}
}
7.5 range(范围查询)
# ===== range 范围查询 =====
# gt > gte >=
# lt < lte <=
GET /crm/employee/_search
{
"query": {
"range": {
"age": {
"gte": 25,
"lte": 35
}
}
}
}
# 也可以用于日期范围
GET /logs/_search
{
"query": {
"range": {
"@timestamp": {
"gte": "2024-01-01",
"lte": "2024-12-31"
}
}
}
}
7.6 bool(组合查询)
# ===== bool 查询 =====
# bool 是最强大的组合查询,包含:
# must → 必须满足(类似 AND),参与得分
# must_not → 必须不满足(类似 NOT)
# should → 满足更好(类似 OR),提高得分
# filter → 必须满足(类似 AND),不参与得分(可缓存,性能好)
GET /crm/employee/_search
{
"query": {
"bool": {
"must": [
{ "match": { "name": "张三" } }
],
"must_not": [
{ "term": { "age": 30 } }
],
"should": [
{ "match": { "skills": "Java" } }
],
"filter": [
{ "term": { "city": "北京" } },
{ "range": { "salary": { "gte": 10000, "lte": 20000 } } }
]
}
}
}
7.7 常见的 DSL 查询类型
# ===== ① 标准查询:match =====
{ "match": { "name": "张三" } }
# ===== ② 单词查询:term(精确匹配)=====
{ "term": { "city": "北京" } }
# ===== ③ 组合查询:bool =====
{ "bool": { "must": [...], "filter": [...], "should": [...] } }
# ===== ④ 范围查询:range =====
{ "range": { "age": { "gte": 20, "lte": 30 } } }
# ===== ⑤ 是否存在:exists =====
{ "exists": { "field": "email" } }
# ===== ⑥ 前缀匹配:prefix =====
{ "prefix": { "name": "张" } }
# ===== ⑦ 通配符:wildcard =====
{ "wildcard": { "name": "张*" } }
# * 匹配多个字符,? 匹配一个字符
# ===== ⑧ 模糊查询:fuzzy =====
{ "fuzzy": { "name": "张" } }
7.8 DSL 查询与过滤的区别
Query(查询上下文)vs Filter(过滤上下文)
Query Filter
┌──────────────────────┐ ┌──────────────────────┐
│ 计算 _score │ │ 不计算 _score │
│ 决定"有多匹配" │ │ 判断"匹配/不匹配" │
│ 结果不被缓存 │ │ 结果可被缓存 │
│ 性能相对较低 │ │ 性能更好 │
│ 适用:全文搜索 │ │ 适用:精确匹配 │
│ match │ │ term │
│ match_phrase │ │ terms │
│ query_string │ │ range │
└──────────────────────┘ └──────────────────────┘
建议:精确条件放在 filter 中(缓存 + 高性能)
全文搜索放在 must/should 中(计算相关性)
八、映射 Mapping
8.1 什么是 Mapping
Mapping 定义了索引中字段的名称、类型、分词器等,相当于 MySQL 的 CREATE TABLE。
# ===== 查看索引的 mapping =====
GET /crm/_mapping
# 返回
{
"crm": {
"mappings": {
"properties": {
"id": { "type": "long" },
"name": {
"type": "text",
"fields": {
"keyword": { "type": "keyword", "ignore_above": 256 }
}
},
"age": { "type": "integer" },
"city": { "type": "text" },
"salary": { "type": "float" },
"skills": { "type": "text" }
}
}
}
}
# 注意:不指定 mapping 时,ES 会自动推断字段类型(动态映射)
8.2 基本字段类型
# ===== 字符串类型 =====
text → 分词,支持全文搜索、模糊匹配
keyword → 不分词,支持精确匹配、排序、聚合
# ===== 数值类型 =====
byte → 8 位整数(-128 ~ 127)
short → 16 位整数
integer → 32 位整数
long → 64 位整数
float → 32 位浮点数
double → 64 位浮点数
# ===== 日期类型 =====
date → 支持多种格式,如:"2024-01-01"
"2024-01-01 12:00:00"、"2024/01/01"
# ===== 布尔类型 =====
boolean → true / false
# ===== 特殊类型 =====
ip → IP 地址
geo_point → 地理位置(经纬度)
binary → Base64 编码的二进制
8.3 自定义 Mapping
# ===== 创建索引时指定 Mapping =====
PUT /employee
{
"settings": {
"number_of_shards": 3,
"number_of_replicas": 1
},
"mappings": {
"properties": {
"name": {
"type": "text",
"analyzer": "ik_max_word", # 中文分词
"fields": {
"keyword": { # 多字段:精确匹配用
"type": "keyword"
}
}
},
"age": {
"type": "integer"
},
"email": {
"type": "keyword" # 邮箱:精确匹配
},
"city": {
"type": "keyword" # 城市:精确匹配
},
"salary": {
"type": "float"
},
"birthday": {
"type": "date",
"format": "yyyy-MM-dd" # 日期格式
},
"is_married": {
"type": "boolean" # 布尔值
},
"description": {
"type": "text",
"analyzer": "ik_max_word" # 全文搜索
}
}
}
}
8.4 添加新字段
# ===== 为已有索引添加字段 =====
PUT /employee/_mapping
{
"properties": {
"phone": {
"type": "keyword"
},
"address": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
8.5 text vs keyword 的选择
场景 text keyword
────────────────────────── ────── ──────
全文搜索(模糊匹配) ✓
精确匹配(等于) ✓
排序 ✓
聚合(Group By) ✓
分词 ✓(不分词)
性能(精确查询) ✓(更好)
实战经验:
├─ 标题、描述、内容 → text(需要分词搜索)
├─ 邮箱、手机号、ID → keyword(精确匹配)
├─ 状态、分类、标签 → keyword(聚合统计)
├─ 需要排序的字段 → keyword
└─ 既需要搜索又需要精确匹配
→ "多字段"方案:text 用于搜索 + keyword 子字段用于精确匹配
示例:
"name": {
"type": "text",
"fields": {
"keyword": { "type": "keyword" }
}
}
# name → match 查询(分词搜索)
# name.keyword → term 查询(精确匹配)
总结
学习路线
第 1 步:掌握核心概念
Index / Type / Document / Field
Shard / Replica / Cluster / Node
第 2 步:学会文档 CRUD
PUT / POST / GET / DELETE / _update / _mget
第 3 步:掌握简单查询
_search 空查询 → URI 字符串查询 → 分页排序
第 4 步:精通 DSL 查询
match → term → range → bool(must/filter/should)
Query vs Filter 的选择
第 5 步:理解 Mapping
字段类型 → text vs keyword → 自定义 mapping
常用指令速查
# 索引操作
PUT /index_name 创建索引
DELETE /index_name 删除索引
GET /index_name/_mapping 查看映射
GET _cat/indices?v 查看所有索引
# 文档操作
PUT /index/_doc/id 创建/替换文档
POST /index/_doc/id/_update 更新文档
GET /index/_doc/id 查询文档
DELETE /index/_doc/id 删除文档
GET /index/_doc/id/_source 仅获取内容
# 查询
GET _search 查询所有
GET /index/_search 查询索引
GET /index/_search?q=... URI 搜索
# DSL 查询
{ "query": { "match_all": {} } } # 全部
{ "query": { "match": { "field": "value" } } } # 分词匹配
{ "query": { "term": { "field": "value" } } } # 精确匹配
{ "query": { "range": { "field": { "gte": 1 } } } } # 范围
{ "query": { "bool": { "must": [...], "filter": [...] } } } # 组合
一句话总结
Elasticsearch 是一个用 RESTful JSON 操作的分布式搜索引擎:Index=数据库、Document=行、Field=列、Mapping=表结构、DSL=SQL。简单查询用 URI,复杂查询用 DSL,精确条件放 filter,全文搜索放 query。掌握这几点就可以上手了。
更多推荐

所有评论(0)