elasticsearch 时间问题

https://www.cnblogs.com/libin2015/p/9394995.html

Elasticsearch中定义时间的类型为Date

Elasticsearch原生支持date类型,json格式通过字符来表示date类型。所以在用json提交日期至elasticsearch的时候,es会隐式转换,

把es认为是date类型的字符串直接转为date类型。至于什么样的字符串es会认为可以转换成date类型,参考elasticsearch官网介绍:

https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-date-format.html

date type默认格式:

"strict_date_optional_time||epoch_millis"

 

date类型是包含时区信息的,如果我们没有在json代表日期的字符串中显式指定时区,对es来说没什么问题,

但是如果通过kibana显示es里的数据时,就会出现问题,数据的时间会晚8个小时。因为kibana从es里读取的date类型数据,没有时区信息,

kibana会默认当作0时区来解析,但是kibana在通过浏览器展示的时候,会通过js获取当前客户端机器所在的时区,也就是东八区,

所以kibana会把从es得到的日期数据减去8小时。这里就会导致kibana经常遇到的“数据时间延迟8小时”的问题。

所以最佳实践方案就是:我们在往es提交日期数据的时候,直接提交带有时区信息的日期字符串,如:“2016-07-15T12:58:17.136+0800”。

 

##索引中定义的日期格式与提交数据的日期格式要一致,否则会报错。

 

创建索引是指定date format示例:

复制代码
PUT my_index
{
  "mappings": {
    "_doc": {
      "properties": {
        "date": {
          "type":   "date",
          "format": "yyyy-MM-dd"
        }
      }
    }
  }
}
复制代码

date官网介绍:
https://www.elastic.co/guide/en/elasticsearch/reference/current/date.html

  1. GMT:格林威治标准时间 
  2. UTC:世界协调时间 
  3. DST:夏日节约时间 
  4. CST:中国标准时间 

其中GMT时间可以近似认为和UTC时间是相等的,但从精度上来说UTC时间更精确。其误差值必须保持在0.9秒以内 

CST= GMT + 8 =UTC + 8 

从上面可以看出来中国的时间是等于UTC时间+8小时,es默认存储时间的格式是UTC时间,如果我们查询es然后获取时间日期默认的数据,会发现跟当前的时间差8个小时,这其实是正常的,因为es默认存储是用的UTC时间,所以我们需要做的就是读取long型时间戳,然后重新格式化成下面的时间戳,即可获得正确的时间 :

yyyy-MM-dd HH:mm:ss 

像差8个时区的事情,最容易见到的就是,我们使用logstash收集的日志,发送到es里面,然后通过head查询就能发现不一致,但是如果我们用kibana查询,就不会发现时区问题,为什么? 因为kibana已经处理时区问题了,所以在kibana的页面显示的时间是正确的。 

此外在使用Java Client聚合查询日期的时候,需要注意时区问题,因为默认的es是按照UTC标准时区算的,所以不设置的聚合统计结果是不正确的。 

在es的DateHistogramBuilder里面有几个比较重要的参数:

  1.  
    field:指定按那个字段聚合
  2.  
    interval:聚合的时间单位(年,季度,月,周,天,小时,分钟,秒)
  3.  
    format:日期格式
  4.  
    time_zone:时区指定
  5.  
    offset:时间偏移量

注意,默认不设置时区参数,es是安装UTC的时间进行查询的,所以分组的结果可能与预期不一样,所以我们要指定时区为Asia/Shanghai代表北京的时区,这样才能获取正确的聚合结果 

1.1 Date类型数据的存储

UTC(Universal Time Coordinated) 叫做世界统一时间,中国大陆所用的时间是东8区时间,比UTC时间超前8小时。即与 UTC 的时差是 +8 ,也就是 UTC+8

在Elasticsearch内部,不论 date 是什么展示格式,所有date类型数据(时间字符串 or 时间戳等)在 Elasticsearch 内部存储时全部都会转换成 UTC时间戳(并且把时区也会计算进去),最后以milliseconds-since-the-epoch 作为存储的格式。

1.2 Date类型数据的查询

在Elasticsearch内部,date被转为UTC,并被存储为一个长整型数字,代表从1970年1月1号0点到现在的毫秒数。

date类型字段上的查询会在内部被转为对long型值的范围查询,查询的结果类型是字符串。

  • 假如插入的时候,值是"2018-01-01",则返回"2018-01-01"

  • 假如插入的时候,值是"2018-01-01 12:00:00",则返回"2018-01-01 12:00:00"

  • 假如插入的时候,值是1514736000000,则返回"1514736000000"。(进去是long型,出来是String型)

在查询日期时,会执行下面的过程:

  • 转换成 long 整形格式的范围(range) 查询
  • 得到聚合的结果
  • 将结果中的 date 类型(long 整型数据)根据 date format 字段转换回对应的展示格式

1.3 Date类型数据的展示

Elasticsearch 数据是以 json格式存储的,而 json中是并没有 date 数据类型,因此 Elasticsearch 中虽然有 date 类型,但在展示时却要转化成另外的格式。

date 类型在 Elasticsearch 展示的格式有下面几种:

  • 将日期时间格式化后的字符串,如 "2015-01-01" 或者 "2015/01/01 12:10:30"
  • long 型的整数,意义是 milliseconds-since-the-epoch,翻译一下就是自 1970-01-01 00:00:00 UTC 以来经过的毫秒数。
  • int 型的整数,意义是 seconds-since-the-epoch, 是指自 1970-01-01 00:00:00 UTC 以来经过的秒数。

当时间字符串中没有时区信息时,此时,在ES内部会将其(“2019-09-24 00:00:00“)当成是0时区“2019-09-24 00:00:00”
而在我们的Java程序中,Date.getTime()所获取的却是将其当做是东8区的时间“2019-09-24 00:00:00”(即返回的是北京时间1970年01月1日0点0分0秒以来的毫秒数,对应UTC时间1970年01月1日8点0分0秒以来的毫秒数,其数值大小等于0时区“2019-09-23 16:00:00”所对应的时间戳)所对应得时间戳。

posted @ 2020-09-09 16:16  醉逍遥_001  阅读(2847)  评论(0编辑  收藏  举报