- 真實世界中很多重要的關聯關係
- 部落格、作者、評論
- 銀行賬戶有多次交易記錄
- 客戶有很多銀行賬戶
- 目錄檔案有很多檔案和子目錄
- 正規化化設計(Normalization)的主要目標是“減少不必要的更新”
- 副作用:一個完全正規化化設計的資料庫經常面臨“查詢緩慢”的問題
- 正規化化節省了儲存空間,但是儲存空間越來越便宜
- 正規化化簡化了更新,但是資料“讀”取操作可能越多
-
- 反正規化化設計
- 資料“Flattening”,不使用關聯關係,而是在文件中儲存冗餘的資料拷貝
- 優點:無需處理Joins操作,資料讀取效能好
- Elasticsearch 通過壓縮_source欄位,減少磁碟空間的開銷
- 缺點:不適合在資料頻繁修改的場景
- 一條資料(使用者名稱)的改動,可能會引起很多資料的更新
- 關係型資料庫,一般會考慮 Normalize 資料;在Elasticsearch,往往考慮 Denormalize 資料
- Denormalize 的好處:讀的速度變快、無需表連線、無需行鎖
- Elasticsearch 並不擅長處理關聯關係,我們一般採用以下四種方法處理關聯
- 物件型別
- 巢狀物件(Nested Object)
- 父子關聯關係(Parent 、Child)
- 應用端關聯
案例1:部落格和其作者資訊
- 物件型別
- 在每個部落格的問下中都保留作者的資訊
- 如果作者資訊發生變化,需要修改相關的部落格文件
PUT /blog
{
"mappings": {
"properties": {
"content": {
"type": "text"
},
"time": {
"type": "date"
},
"user": {
"properties": {
"city": {
"type": "text"
},
"userid": {
"type": "long"
},
"username": {
"type": "keyword"
}
}
}
}
}
}
# 插入一條 Blog 資訊
PUT blog/_doc/1
{
"content":"I like Elasticsearch",
"time":"2019-01-01T00:00:00",
"user":{
"userid":1,
"username":"Jack",
"city":"Shanghai"
}
}
案例2:包含物件陣列的文件
# 電影的Mapping資訊
PUT my_movies
{
"mappings" : {
"properties" : {
"actors" : {
"properties" : {
"first_name" : {
"type" : "keyword"
},
"last_name" : {
"type" : "keyword"
}
}
},
"title" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
}
# 寫入一條電影資訊
POST my_movies/_doc/1
{
"title":"Speed",
"actors":[
{
"first_name":"Keanu",
"last_name":"Reeves"
},
{
"first_name":"Dennis",
"last_name":"Hopper"
}
]
}
# 查詢電影資訊
POST my_movies/_search
{
"query": {
"bool": {
"must": [
{"match": {"actors.first_name": "Keanu"}},
{"match": {"actors.last_name": "Hopper"}}
]
}
}
}
- 儲存時,內部物件的邊界沒有在考慮在內,JSON格式被處理成扁平鍵值對的結構
- 當對多個欄位進行查詢時,導致了意外的搜尋結果
- 可以用 Nested Data Type 解決這個問題
- Nested 資料型別:允許物件陣列中的物件唄獨立索引
- 使用 Nested 和 Properties 關鍵詞,將所有 actors 索引到對個分隔的文件
- 在內部,Nested 文件會被儲存在兩個 Lucene 文件中,查詢時做join處理
PUT my_movies
{
"mappings" : {
"properties" : {
"actors" : {
"type": "nested",
"properties" : {
"first_name" : {"type" : "keyword"},
"last_name" : {"type" : "keyword"}
}},
"title" : {
"type" : "text",
"fields" : {"keyword":{"type":"keyword","ignore_above":256}}
}
}
}
}
- 在內部,Nested 文件被儲存在兩個Lucene 文件中
# Nested 查詢
POST my_movies/_search
{
"query": {
"bool": {
"must": [
{"match": {"title": "Speed"}},
{
"nested": {
"path": "actors",
"query": {
"bool": {
"must": [
{"match": {
"actors.first_name": "Keanu"
}},
{"match": {
"actors.last_name": "Hopper"
}}
]
}
}
}
}
]
}
}
}
//返回
"hits" : {
"total" : {
"value" : 0,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
}
# Nested Aggregation
POST my_movies/_search
{
"size": 0,
"aggs": {
"actors": {
"nested": {
"path": "actors"
},
"aggs": {
"actor_name": {
"terms": {
"field": "actors.first_name",
"size": 10
}
}
}
}
}
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結