33 · index / mapping 管理(建索引、别名、reindex)
阶段:第四阶段 / 写入与索引管理
ES:建索引 / mapping / 别名 / reindex | PostgreSQL:DDL、
CREATE TABLE、视图、表重建
1. 概念
索引管理相当于数据库的 DDL:
| ES 操作 | SQL 对照 |
|---|---|
PUT /index + mapping |
CREATE TABLE + 列定义 |
| 别名 alias | 视图 / 可切换的表名 |
reindex |
INSERT INTO new SELECT * FROM old(表重建) |
PUT /index/_mapping |
ALTER TABLE ADD COLUMN |
本项目建索引入口:InsertToEsTask.createIndex → CreateSalesIndexHandler,
mapping 从 mappings/*.json(如 salesdata_idx.json)读取。
2. PostgreSQL 对照
sql
CREATE TABLE salesdata (
invoice_number varchar,
net_amount numeric,
invoice_dt date
);
-- 加列(ES 只能加不能改类型)
ALTER TABLE salesdata ADD COLUMN customer_group varchar;
-- 视图切换(类比别名)
CREATE VIEW salesdata_current AS SELECT * FROM salesdata_v2;
3. ES DSL
# 建索引 + settings + mapping
PUT salesdata_idx
{
"settings": { "number_of_shards": 3, "number_of_replicas": 1 },
"mappings": {
"properties": {
"invoice_number": { "type": "keyword" },
"net_amount": { "type": "double" },
"invoice_dt": { "type": "date", "format": "yyyy-MM-dd" }
}
}
}
# 追加字段(不能改已有字段类型)
PUT salesdata_idx/_mapping
{ "properties": { "customer_group": { "type": "keyword" } } }
# 别名切换(原子)
POST _aliases
{
"actions": [
{ "remove": { "index": "salesdata_v1", "alias": "salesdata" } },
{ "add": { "index": "salesdata_v2", "alias": "salesdata" } }
]
}
# reindex:老索引数据搬到新索引
POST _reindex
{ "source": { "index": "salesdata_v1" }, "dest": { "index": "salesdata_v2" } }
4. Spring Boot 实现
java
@Component
public class Doc33IndexAdmin {
@Autowired
private ElasticsearchClient elasticsearchClient;
/** 建索引 + mapping(简化版,本项目实际读 mappings/*.json) */
public void createIndex(String indexName) throws IOException {
boolean exists = elasticsearchClient.indices()
.exists(e -> e.index(indexName)).value();
if (exists) {
return;
}
elasticsearchClient.indices().create(c -> c
.index(indexName)
.settings(st -> st.numberOfShards("3").numberOfReplicas("1"))
.mappings(m -> m
.properties("invoice_number", p -> p.keyword(k -> k))
.properties("net_amount", p -> p.double_(d -> d))
.properties("invoice_dt", p -> p.date(d -> d.format("yyyy-MM-dd")))));
}
/** 追加字段:类比 ALTER TABLE ADD COLUMN */
public void addKeywordField(String indexName, String field) throws IOException {
elasticsearchClient.indices().putMapping(m -> m
.index(indexName)
.properties(field, p -> p.keyword(k -> k)));
}
/** 原子切换别名 */
public void switchAlias(String alias, String oldIndex, String newIndex) throws IOException {
elasticsearchClient.indices().updateAliases(a -> a
.actions(ac -> ac.remove(r -> r.index(oldIndex).alias(alias)))
.actions(ac -> ac.add(ad -> ad.index(newIndex).alias(alias))));
}
}
与本项目对照:建索引逻辑见
CreateSalesIndexHandler,mapping 新增字段时参考
ADD_FIELD_SKILL里 ES 部分(在 mapping 的properties追加字段)。
5. 坑与最佳实践
- mapping 不可改已有字段类型:改类型只能新建索引 + reindex + 切别名。
- 用别名解耦 :应用只认别名(如
salesdata),底层索引版本切换对上层透明、零停机。 - 动态映射风险:生产索引建议显式 mapping,或关掉动态映射,避免字段类型被自动猜错。
- 分片数建后不可改 :
number_of_shards定死,需重新规划只能 reindex;副本数可动态调。 - reindex 期间双写或停写:保证新老索引数据一致,切别名前校验文档数。
阶段小结
第四阶段完成后,你应该能:单文档增删改、bulk 批量写、按条件批量更新/删除、
建索引与 mapping、用别名做零停机切换、用 reindex 重建索引。