当ElasticSearch时间字段设置多个格式到底是用的哪个?
# ElasticSearch时间类型的坑!究竟如何选择使用的格式?
在使用 ElasticSearch 作为数据存储时,日期字段常常是我们进行查询和分析的关键数据之一。尤其是在处理时间戳时,ElasticSearch 提供了灵活的时间格式支持,可以处理多种不同的时间格式。本文将探讨当设置多个时间格式时,ElasticSearch 是如何决定使用哪个格式的,并分析可能出现的潜在问题。
# ElasticSearch 时间字段的定义
在 ElasticSearch 中,时间字段通常会定义为 date
类型。这种类型允许我们指定时间格式,帮助 ElasticSearch 解析并存储时间数据。通过 mappings
来定义时间字段时,可以指定一个或多个时间格式。
# 支持的常用时间格式
ElasticSearch 支持多种时间格式,使得我们可以方便地处理各种不同来源的数据。常见的几种时间格式包括:
# 1. yyyy-MM-dd HH:mm:ss
这种格式表示一个具体的时间点,包括年月日、小时、分钟、秒。例如,2022-03-15 12:30:45
。
# 2. yyyy-MM-dd
这种格式只包含年月日,不包括具体的时间部分。例如,2022-03-15
。
# 3. epoch_millis
epoch_millis
表示毫秒级的时间戳,指从 1970-01-01 00:00:00 UTC
到当前时间的毫秒数。例如,1672531200000
表示 2023-01-01 00:00:00 UTC
。
# 4. epoch_second
epoch_second
表示秒级的时间戳,指从 1970-01-01 00:00:00 UTC
到当前时间的秒数。例如,1672531200
表示 2023-01-01 00:00:00 UTC
。
# 如何设置多个格式
在 ElasticSearch 中,可以通过在 mappings
中为日期字段指定多个格式,来支持不同的时间表示方式。通过使用 ||
运算符,可以将多个格式结合起来,ElasticSearch 会尝试按顺序匹配每个格式。
以下是一个完整的例子,展示了如何为日期字段设置多个格式:
在这个配置中这个字段支持了四种时间格式:
yyyy-MM-dd HH:mm:ss
yyyy-MM-dd
epoch_millis
(毫秒级时间戳)epoch_second
(秒级时间戳)
# ElasticSearch 如何选择时间格式
那么如果当我们写入一个时间戳,到底使用的是epoch_millis
还是epoch_second
呢?
在这里很多朋友可能说,10 位的时间戳是epoch_second
秒级时间戳,13 位的是epoch_millis
毫秒级时间戳。
那么问题来了,当我们写入 1 时,它算哪个类型呢?
先说结论:实际它不是按照位数判断的,当向 ElasticSearch 中写入时间数据时,它会按照配置的格式顺序进行匹配,找到第一个符合格式的规则进行解析。
# 我们来实际验证一下
写入一条数据
POST /example_index/_doc
{
"first_login_time": 1672531200
}
2
3
4
查看数据(这里我们通过 script 方式自动转换时间)
GET /example_index/_search
{
"_source": {
"includes": ["first_login_time"]
},
"script_fields": {
"formatted_time": {
"script": {
"source": """
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneOffset.UTC);
return formatter.format(Instant.ofEpochMilli(doc['first_login_time'].value.millis));
"""
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
结果我们发现,它是按照毫秒单位写入的,这也就验证了刚刚的结论,按照我们设置的 format 顺序进行的匹配。
{
"_index" : "example_index",
"_type" : "_doc",
"_id" : "9r2WbZQBpX9FjWS70nx1",
"_score" : 1.0,
"_source" : {
"first_login_time" : 1672531200
},
"fields" : {
"formatted_time" : [
"1970-01-20 08:35:31"
]
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
按照这个逻辑,发现epoch_second
类型永远无法触发,想了下如果反过来配置呢?
按照这个格式创建字段yyyy-MM-dd HH:mm:ss || yyyy-MM-dd || epoch_second || epoch_millis
,写入毫秒的时间戳。
发现结果为+33658-09-27 01:46:40
,也同样无法按照毫秒级写入。
# 潜在问题与结论
经过实际验证,我们发现,ElasticSearch 在同时设置 epoch_second
和 epoch_millis
时,始终会选择第一个匹配的格式。因此,如果你同时设置这两个格式,后面的配置(如 epoch_millis
)将不会生效。这个行为可能是 ElasticSearch 的一个潜在 Bug,用户在配置多个时间格式时应避免同时使用 epoch_second
和 epoch_millis
。
# 解决方案:
- 如果需要使用秒级时间戳,请仅配置
epoch_second
。 - 如果需要使用毫秒级时间戳,请仅配置
epoch_millis
。 - 避免同时配置这两个选项。