這個標題確定繞暈不少人吧。具體說明一下場景就明白了: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 是 long。apache
不過日常看起來彷佛也沒什麼影響,寫入數據都照常,查看數據的時候默認顯示的 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 版正式杜絕這種可能。