1. 程式人生 > 其它 >Elasticsearch 滯後8個小時等時區問題

Elasticsearch 滯後8個小時等時區問題

1、實戰問題

如下都是實戰環節遇到的問題:

  • logstash誰解決過時區問題,mysql是東八區shanghai 但是這玩意讀完存到es就少了8小時?

  • 目前索引會比真正時間晚8小時,導致8點前的日誌寫到昨天索引裡,大佬們有招嗎?

  • 問一下 logstash輸出日誌到本地檔案中,按照小時生成索引,但是他這邊的時區是utc,生成的時間和北京時間少8小時,這一塊大佬們是咋操作的?

  • ......從瀏覽器kibana那裡看timestamp時間戳變成了utc的時區?

上面的問題都涉及到時區問題,涉及到資料的同步(logstash)、寫入、檢索(elasticsearch)、視覺化(kibana)的幾個環節。

2、時區問題拆解

我們通過如下幾個問題展開拆解。

2.1 Elasticserch 預設時區是?能改嗎?

官方文件強調:在 Elasticsearch 內部,日期被轉換為 UTC時區並存儲為一個表示自1970-01-01 00:00:00 以來經過的毫秒數的值。

Internally, dates are converted to UTC (if the time-zone is specified) and stored as a long number representing milliseconds-since-the-epoch.

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

Elasticsearch date 型別預設時區:UTC。

正如官方工程師強調(如下截圖所示):Elasticsearch 預設時區不可以修改。

https://discuss.elastic.co/t/index-creates-in-different-timezone-other-than-utc/148941但,我們可以“曲線救國”,通過:

  • ingest pipeline 預處理方式寫入的時候修改時區;
  • logstash filter 環節做時區轉換;
  • 查詢時指定時區;
  • 聚合時指定時區。

2.2 Kibana 預設時區是?能改嗎?

kibana 預設時區是瀏覽器時區。可以修改,修改方式如下:Stack Management -> Advanced Settings ->Timezone for data formatting.

2.3 Logstash 預設時區是?能改嗎?

預設:UTC。可以通過中間:filter 環節進行日期資料處理,包括:轉時區操作。小結一下:

  • logstash 預設 UTC 時區。
  • Elasticsearch 預設 UTC 時區。
  • Kibana 預設瀏覽器時區,基本我們用就是:東八區。
  • 如果基於Mysql 同步資料,Mysql 資料是:東八區。

我們看一下東8區百度百科定義:東八區(UTC/GMT+08:00)是比世界協調時間(UTC)/格林尼治時間(GMT)快8小時的時區,理論上的位置是位於東經112.5度至127.5度之間,是東盟標準的其中一個候選時區。當格林尼治標準時間為0:00時,東八區的標準時間為08:00。通過上面的定義,能加深對 logstash 同步資料後,資料滯後8小時的理解。

3、時區問題解決方案

基於上面的分析,如何解決時區問題呢?由於 kibana 支援手動修改時區,不在下文討論 的範圍之內。實戰專案中,自己根據業務需求修改即可。那麼問題就轉嫁為:寫入的時候轉換成給定時區(如:東8區)就可以了。

3.1 方案一:ingest 預處理為東8區時區

  • 步驟 1:定義預處理管道:chage_utc_to_asiash(名稱自己定義即可)。

在該管道中實現了時區轉換。

  • 步驟 2:建立索引同時指定預設管道:chage_utc_to_asiash。
  • 步驟 3:寫入資料(單條或 bulk 批量均可)
PUT _ingest/pipeline/chage_utc_to_asiash
{
  "processors": [
    {
      "date" : {
        "field" : "my_time",
        "target_field": "my_time", 
        "formats" : ["yyyy-MM-dd HH:mm:ss"],
        "timezone" : "Asia/Shanghai"
      }
    }
  ]
}

