Elasticsearch 同一索引不一樣類型下同名字段的映射衝突實例

    這個標題確定繞暈不少人吧。具體說明一下場景就明白了:Nginx 和 Apache 的訪問日誌,由於都屬於網站訪問,因此寫入到同一個索引的不一樣類型下,比方 logstash-accesslog-2015.04.03/nginx 和 logstash-accesslog-2015.04.03/apache。既然都是訪問日誌,確定不少字段的內容含義是雷同的,好比 clientip, domain, urlpath 等等。其中 nginx 有一個變量叫 $request_time,apache 有一個變量叫 %T,乍看上去也是同義的,我就統一命名爲 「requestTime」 了。這就是」同一索引(logstash-accesslog-YYYY.MM.DD)下不一樣類型(nginx,apache)的同名字段(requestTime)」。nginx

但事實上,這裏有個問題:nginx 中的以秒爲單位,是把毫秒算做小數;apache 中的以秒爲單位,是真的只記秒鐘整數位!shell

因此,這兩個類型生成的映射在這個字段上是不一致的。nginx 類型的 requestTime 是 double,apache 類型的 requestTime 是 longapache

不過日常看起來彷佛也沒什麼影響,寫入數據都照常,查看數據的時候默認顯示的 JSON 也各自無異。直到我準備用一把 scripted field 的時候,發現計算 doc['requestTime'].value * 1000 獲得的數都大的嚇人!app

由於相似計算以前在只有 nginx 日誌入庫的時候曾經正確運行過,因此只能是猜想 apache 日誌對此形成了影響,可是即便我把請求修改爲限定在 nginx 類型數據中進行,結果也沒發生變化。dom

仔細閱讀 scripting module 的文檔,其中提到了 doc['fieldname'].value 和_source.fieldname 兩種寫法的區別:前者會利用內存中的數據,然後者強制讀取磁盤上_source 存儲的 JSON 內容,從中釋放出相應字段內容。莫非是 requestTime 字段跟_source JSON 裏存的數據確實不同,而咱們日常搜索查看的都是從 JSON 裏釋放出來的,因此纔會如此?curl

爲了驗證個人猜想,作了一個請求測試:測試

# curl es.domain.com:9200/logstash-accesslog-2015.04.03/nginx/_search?q=_id:AUx-QvSBS-dhpiB8_1f1\&pretty -d '{     
        "fields": ["requestTime", "bodySent"],     
        "script_fields" : {         
            "test1" : {             
                "script" : "doc[\"requestTime\"].value"         
            },         
            "test3" : {             
                "script" : "_source.bodySent / _source.requestTime"         
            },         
            "test2" : {             
                "script" : "doc[\"requestTime\"].value * 1000"         
            }     
         } 
     }'

獲得的結果以下:網站

{   
    "took" : 43,   
    "timed_out" : false,   
    "_shards" : {     
    "total" : 56,     
    "successful" : 56,     
    "failed" : 0   
    },   
    "hits" : {     
        "total" : 1,     
        "max_score" : 1.0,     
        "hits" : [ {       
        "_index" : "logstash-accesslog-2015.04.03",       
        "_type" : "nginx",       
        "_id" : "AUx-QvSBS-dhpiB8_1f1",       
        "_score" : 1.0,       
        "fields" : {         
            "test1" : [ 4603039107142836552 ],         
            "test2" : [ -8646911284551352000 ],         
            "requestTime" : [ 0.54 ],         
            "test3" : [ 2444.4444444444443 ],         
            "bodySent" : [ 1320 ]       
            }     
        } ]   
    } 
}

果真!直接讀取的字段,以及採用 _source.fieldname 方式讀取的內容,都是正確的;而採用doc['fieldname'].value 獲取的內存數據,就不對。(0.54 存成 long 型會變成 4603039107142836552。這個 460 還正好能跟 540 湊成 1000,應該是某種特定存法,不過這裏我就沒深究了)url

再做下一步驗證。咱們知道,ES 數據的映射是根據第一條數據的類型肯定的,以後的數據如何類型跟已經成型的映射不統一,那麼寫入會失敗。如今這個 nginx 和 apache 兩個類型在 requestTime 字段上的映射是不同的,可是內存裏卻並無按照映射來處理。那麼,我往一個類型下寫入另外一個類型映射要求的數據,會報錯仍是會經過呢?spa

# curl -XPOST es.domain.com:9200/test/t1/1 -d '{"key":1}' 
{"_index":"test","_type":"t1","_id":"1","_version":1,"created":true} 
# curl -XPOST es.domain.com:9200/test/t2/1 -d '{"key":2.2}' 
{"_index":"test","_type":"t2","_id":"1","_version":1,"created":true} 
# curl -XPOST es.domain.com:9200/test/t1/2 -d '{"key":2.2}' 
{"_index":"test","_type":"t1","_id":"2","_version":1,"created":true} 
# curl -XPOST es.domain.com:9200/test/t2/2 -d '{"key":1}' 
{"_index":"test","_type":"t2","_id":"2","_version":1,"created":true} 
# curl -XPOST es.domain.com:9200/test/t1/3 -d '{"key":"1"}' 
{"_index":"test","_type":"t1","_id":"3","_version":1,"created":true} 
# curl -XPOST es.domain.com:9200/test/t2/3 -d '{"key":"1"}' 
{"_index":"test","_type":"t2","_id":"3","_version":1,"created":true} 
# curl -XPOST es.domain.com:9200/test/t2/4 -d '{"key":"abc"}' 
{"error":"RemoteTransportException[[10.10.10.10][inet[/10.10.10.10:9300]][indices:data/write/index]]; nested: MapperParsingException[failed to parse [key]]; nested: NumberFormatException[For input string: \"abc\"]; ","status":400} 
# curl -XGET es.domain.com:9200/test/_mapping 
{"test":{"mappings":{"t1":{"properties":{"key":{"type":"long"}}},"t2":{"properties":{"key":{"type":"double"}}}}}}

結果出來了,在映射相互衝突之後,實際數據只要是 numeric detect 能經過的,就都經過了!

BTW: kibana 4 中,已經會對這種狀況以黃色感嘆號圖標作出提示;而根據官方消息,ES 將來會在 2.0 版正式杜絕這種可能。

相關文章
相關標籤/搜索