Elasticsearch系列(四)ES數據基本知識

本文轉載自:ES數據操作

本章就不詳細示例ES數據的基本操作,只記錄一些知識點,便於讀者閱讀後面的章節有幫助;

一、文檔及文檔元數據

對象(object)是一種語言相關,記錄在內存中的的數據結構。爲了在網絡間發送,或者存儲它,我們需要一些標準的格式來表示它。JSON (JavaScript Object Notation)是一種可讀的以文本來表示對象的方式。它已經成爲NoSQL世界中數據交換的一種事實標準。當對象被序列化爲JSON,它就成爲JSON文檔(JSON document)了。

Elasticsearch是一個分佈式的文檔(document)存儲引擎。它可以實時存儲並檢索複雜數據結構——序列化的JSON文檔。換言說,一旦文檔被存儲在Elasticsearch中,它就可以在集羣的任一節點上被檢索。

當然,我們不僅需要存儲數據,還要快速的批量查詢。雖然已經有很多NoSQL的解決方案允許我們以文檔的形式存儲對象,但它們依舊需要考慮如何查詢這些數據,以及哪些字段需要被索引以便檢索時更加快速。

在Elasticsearch中,每一個字段的數據都是默認被索引的。也就是說,每個字段專門有一個反向索引用於快速檢索。而且,與其它數據庫不同,它可以在同一個查詢中利用所有的這些反向索引,以驚人的速度返回結果。

在Elasticsearch中,文檔(document)這個術語有着特殊含義。它特指最頂層結構或者根對象(root object)序列化成的JSON數據(以唯一ID標識並存儲於Elasticsearch中)。

一個文檔不只有數據。它還包含了元數據(metadata)——關於文檔的信息。三個必須的元數據節點是:

節點 說明
_index 文檔存儲的地方
_type 文檔代表的對象的類
_id 文檔的唯一標識

_index
索引(index)類似於關係型數據庫裏的“數據庫”——它是我們存儲和索引關聯數據的地方。

_type

在關係型數據庫中,我們經常將相同類的對象存儲在一個表裏,因爲它們有着相同的結構。同理,在Elasticsearch中,我們使用相同類型(type)的文檔表示相同的“事物”,因爲他們的數據結構也是相同的。

_id

id僅僅是一個字符串,它與_index和_type組合時,就可以在Elasticsearch中唯一標識一個文檔。當創建一個文檔,你可以自定義_id,也可以讓Elasticsearch幫你自動生成。


注:ES中的數據的基本增刪改查,我就不在這貼了;

二、版本控制

當使用index API更新文檔的時候,我們讀取原始文檔,做修改,然後將整個文檔(whole document)一次性重新索引。最近的索引請求會生效——Elasticsearch中只存儲最後被索引的任何文檔。如果其他人同時也修改了這個文檔,他們的修改將會丟失。

很多時候,這並不是一個問題。或許我們主要的數據存儲在關係型數據庫中,然後拷貝數據到Elasticsearch中只是爲了可以用於搜索。或許兩個人同時修改文檔的機會很少。亦或者偶爾的修改丟失對於我們的工作來說並無大礙。

在數據庫中,有兩種通用的方法確保在併發更新時修改不丟失:

悲觀併發控制

這在關係型數據庫中被廣泛的使用,假設衝突的更改經常發生,爲了解決衝突我們把訪問區塊化。典型的例子是在讀一行數據前鎖定這行,然後確保只有加鎖的那個線程可以修改這行數據。

樂觀併發控制

被Elasticsearch使用,假設衝突不經常發生,也不區塊化訪問,然而,如果在讀寫過程中數據發生了變化,更新操作將失敗。這時候由程序決定在失敗後如何解決衝突。實際情況中,可以重新嘗試更新,刷新數據(重新讀取)或者直接反饋給用戶。

Elasticsearch樂觀併發控制:

Elasticsearch是分佈式的。當文檔被創建、更新或刪除,文檔的新版本會被複制到集羣的其它節點。Elasticsearch即是同步的又是異步的,意思是這些複製請求都是平行發送的,並無序(out of sequence)的到達目的地。這就需要一種方法確保老版本的文檔永遠不會覆蓋新的版本。

上文我們提到index、get、delete請求時,我們指出每個文檔都有一個_version號碼,這個號碼在文檔被改變時加一。Elasticsearch使用這個_version保證所有修改都被正確排序。當一箇舊版本出現在新版本之後,它會被簡單的忽略。

我們利用_version的這一優點確保數據不會因爲修改衝突而丟失。我們可以指定文檔的version來做想要的更改。如果那個版本號不是現在的,我們的請求就失敗了。

所有更新和刪除文檔的請求都接受version參數,它可以允許在你的代碼中增加樂觀鎖控制。

使用外部版本控制系統

一種常見的結構是使用一些其他的數據庫做爲主數據庫,然後使用Elasticsearch搜索數據,這意味着所有主數據庫發生變化,就要將其拷貝到Elasticsearch中。如果有多個進程負責這些數據的同步,就會遇到上面提到的併發問題。