PUT my-index-000001
{
  "settings": {
    "default_pipeline": "chage_utc_to_asiash"
  },
  "mappings": {
    "properties": {
      "my_time": {
        "type": "date"
      }
    }
  }
}

PUT my-index-000001/_doc/1
{
  "my_time": "2021-08-09 08:07:16"
} 

當寫入資料後,執行檢索時,kibana dev tool 返回結果如下:

 "hits" : [
      {
        "_index" : "my-index-000001",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "my_time" : "2021-08-09T08:07:16.000+08:00"
        }
      }
    ]

最明顯的特徵是:多了+08:00時區(東8區)標誌。然後,我們用:kibana discover視覺化展示一下:

上圖中,kibana 採用預設瀏覽器時區。如果不做上面的 ingest 預處理實現,會怎麼樣呢?大家如果實現過,肯定會感觸很深。需要我們在kibana中切換時間範圍,才能找到之前寫入的資料。ingest 預處理時區的好處:方便、靈活的實現了寫入資料的時區轉換。

3.2 方案二:logstash 中間 filter 環節處理

拿真實同步案例講解一下時區處理:

  • 資料來源端:Mysql;
  • 資料目的端:Elasticsearch;
  • 同步方式:logstash,本質藉助:logstash_input_jdbc 外掛同步;
  • 時區處理:logstash filter 環節 ruby 指令碼處理。

如下只給出了中間 filter 環節的指令碼:

filter {
 ruby { 
   code => "event.set('timestamp', event.get('publish_time').time.localtime + 8*60*60)" 
 }
 ruby {
   code => "event.set('publish_time',event.get('timestamp'))"
 }
 mutate {
   remove_field => ["timestamp"]
 }
}

三行指令碼含義,解釋如下:

  • 第一行:將 publish_time 時間加8小時處理,賦值給 timestamp。

publish_time 到了 logstash 已轉成了 UTC 時區了。timestamp 類似似 C 語言中的交換兩個數函式中的 temp 臨時變數。

  • 第二行:將 timestamp 時間賦值給 publish_time。
  • 第三行:刪除中轉欄位:timestamp。

源資料Mysql 效果:

同步後 效果:

如上兩個截圖,對比一下區別:

  • publish_time 做了時區處理,兩者時間已一致,都是東 8 區。
  • update_time 未做時間處理,寫入Elasticsearch 後由東8區時間 10:57:31 轉為UTC時區時間 02:57:31,少了8小時。

4、檢索和聚合的時候指定時區

假定我們寫入ES前未做時區處理(實戰環節常有的場景),但是檢索或者聚合的時候想做時區處理可以嗎?

可以的,具體實現方式如下:

POST testindex/_search?pretty
{
  "query": {
    "range": {
      "date": {
        "gte": "2020-01-01 00:00:00",
        "lte": "2020-01-03 23:59:59",
        "format": "yyyy-MM-dd HH:mm:ss",
        "time_zone": "+08:00"
      }
    }
},
  "size": 0,
  "aggs": {
    "per_day": {
      "date_histogram": {
        "calendar_interval": "day",
        "field": "date",
        "time_zone": "+08:00"
      }
    }
  }
}

如上示例中,整合了檢索和聚合,有兩個要點:

  • 要點1:range query 中指定時區檢索。
  • 要點2:data_histogram 聚合中指定時區聚合。

5、小結

資料寫入時間不一致、資料滯後8小時等時區問題的本質是:各個處理端時區不一致,寫入源的時區、Kibana預設是本地時區(如中國為:東8區時區),而 logstash、Elasticsearch 是UTC時區。

本文給出了兩種寫入前預處理的解決方案,方案一:基於管道預處理;方案二:基於logstash filter 中間環節過濾。兩種方案各有利弊,預處理管道相對更輕量級,實戰選型建議根據業務需求。本文最後指出在檢索和聚合環節使用時區處理方式。

參考

https://t.zsxq.com/2nYnq76

https://mp.weixin.qq.com/s/gBY7uNSjy-4fux0cf3rBDg