闭包表模型(Closure Table)通俗教程
在现代的项目中,很多情况下我们需要存储和管理树形结构的数据,比如:
- 类目树:一个电商网站中的商品分类系统。
- 组织架构:公司中的部门、团队及员工之间的层级关系。
- 评论系统:文章下的评论及回复的层级结构。
闭包表(Closure Table)是一种常用的数据库设计模式,它用来表示这种层次结构的数据,同时能高效地处理查询树的某些操作,如:获取某个节点的所有子节点,或者获取某个节点的完整层级路径。
为什么使用闭包表?
1. 问题背景
在一个常见的树形结构中,每个节点只存储它的父节点的 ID(邻接表模型),比如类目系统:
ID | 名称 | 父ID
----------------------
1 | 电子产品 | NULL (顶级类目)
2 | 手机 | 1 (父类目是电子产品)
3 | 笔记本电脑 | 1 (父类目是电子产品)
4 | 安卓手机 | 2 (父类目是手机)
在这种结构下,虽然每个节点知道它的父节点是谁,但如果我们想查询某个节点的所有子孙节点(比如找出所有属于“手机”的子类目),我们就需要进行递归查询,性能不高。对于深层次的树结构来说,递归会变得越来越复杂,查询性能下降。
2. 闭包表的优点
闭包表通过存储每个节点和它所有祖先及后代的关系,可以在常数时间内查询到一个节点的所有子孙节点或祖先节点,无需递归,大大提升了查询性能。
闭包表的工作原理
闭包表通过创建一个额外的表,来存储每个节点与其所有祖先和后代之间的关系。这个表有三个重要的字段:
- ancestor_id:代表某个节点的祖先(父类目或更高层级的类目)。
- descendant_id:代表某个节点的后代(子类目或更低层级的类目)。
- depth:表示祖先节点与后代节点之间的距离(层级深度)。
通过这种方式,我们不仅存储了父子关系,还存储了所有祖先与后代之间的关系。
举个例子
假设我们有这样一个类目树:
电子产品
├── 手机
│ └── 安卓手机
└── 笔记本电脑
每个类目在类目表中的数据如下:
ID | 名称