Elasticsearch时间类型实战
目录前言一、核心基础ES时间类型底层原理1.1 ES时间类型唯一标准date1.2 ES date 底层核心特性1.3 ES官方两种标准时间格式1.3.1 通用兼容格式生产首选1.3.2 自定义本地时间格式二、ES时间字段Mapping实战配置两套可用方案2.1 方案一自定义本地时间格式适配MySQL原样同步2.2 方案二UTC标准格式三、Java实体类适配Java API Client查询实战3.1 适配自定义格式yyyy-MM-dd HH:mm:ss3.1.1 实体类配置3.2 适配UTC标准格式strict_date_optional_time3.2.1 实体类配置四、全网高频报错LocalDateTime反序列化失败根治方案4.2 报错根因4.3 分步解决方案第一步引入依赖第二步手动配置ES客户端核心根治前言在实际开发中Elasticsearch下文简称ES时间类型的使用是高频易错点绝大多数开发者都会遇到时区错乱、序列化报错、MySQL与ES数据同步不一致、Java API查询反序列化失败等问题。本文结合真实开发场景从零梳理ES时间类型底层原理、Mapping配置规范、Java实体适配、MySQL数据同步方案、全网最常见报错及根治方案解决所有ES时间相关疑难问题。一、核心基础ES时间类型底层原理1.1 ES时间类型唯一标准dateES 中所有时间字段必须使用 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毫秒时间戳17467284301231.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:ss3.1.1 实体类配置对应Mapping无时区格式使用LocalDateTime必须加格式化注解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_time3.2.1 实体类配置对应ES UTC时间使用Instant绝对UTC时间无时区BUGimport 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 handling4.2 报错根因很多同学只加依赖仍报错核心原因Spring默认ObjectMapper支持Java8时间类型但ES Java Client拥有独立的ObjectMapperES客户端默认未注册Java8时间模块无法解析LocalDateTime/LocalDate4.3 分步解决方案第一步引入依赖dependency groupIdcom.fasterxml.jackson.datatype/groupId artifactIdjackson-datatype-jsr310/artifactId /dependency第二步手动配置ES客户端核心根治手动为ES独立的ObjectMapper注册时间模块彻底解决解析失败问题如果大家使用的是安全模式下的es连接可以查看博主的文章Java 连接 Elasticsearch 8.x 安全模式实战证书校验与 ApiKey 认证全解析_elasticsearch 不加spring 连接es服务 ssl校验-CSDN博客上述文中中的连接时没有配置时间的大家在结合这个文章中的配置进行一下时间模块的注册就可以了。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); } }