前言
看到网上很多文章吐槽,邻接表模型用递归获取祖先节点/后代节点,导致性能很差。
而用闭包表就能空间换时间,很快获取。书本也是这么夸闭包表的。
可是我看着闭包表的结构,没搞懂它是如何走索引,来快速获取祖先节点/父节点/子节点的?
闭包表结构
引入一个实际例子说吧。假设闭包表结构为:(简化,后续用 节点指代的名称
代替 节点ID
)
CREATE TABLE 闭包表 (
祖先节点ID INT,
后代节点ID INT,
这俩节点距离 INT,
PRIMARY KEY (祖先节点ID, 后代节点ID)
);
现有一张约 66W 行数据的 5 级地区表,各级数量为:(参考自 中华人民共和国行政区划(五级))
省 | 市 | 区 | 街 | 村 |
---|---|---|---|---|
31 | 342 | 2990 | 41356 | 618134 |
那么,建立的闭包表的行数量为:
1*1 (根, 根) + 31*2 (根, 省), (省, 省) + 342*3 + 2990*4 + 41356*5 + 618134*6 = 3928633 行
1. 如何快速获取 31 个省份?
网上很多的 SQL
是这样的:
SELECT 后代节点 FROM 闭包表 WHERE 祖先节点 = '根节点' AND /* 缺失:后代节点 = ? AND */ 距离 = 1
可是,根据最左匹配原则,距离 = 1
是无法利用的,所以上述 SQL
要扫描 66W 行(每个地区都有到根节点的记录),才能获得结果??
2. 如何获取“杭州”所属省份?
网上的 SQL
:
SELECT 祖先节点 FROM 闭包表 WHERE /* 缺失:祖先节点 = ? AND */ 后代节点 = '杭州' AND 距离 = 1
根据最左匹配原则,利用不了索引,所以上述 SQL
要扫描 390W 行,才能获得“杭州”的父节点??
3. 如何获取“哈尔滨市zf亚布力滑雪度假区管理委员会虚拟社区”的省市区街村全称?(挑了个名字最长的。。)
网上的 SQL
:
SELECT 祖先节点 FROM 闭包表 WHERE /* 缺失:祖先节点 = ? AND */ 后代节点 = '...' ORDER BY 距离 DESC
同理,这也是要扫描 390W 行,才能得到结果??
“闭包表”对我来说是个新的概念,学到了!
从数据结构的设计来看,这个表是记录了每两个节点之间的关系,如果这个理解无误,这个表的数据量应该是相当的的大。题中给出了数据:
快速获取 31 个省份这个 SQL,在建立(祖先,距离)复合节点索引的情况下,这个 SQL 应该还是快
但是如果要查杭州所在的省份,
距离 = 1
这个条件是因为你知道杭州是市级节点。不过显然这个索引需要(后代,距离)复合索引。或者只有(后代)索引,应该可以先过滤出杭州作为后代的相关节点(也就几条),再从中筛选距离为 1 的。基于(后代)索引,查某个地方的所有父节点也快。
综上,如果分别建立(祖先,距离)索引和(后代)索引,应该能有效提升查询速度。