譯[Elasticsearch] 數據建模 - 處理關聯關係(1)

數據建模(Modeling Your Data)數據庫

ES是一頭不一樣尋常的野獸,尤爲是當你來自SQL的世界時。它擁有不少優點:性能,可擴展性,準實時的搜索,以及對大數據的分析能力。而且,它很容易上手!只須要下載就可以開始使用它了。緩存

可是它也不是魔法。爲了更好的利用ES,你須要瞭解它從而讓它可以知足你的需求。服務器

在ES中,處理實體之間的關係並不像關係型存儲那樣明顯。在關係數據庫中的黃金準則 - 數據規範化,在ES中並不適用。在處理關聯關係,嵌套對象和父子關聯關係中,咱們會討論幾種可行方案的優勢和缺點。ide

緊接着在爲可擴展性而設計中,咱們會討論ES提供的一些用來快速靈活實現擴展的特性。對於擴展,並無一個能夠適用於全部場景的解決方案。你須要考慮數據是如何在你的系統中流轉的,從而恰當地對你的數據進行建模。針對基於時間的數據好比日誌事件或者社交數據流的方案比相對靜態的文檔集合的方案是十分不一樣的。post

最後,咱們會討論同樣在ES中不會擴展的東西。性能

處理關聯關係(Handling Relationships)大數據

在真實的世界中,關聯關係很重要:博客文章有評論,銀行帳戶有交易,客戶有銀行帳戶,訂單有行項目,目錄也擁有文件和子目錄。編碼

在關係數據庫中,處理關聯關係的方式讓你不會感到意外:.net

每一個實體(或者行,在關係世界中)能夠經過一個主鍵惟一標識。
實體是規範化了的。對於一個惟一的實體,它的數據僅被存儲一次,而與之關聯的實體則僅僅保存它的主鍵。改變一個實體的數據只能發生在一個地方。
在查詢期間,實體能夠被聯接(Join),它讓跨實體查詢成爲可能。
對於單個實體的修改是原子性,一致性,隔離性和持久性的。(參考ACID事務獲取更多相關信息。)
絕大多數的關係型數據庫都支持針對多個實體的ACID事務。
可是關係型數據庫也有它們的侷限,除了在全文搜索領域它們拙劣的表現外。在查詢期間聯接實體是昂貴的 - 聯接的實體越多,那麼查詢的代價就越大。對不一樣硬件上的實體執行聯接操做的代價太大以致於它甚至是不切實際的。這就爲在單個服務器上可以存儲的數據量設下了一個限制。設計

ES,像多數NoSQL數據庫那樣,將世界看做是平的。一個索引就是一系列獨立文檔的扁平集合。一個單一的文檔應該包括用來判斷它是否符合一個搜索請求的全部信息。

雖然在ES中改變一份文檔的數據是符合ACIDic的,涉及到多份文檔的事務就否則了。在ES中,當事務失敗後是沒有辦法將索引回滾到它以前的狀態的。

這個扁平化的世界有它的優點:

索引是迅速且不須要上鎖的。
搜索是迅速且不須要上鎖的。
大規模的數據能夠被分佈到多個節點上,由於每份文檔之間是獨立的。
可是關聯關係很重要。咱們須要以某種方式將扁平化的世界和真實的世界鏈接起來。在ES中,有4中經常使用的技術來管理關聯數據:

應用端聯接(Application-side joins)
數據非規範化(Data denormalization)
嵌套對象(Nested objects)
父子關聯關係(Parent/child relationships)
一般最終的解決方案會結合這些方案的幾種。

應用端聯接(Application-side Joins)

咱們能夠經過在應用中實現聯接來(部分)模擬一個關係型數據庫。好比,當咱們想要索引用戶和他們的博客文章時。在關係型的世界中,咱們能夠這樣作:

PUT /my_index/user/1  (1)
{
  "name":     "John Smith",
  "email":    "john@smith.com",
  "dob":      "1970/10/24"
}
PUT /my_index/blogpost/2   (2)
{
  "title":    "Relationships",
  "body":     "It's complicated...",
  "user":     1   (3)
}

(1)(2) 索引,類型以及每份文檔的ID一塊兒構成了主鍵。

(3) 博文經過保存了用戶的ID來聯接到用戶。因爲索引和類型是被硬編碼到了應用中的,因此這裏並不須要。

經過用戶ID等於1來找到對應的博文很容易:

GET /my_index/blogpost/_search
{
  "query": {
    "filtered": {
      "filter": {
        "term": { "user": 1 }
      }
    }
  }
}

爲了找到用戶John的博文,咱們能夠執行兩條查詢:第一條查詢用來獲得全部名爲John的用戶的IDs,第二條查詢經過這些IDs來獲得對應文章:

GET /my_index/user/_search
{
  "query": {
    "match": {
      "name": "John"
    }
  }
}
GET /my_index/blogpost/_search
{
  "query": {
    "filtered": {
      "filter": {
        "terms": { "user": [1] }   (1)
      }
    }
  }
}

(1) 傳入到terms過濾器的值是第一條查詢的結果。

應用端聯接最大的優點在於數據是規範化了的。改變用戶的名字只須要在一個地方操做:用戶對應的文檔。劣勢在於你須要在搜索期間運行額外的查詢來聯接文檔。

在這個例子中,只有一位用戶匹配了第一條查詢,可是在實際應用中可能輕易就獲得了數以百萬計的名爲John的用戶。將全部的IDs傳入到第二個查詢中會讓該查詢很是巨大,它須要執行百萬計的term查詢。

這種方法在第一個實體的文檔數量較小而且它們不多改變時合適(這個例子中實體指的是用戶)。這就使得經過緩存結果來避免頻繁查詢成爲可能。

反規範化你的數據(Denormalizing Your Data)

讓ES達到最好的搜索性能的方法是採用更直接的辦法,經過在索引期間反規範化你的數據。經過在每份文檔中包含冗餘數據來避免聯接。

若是咱們須要經過做者的名字來搜索博文,能夠在博文對應的文檔中直接包含該做者的名字:

PUT /my_index/user/1
{
  "name":     "John Smith",
  "email":    "john@smith.com",
  "dob":      "1970/10/24"
}
PUT /my_index/blogpost/2
{
  "title":    "Relationships",
  "body":     "It's complicated...",
  "user":     {
    "id":       1,
    "name":     "John Smith" 
  }
}

如今,咱們能夠經過一條查詢來獲得用戶名爲John的博文了:

GET /my_index/blogpost/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "title":     "relationships" }},
        { "match": { "user.name": "John"          }}
      ]
    }
  }
}

對數據的反規範化的優點在於速度。由於每份文檔包含了用於判斷是否匹配查詢的全部數據,不須要執行代價高昂的聯接操做。

http://blog.csdn.net/dm_vincent/article/details/47710367

相關文章
相關標籤/搜索