關于樹結構的數(shù)據(jù),如何在關系型數(shù)據(jù)庫中表示,已經(jīng)成為了一個老生常談的問題.在這個項目中,我們也遇到了這個問題.
目前,業(yè)界中已經(jīng)給出了一些解決方案,最常見,最常用的是這三種:Parent-Child, Materialized Path, Nested Sets.其中,討論最多的,又是Nested Sets.
今天,在選擇方案的過程中,對這幾種方案,都做了一個詳細的比較,也針對我們的使用場景,做了一些假設.最終,選擇了第二種方式,即Materialized Path.
這幾種方案,其實都不完美.
我們的主要的使用場景,基本如下:
- 查詢樹中全部的節(jié)點,并按其層次關系,組成一個JSON對象,返回給前臺
- 分頁查詢,即查詢樹中一定數(shù)量的一級節(jié)點,以及這些一級節(jié)點的全部子節(jié)點
- 增加一個葉子節(jié)點
- 刪除一個節(jié)點,可能是葉子節(jié)點,也可能是非葉子節(jié)點
- 修改一個節(jié)點的數(shù)據(jù)
我們的樹結構中的特點有:
- 一級節(jié)點比較多,二級以后的節(jié)點應該就很少了
根據(jù)這個特色,我們設計主鍵字段的為36位的uuid,路徑字段的數(shù)據(jù)類型為TINYTEXT,因為TINYTEXT的長度為255 bytes,基本上能夠支持七層深度的樹.如果我們采用其他的數(shù)據(jù)類型,比如說,TEXT,其長度為65535 bytes,那可以支持更深的樹.
那我們的主鍵字段,為什么不采用可以AUTO_INCREMENT的int型呢?考慮到AUTO_INCREMENT是串行操作的,當同時有多個插入操作時,可能會存在性能上的問題.MySQL到底是怎么實現(xiàn)的,我也沒看過.但是我覺得在AUTO_INCREMENT這里,至少應該有一個鎖機制,來保證生成的id是連續(xù)并且唯一的.就算沒有,在計算機底層執(zhí)行主鍵id+1的操作時,也一定是串行的.而我們采用在代碼內自己生成UUID的方式,就可以避免這個約束.
我們之前在查找解決方案的時候,在對這三種方案的對比中,很多文章都使用的是最常規(guī)的樹的操作的例子.但是,由于我們對樹的操作有一些特殊性,比如,刪除一個節(jié)點,就連帶著刪除這個節(jié)點的子樹,使得使用Materialized Path這種方案,比較方便.其實Nested Sets這種方案,也是不錯的,但是實現(xiàn)起來比這個復雜一些,就沒有采用它.當然,還有其他的方案,比如使用專門的圖數(shù)據(jù)庫.