目录
[1.1 ES时间类型唯一标准:date](#1.1 ES时间类型唯一标准:date)
[1.2 ES date 底层核心特性](#1.2 ES date 底层核心特性)
[1.3 ES官方两种标准时间格式](#1.3 ES官方两种标准时间格式)
[1.3.1 通用兼容格式(生产首选)](#1.3.1 通用兼容格式(生产首选))
[1.3.2 自定义本地时间格式](#1.3.2 自定义本地时间格式)
[2.1 方案一:自定义本地时间格式(适配MySQL原样同步)](#2.1 方案一:自定义本地时间格式(适配MySQL原样同步))
[2.2 方案二:UTC标准格式](#2.2 方案二:UTC标准格式)
[三、Java实体类适配+Java API Client查询实战](#三、Java实体类适配+Java API Client查询实战)
[3.1 适配自定义格式(yyyy-MM-dd HH:mm:ss)](#3.1 适配自定义格式(yyyy-MM-dd HH:mm:ss))
[3.1.1 实体类配置](#3.1.1 实体类配置)
[3.2 适配UTC标准格式(strict_date_optional_time)](#3.2 适配UTC标准格式(strict_date_optional_time))
[3.2.1 实体类配置](#3.2.1 实体类配置)
四、全网高频报错:LocalDateTime反序列化失败(根治方案)
[4.2 报错根因](#4.2 报错根因)
[4.3 分步解决方案](#4.3 分步解决方案)
前言
在实际开发中,Elasticsearch(下文简称ES)时间类型的使用是高频易错点,绝大多数开发者都会遇到时区错乱、序列化报错、MySQL与ES数据同步不一致、Java API查询反序列化失败等问题。
本文结合真实开发场景,从零梳理ES时间类型底层原理、Mapping配置规范、Java实体适配、MySQL数据同步方案、全网最常见报错及根治方案解决所有ES时间相关疑难问题。
一、核心基础:ES时间类型底层原理
1.1 ES时间类型唯一标准:date
ES 中所有时间字段,必须使用 date 类型,绝对禁止使用 keyword、text、long 存储业务时间:
-
禁止字符串存储:无法范围查询、时间聚合、排序失效
-
禁止纯long时间戳存储:可读性极差,排查问题困难,可视化查询不友好
1.2 ES date 底层核心特性
无论你传入什么格式的时间,ES底层统一存储【UTC毫秒时间戳】,所有格式化展示、时区适配都是上层封装,底层无字符串概念。
这是ES和MySQL时间设计的本质区别,也是所有时区BUG的根源:
-
MySQL datetime:纯本地时间字符串,无时区属性,绑定服务器时区
-
ES date:绝对时间戳,默认强制识别为UTC 0时区
1.3 ES官方两种标准时间格式
1.3.1 通用兼容格式(生产首选)
适配时间字符串+毫秒时间戳,兼容性最强,官方默认配置:
"format": "strict_date_optional_time||epoch_millis"
支持入库格式:
-
标准UTC时间:2025-05-08T10:20:30.123Z
-
秒级UTC时间:2025-05-08T10:20:30Z
-
毫秒时间戳:1746728430123
1.3.2 自定义本地时间格式
适配MySQL默认时间格式,无时区,仅限全环境东八区场景:
"format": "yyyy-MM-dd HH:mm:ss"
支持入库格式:2025-05-08 10:20:30
自定义的时间格式可以随意自定义,只是搭建可以看一下底层原理,就理解了为什么不太推荐使用。
二、ES时间字段Mapping实战配置(两套可用方案)
本文提供实战偷懒方案(中小企业) 和标准化规范方案(大厂生产),按需选用。
2.1 方案一:自定义本地时间格式(适配MySQL原样同步)
适用场景:国内单体项目、全服务器东八区、无海外业务、无需跨时区统计
优点:MySQL数据无需转换,同步代码极简,开发效率高
缺点:不支持跨时区,扩展性差
PUT /order_index_001
{
"settings": {
"number_of_replicas": 0
},
"mappings": {
"properties": {
"order_id": { "type": "keyword" },
"price": { "type": "scaled_float", "scaling_factor": 100 },
"pay_num": { "type": "integer" },
"order_time": { "type": "date", "format": "yyyy-MM-dd HH:mm:ss" },
"category": { "type": "keyword" }
}
}
}
2.2 方案二:UTC标准格式
适用场景:微服务、分布式项目、上云项目、有海外业务、大数据统计
优点:无时区BUG、全球统一、支持所有时间查询/聚合、扩展性拉满
PUT /order_index_001
{
"settings": {
"number_of_replicas": 0
},
"mappings": {
"properties": {
"order_id": { "type": "keyword" },
"price": { "type": "scaled_float", "scaling_factor": 100 },
"pay_num": { "type": "integer" },
"order_time": { "type": "date", "format": "strict_date_optional_time||epoch_millis" },
"category": { "type": "keyword" }
}
}
}
三、Java实体类适配+Java API Client查询实战
下面的例子中展示了使用不同的时间格式,Java实体类的类型。
3.1 适配自定义格式(yyyy-MM-dd HH:mm:ss)
3.1.1 实体类配置
对应Mapping无时区格式,使用 LocalDateTime,必须加格式化注解
java
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class OrderIndex {
private String order_id;
private Double price;
private Integer pay_num;
private String category;
// 格式必须与ES Mapping完全一致
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime order_time;
}
3.2 适配UTC标准格式(strict_date_optional_time)
3.2.1 实体类配置
对应ES UTC时间,使用 Instant(绝对UTC时间,无时区BUG)
java
import lombok.Data;
import java.time.Instant;
@Data
public class OrderIndex {
private String order_id;
private Double price;
private Integer pay_num;
private String category;
// 标准UTC时间,无需格式化注解
private Instant order_time;
}
四、全网高频报错:LocalDateTime反序列化失败(根治方案)
co.elastic.clients.json.JsonpMappingException: Error deserializing co.elastic.clients.elasticsearch.core.search.Hit: jakarta.json.JsonException: Jackson exception Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type `java.time.LocalDateTime` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling
4.2 报错根因
很多同学只加依赖仍报错,核心原因:
-
Spring默认ObjectMapper支持Java8时间类型,但ES Java Client拥有独立的ObjectMapper
-
ES客户端默认未注册Java8时间模块,无法解析LocalDateTime/LocalDate
4.3 分步解决方案
第一步:引入依赖
XML
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
第二步:手动配置ES客户端(核心根治)
手动为ES独立的ObjectMapper注册时间模块,彻底解决解析失败问题
如果大家使用的是安全模式下的es连接,可以查看博主的文章:
Java 连接 Elasticsearch 8.x 安全模式实战:证书校验与 ApiKey 认证全解析_elasticsearch 不加spring 连接es服务 ssl校验-CSDN博客
上述文中中的连接时没有配置时间的,大家在结合这个文章中的配置,进行一下时间模块的注册就可以了。
java
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.elasticsearch.client.RestClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class EsClientConfig {
@Bean
public ElasticsearchClient elasticsearchClient(RestClient restClient) {
// 注册Java8时间模块,支持LocalDateTime/Instant解析
ObjectMapper objectMapper = new ObjectMapper()
.registerModule(new JavaTimeModule());
JacksonJsonpMapper mapper = new JacksonJsonpMapper(objectMapper);
ElasticsearchTransport transport = new RestClientTransport(restClient, mapper);
return new ElasticsearchClient(transport);
}
}