如果主數據庫有版本字段——或一些類似於timestamp等可以用於版本控制的字段——是你就可以在Elasticsearch的查詢字符串後面添加version_type=external來使用這些版本號。版本號必須是整數,大於零小於9.2e+18——Java中的正的long。

外部版本號與之前說的內部版本號在處理的時候有些不同。它不再檢查_version是否與請求中指定的一致,而是檢查是否小於指定的版本。如果請求成功,外部版本號就會被存儲到_version中。

外部版本號不僅在索引和刪除請求中指定,也可以在創建(create)新文檔中指定。

三、文檔局部更新

我們說了一種通過檢索,修改,然後重建整文檔的索引方法來更新文檔。這是對的。然而,使用update API,我們可以使用一個請求來實現局部更新,例如增加數量的操作。

我們也說過文檔是不可變的——它們不能被更改,只能被替換。update API必須遵循相同的規則。表面看來,我們似乎是局部更新了文檔的位置,內部卻是像我們之前說的一樣簡單的使用update API處理相同的檢索-修改-重建索引流程,我們也減少了其他進程可能導致衝突的修改。

最簡單的update請求表單接受一個局部文檔參數doc,它會合併到現有文檔中——對象合併在一起,存在的標量字段被覆蓋,新字段被添加


更新可能不存在的文檔

想象我們要在Elasticsearch中存儲瀏覽量計數器。每當有用戶訪問頁面,我們增加這個頁面的瀏覽量。但如果這是個新頁面,我們並不確定這個計數器存在與否。當我們試圖更新一個不存在的文檔,更新將失敗。
在這種情況下,我們可以使用upsert參數定義文檔來使其不存在時被創建。

更新和衝突

這這一節的介紹中,我們介紹瞭如何在檢索(retrieve)和重建索引(reindex)中保持更小的窗口,如何減少衝突性變更發生的概率,不過這些無法被完全避免,像一個其他進程在update進行重建索引時修改了文檔這種情況依舊可能發生。

爲了避免丟失數據,update API在檢索(retrieve)階段檢索文檔的當前_version,然後在重建索引(reindex)階段通過index請求提交。如果其他進程在檢索(retrieve)和重建索引(reindex)階段修改了文檔,_version將不能被匹配,然後更新失敗。

對於多用戶的局部更新,文檔被修改了並不要緊。例如,兩個進程都要增加頁面瀏覽量,增加的順序我們並不關心——如果衝突發生,我們唯一要做的僅僅是重新嘗試更新既可。

這些可以通過retry_on_conflict參數設置重試次數來自動完成,這樣update操作將會在發生錯誤前重試——這個值默認爲0。

 POST /website/pageviews/1/_update?retry_on_conflict=5
{
   "script" : "ctx._source.views+=1",
   "upsert": {
       "views": 0
   }
}

這適用於像增加計數這種順序無關的操作,但是還有一種順序非常重要的情況。例如index API,使用“保留最後更新(last-write-wins)”的update API,但它依舊接受一個version參數以允許你使用樂觀併發控制(optimistic concurrency control)來指定你要更細文檔的版本。

四、批量操作

1、批量搜索

像Elasticsearch一樣,檢索多個文檔依舊非常快。合併多個請求可以避免每個請求單獨的網絡開銷。如果你需要從Elasticsearch中檢索多個文檔,相對於一個一個的檢索,更快的方式是在一個請求中使用multi-get或者mget API。

如果所有文檔具有相同_index和_type,你可以通過簡單的ids數組來代替完整的docs數組:

POST /website/blog/_mget
{
   "ids" : [ "2", "1" ]
}

如果想知道每個文檔是否都成功了,你需要檢查found標誌。

2、批量寫

bulk API允許我們使用單一請求來實現多個文檔的create、index、update或delete。這對索引類似於日誌活動這樣的數據流非常有用,它們可以以成百上千的數據爲一個批次按序進行索引。

這些說明bulk請求不是原子操作——它們不能實現事務。每個請求操作時分開的,所以每個請求的成功與否不干擾其它操作。


3、批量大小參考

整個批量請求需要被加載到接受我們請求節點的內存裏,所以請求越大,給其它請求可用的內存就越小。有一個最佳的bulk請求大小。超過這個大小,性能不再提升而且可能降低。

最佳大小,當然並不是一個固定的數字。它完全取決於你的硬件、你文檔的大小和複雜度以及索引和搜索的負載。幸運的是,這個最佳點(sweetspot)還是容易找到的:

試着批量索引標準的文檔,隨着大小的增長,當性能開始降低,說明你每個批次的大小太大了。開始的數量可以在1000~5000個文檔之間,如果你的文檔非常大,可以使用較小的批次。

通常着眼於你請求批次的物理大小是非常有用的。一千個1kB的文檔和一千個1MB的文檔大不相同。一個好的批次最好保持在5-15MB大小間。

本文轉載自:ES數據操作

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章