iMySQL(一)

1 如何在數(shù)據(jù)庫(kù)中存儲(chǔ)層級(jí)結(jié)構(gòu)


(1) 鄰接表模型


  • 求節(jié)點(diǎn)的路徑
    舉例來(lái)說(shuō),“Cherry”所在的路徑為Food > Fruit > Red > Cherry。在這里,我們可以從Cherry開(kāi)始查起,然后遞歸查詢(xún)查詢(xún)節(jié)點(diǎn)前的節(jié)點(diǎn),直到某節(jié)點(diǎn)的父節(jié)點(diǎn)ID為0(Food)。

  • 優(yōu)點(diǎn)
    簡(jiǎn)單易懂

  • 缺點(diǎn)
    執(zhí)行起來(lái)效率低下。我們對(duì)于每個(gè)結(jié)果,期望只需要一次查詢(xún);可是當(dāng)使用鄰接表模型時(shí)嵌套的遞歸使用了多次查詢(xún),當(dāng)樹(shù)很大的時(shí)候,這種慢就會(huì)表現(xiàn)得尤為明顯。

上帝是公平的,當(dāng)在執(zhí)行時(shí)效率低下,意味著可以增加預(yù)處理的程度。

(2)MPTT(修改過(guò)的前序遍歷算法)

  • 原理
    現(xiàn)在我們把樹(shù)“橫”著放。如下圖所示,我們首先從根節(jié)點(diǎn)(“Food”)開(kāi)始,先在它左側(cè)標(biāo)記“1”,然后我們到“Fruit”,左側(cè)標(biāo)記“2”,接著按照前序遍歷的順序遍歷完樹(shù),依次在每個(gè)節(jié)點(diǎn)的左右側(cè)標(biāo)記數(shù)字。
  • 實(shí)現(xiàn)
  • 打印樹(shù)
    如果要想打印樹(shù),你只需要知道你要檢索的節(jié)點(diǎn)。比如,想要打印“Fruit”的子樹(shù),可以查詢(xún)左數(shù)大于2而小于11的節(jié)點(diǎn)。
SELECT * FROM tree WHERE lft BETWEEN 2 AND 11;
  • 求節(jié)點(diǎn)的路徑
    對(duì)于某節(jié)點(diǎn),我們只需求出左數(shù)值小于其左數(shù)值、右數(shù)大于其右數(shù)的所有節(jié)點(diǎn)。比如說(shuō)“Cherry”這個(gè)節(jié)點(diǎn)(4-5)
SELECT title FROM tree WHERE lft < 4 AND rgt > 5 ORDER BY lft ASC;
  • 增加節(jié)點(diǎn)
    我們想添加“Strawberry“到”Red“節(jié)點(diǎn)下,那么“Red”節(jié)點(diǎn)的右數(shù)就得從6到8,而“Yellow”就得從7-10變成9-12,以此類(lèi)推。更新Red節(jié)點(diǎn)就意味著大于5的左數(shù)和右數(shù)都要增加2。
UPDATE tree SET rgt=rgt+2 WHERE rgt>5;
UPDATE tree SET lft=lft+2 WHERE lft>5;

INSERT INTO tree SET lft=6, rgt=7, title='Strawberry';
  • 優(yōu)點(diǎn)
    查詢(xún)的時(shí)候只需要一條SQL語(yǔ)句

2 進(jìn)入正題

  • Mysql架構(gòu)
    • Parser
      將用戶的查詢(xún)語(yǔ)句進(jìn)行解析,并創(chuàng)建一個(gè)內(nèi)部的數(shù)據(jù)結(jié)構(gòu)——分析樹(shù)
    • Optimizer
      各種優(yōu)化,例如重寫(xiě)查詢(xún)、選擇讀取表的順序,以及使用哪個(gè)索引等。
  • 類(lèi)目表結(jié)構(gòu)(是否可以改進(jìn)?)
CREATE TABLE `itemcats` (
  `cid` int(11) NOT NULL,
  `parent_cid` int(11) DEFAULT NULL,
  `name` varchar(50) NOT NULL,
  `is_parent` tinyint(1) NOT NULL,
  `status` varchar(20) DEFAULT NULL,
  `sort_order` int(11) DEFAULT NULL,
  `lft` int(11) NOT NULL,
  `rght` int(11) NOT NULL,
  `level` int(11) NOT NULL,
  PRIMARY KEY (`cid`),
  KEY `parent_cid` (`parent_cid`),
  KEY `lft_rght` (`lft`,`rght`),
  KEY `level` (`level`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  • 為什么InnoDB表要建議用自增列做主鍵?
    如果InnoDB表的數(shù)據(jù)寫(xiě)入順序能和B+樹(shù)索引的葉子節(jié)點(diǎn)順序一致的話,這時(shí)候存取效率是最高的
    • 使用自增列(INT/BIGINT類(lèi)型)做主鍵,這時(shí)候?qū)懭腠樞蚴亲栽龅?,和B+數(shù)葉子節(jié)點(diǎn)分裂順序一致;
    • 如果一個(gè)InnoDB表又沒(méi)有顯示主鍵,又有可以被選擇為主鍵的唯一索引,但該唯一索引可能不是遞增關(guān)系時(shí)(例如字符串、UUID、多字段聯(lián)合唯一索引的情況),該表的存取效率就會(huì)比較差。
  • 數(shù)值類(lèi)型
  • 字符串類(lèi)型
    • char(n)和varchar(n)中括號(hào)中n代表字符的個(gè)數(shù),并不代表字節(jié)個(gè)數(shù),所以當(dāng)使用了中文的時(shí)候(UTF8)意味著可以插入m個(gè)中文,但是實(shí)際會(huì)占用m*3個(gè)字節(jié)。
    • 同時(shí)char和varchar最大的區(qū)別就在于char不管實(shí)際value都會(huì)占用n個(gè)字符的空間,而varchar只會(huì)占用實(shí)際字符應(yīng)該占用的空間+1,并且實(shí)際空間+1<=n。
    • 超過(guò)char和varchar的n設(shè)置后,字符串會(huì)被截?cái)唷?/li>
    • char在存儲(chǔ)的時(shí)候會(huì)截?cái)辔膊康目崭?,varchar和text不會(huì)。
    • varchar會(huì)使用1-3個(gè)字節(jié)來(lái)存儲(chǔ)長(zhǎng)度,text不會(huì)。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容