在數據庫中存儲樹形結構的數據,這是一個非常普遍的需求,典型的比如論壇系統的版塊關系。在傳統的關系型數據庫中,就已經產生了各種解決方案。
此文以存儲樹形結構數據為需求,分別描述了利用關系型數據庫和文檔型數據庫作為存儲的幾種設計模式。
A.關系型數據庫設計模式1
id | name | parent_id |
1 | A | NULL |
2 | B | 1 |
3 | C | 1 |
4 | D | 2 |
上圖表示了傳統的設計方法之一,就是將樹形結構的每一個結點作為關系型數據庫中的一行進行存儲,每一個結點保存一個其父結點的指針。
- 優點:結構簡單易懂,插入修改操作都很簡單
- 缺點:如果要獲取某個結點的所有子結點,將是一件很惡心的事
B.關系型數據庫設計模式2
id | name | parent_id | left | right |
1 | A | NULL | 1 | 8 |
2 | B | 1 | 2 | 5 |
3 | C | 1 | 6 | 7 |
4 | D | 2 | 3 | 4 |
上圖在模式1的基礎上多了兩列,left和right,相當於btree中的左右分支,分別存儲了左右分支結點的最大值和最小值。
- 優點:要查找一個結點的子結點很容易,只需要做一個范圍查詢就行了(比如B節點的子結點,只需要查詢 id >=2 && id<=5)
- 缺點:由於樹結構存在在這里面了,所以添加或修改已存在結點將可能產生連鎖反應,操作過於復雜
C.文檔型數據庫設計模式1
{ "name": "A", "children": [ {"name": "B", "children": [{"name": "D"}]}, {"name": "C"} ] }
將整個樹結構存成一個文檔,文檔結構既樹型結構,簡明易懂。
- 優點:簡明易懂
- 缺點:文檔會越來越大,對所有結點的修改都集中到這一個文檔中,並發操作受限
D.文檔型數據庫設計模式2
{"_id": "A", "children": ["B", "C"]} {"_id": "B", "children": ["D"]} {"_id": "C"} {"_id": "D"}
將每個結點的所有子結點存起來
- 優點:結構簡單,查找子結點方便
- 缺點:查找父結點會比較麻煩
E.文檔型數據庫設計模式3
{ "leaf": "A", "children": [ {"leaf": "B", "children": [{"leaf": "D"}] }, {"leaf": "C"} ] } {"_id": "A", ...} {"_id": "B", ...} {"_id": "C", ...} {"_id": "D", ...}
充分利用文檔型存儲schema-less的優點,先利用上面C方案存存儲一個大的樹形文檔,再將每一個結點的其他信息單獨存儲。
- 優點:操作方便,結構上的操作可以直接操作大的樹形文檔,數據上的操作也只需要操作單條數據
- 缺點:對所有結點的修改都集中到這一個文檔中,並發操作受限