4. Mapping与Setting调优:动态映射与显式映射、字段类型选择、索引刷新间隔设置
好,咱们进入第四章。这一章聊的是Mapping和Setting调优。说白了,就是告诉Elasticsearch:你的数据长什么样,该怎么存,多久刷一次盘。
很多新手上来就一把梭,把数据全扔进去,让ES自己猜字段类型。嗯,这在开发环境玩玩还行,上了生产,分分钟让你吃瘪。我个人习惯是:能用显式映射,就别用动态映射。为什么?往下看。
4.1 动态映射 vs 显式映射
动态映射,就是ES根据你第一条数据的格式,自动推断字段类型。比如你传了个"age": 25,它自动给你设成long类型。听起来挺智能对吧?
但我在项目中遇到过这么个坑:有个日志字段,大部分时候传的是数字,偶尔传了个字符串"unknown"。结果ES直接报错,整条数据写不进去。你想想看,就因为一条异常数据,导致整个索引写入失败,这谁受得了?
显式映射的好处很明显:
- 类型可控:字段是什么类型,你说了算
- 性能可预期:不会因为类型推断错误导致写入失败
- 存储可优化:可以针对性地设置分词器、索引选项
那什么时候用动态映射?我个人觉得,只有一种场景:数据量极小,且字段结构完全不可预知的探索性分析。比如你临时导入一批数据看看大概情况,用完就删的那种。
来看个显式映射的例子:
PUT /my_index
{
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "ik_smart"
},
"price": {
"type": "float"
},
"create_time": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss"
},
"tags": {
"type": "keyword"
}
}
}
}
看到没?每个字段我都明确指定了类型。这样ES就知道该怎么处理这些数据,不会自作主张。
4.2 字段类型选择:keyword vs text
这是面试高频题,也是实际开发中最容易踩坑的地方。我简单说下区别:
| 特性 | text | keyword |
|---|---|---|
| 分词 | 会分词,支持全文搜索 | 不分词,作为一个整体 |
| 聚合 | 不支持(需要fielddata) | 原生支持 |
| 排序 | 不支持 | 支持 |
| 精确匹配 | 不支持(term查询效率低) | 完美支持 |
| 典型场景 | 文章内容、商品描述 | 标签、ID、状态码、邮箱 |
说白了,需要全文搜索的用text,需要精确匹配、聚合、排序的用keyword。就这么简单。
但我见过太多人把ID字段设成text类型。你想想看,一个订单ID"ORD-2024-001",被分词器拆成"ORD"、"2024"、"001"三个词,然后你用term查询"ORD-2024-001"反而查不到。这不是给自己挖坑吗?
1. 这个字段需要模糊搜索吗? → 用text
2. 这个字段需要做group by吗? → 用keyword
3. 这个字段需要排序吗? → 用keyword
如果三个问题答案都是"否",那大概率用keyword就对了。
还有一种常见做法是multi-fields,即一个字段同时保留text和keyword两种类型:
PUT /my_index
{
"mappings": {
"properties": {
"product_name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
}
}
}
}
}
这样你就可以用product_name做全文搜索,用product_name.keyword做精确匹配和聚合。一举两得。不过要注意,这会增加存储开销,每个文档多存一份数据。
4.3 索引刷新间隔设置
刷新间隔(refresh_interval),是ES里一个容易被忽视但影响巨大的参数。它决定了数据写入后多久可以被搜索到。
默认值是1s,也就是每秒刷新一次。这意味着你写入一条数据,最多等1秒就能搜到。听起来很爽对吧?
但代价是什么?写入性能。每次刷新都会生成一个新的段(segment),频繁刷新会导致段数量暴增,合并压力变大,写入吞吐量下降。
我记得有一次帮一个客户做性能调优,他们的写入TPS只有2000,怎么都上不去。我一看,refresh_interval设的是默认1s。改成30s后,TPS直接飙到8000。你想想看,就改了一个参数,性能翻了4倍。
常见的设置策略:
- 日志场景:不需要实时搜索,可以设到30s-60s,甚至更大
- 电商场景:商品上架后需要尽快被搜到,设到5s-10s
- 监控告警:需要秒级响应,保持1s默认值
- 批量导入:可以先设为-1(禁用刷新),导入完成后再恢复
来看怎么动态调整:
// 设置刷新间隔为30秒
PUT /my_index/_settings
{
"index": {
"refresh_interval": "30s"
}
}
// 批量导入时禁用刷新
PUT /my_index/_settings
{
"index": {
"refresh_interval": "-1"
}
}
// 导入完成后恢复
PUT /my_index/_settings
{
"index": {
"refresh_interval": "1s"
}
}
还有一个细节:刷新和刷盘是两回事。刷新只是把数据从内存缓冲区移到文件系统缓存,让数据可搜索。但数据还没落盘。真正落盘是flush操作,由translog触发。别搞混了。
嗯,这一章的内容就这些。总结一下:
- 能用显式映射就别用动态映射,省得给自己找麻烦
- text和keyword的选择,核心看你要不要分词、要不要聚合
- 刷新间隔是个性能杠杆,调好了事半功倍
下一章咱们聊聊分片和副本的调优策略,那才是真正考验架构设计能力的地方。