本文同步发布与程序员小墨微信公众号,地址:https://mp.weixin.qq.com/s/bEtIahAgFIoof_3b7i9v_w
最近在做一个文件夹的需求,需要将系统中现有的项目按照多层嵌套文件夹的形式进行分类。
由于这是一个全新的需求,所以在开发之初,需要尽可能的设计好这套结构,来兼容之后可能会有的一些需求变化等。
在设计这套结构的时候,我们也想到过很多方案,最终选择了一套最适合我们实际需求的解决方案。
下面就把我所想到的一些结构都列出来,并分析一下每套结构的优缺点。
其实在软件设计的过程中也是一个不断取舍的过程,就像我们在高并发、高可用和高性能三者之间进行取舍一样。不同的方案有不同的优缺点,不存在绝对好的方案,只是在确定的需求面前,我们能够找到一套最适合的方案来进行实现。
关于以下的方案,其实也不仅局限于文件夹,像多层评论、组织结构等需求也是可以进行参考的。
在写这篇文章的时候也查了一些参考资料,我在文章中最后部分列出了,感兴趣的小伙伴也可以前往深入钻研一下。也可以自行前往搜索引擎搜索 树形结构存储
、无限层级分类 数据库设计
或其他类似关键词。
基础方案
方案一 使用 parent_id
存储每个文件夹的
parend_id
。
优点
根目录可以不用保存:约定
id = 0 / '' / NULL
等为父级文件夹即可。(特别是针对现有系统进行改动的话,我们不需要在系统升级时为每个用户创建一个根目录)更新维护方便:调整文件夹层级结构,添加文件夹,删除文件夹等
缺点
不支持排序
需要递归查询:如果嵌套层数过多,需要进行多次查询数据库,效率会比较低
查询文件夹是哪一级需要递归,比较复杂
适用场景
对于默认不用展开,每次都需要用户手动展开文件夹的系统,用这种存储方式会比较好。
方案二 使用 child_ids
将文件夹下的所有子文件夹id按照逗号分隔形式,存成一个字符串。
优点
支持排序
更新维护方便:调整文件夹层级结构,添加文件夹,删除文件夹等
不需要递归查询:可以直接从父级文件夹顺着查下来
缺点
不方便查找文件夹所在的父文件夹 文件夹下能够创建的子文件夹数量是有限的:由于所有子文件夹都存在一个单元格中,所以当一个文件夹中的子文件夹过多时,可能会造成字段超长的问题。 需要保存根文件夹:对于新系统来说这个缺点可以不受影响,但是对于线上已有系统,我们添加文件夹功能后需要为每个用户在数据库中提前创建好根文件夹。
方案三 使用 parent_id + child_ids
同时采用 基础方案一(parent_id) 和 基础方案二(child_ids)
优点
同时包含方案一和方案二的所有优点 child_ids
弥补了方案一不支持排序和需要递归查询的缺点parent_id
弥补了方案二需要保存根文件夹的缺点
缺点
维护成本略高:调整文件夹层级关系的时候,需要同时修改
parent_id
和child_ids
child_ids
字段超长问题依然存在
方案四 使用文件夹路径
将文件夹的id按照
/
\
,
;
等分隔符进行分割的方式进行存储,类似 Windows 或 linux 的文件路径。(注意不是存的不是文件夹名,因为文件夹名可以修改,但是对于某个文件夹,其在创建时id就已经确定,之后不会再进行修改了)
优点
查询方便:不需要递归查询
缺点
不支持排序
查询某文件夹下所有层级子文件夹
SELECT id, name FROM `folder` WHERE path LIKE '/xxx/xxx/%'
方案五 ClosureTable
参考这篇文章中的方案三:https://www.cnblogs.com/LoveShare/p/14203206.html
优点
查询很方便:在查询树形结构的任意关系时都很方便
缺点
占用空间大:需要存储的数据量比较多,索引表需要的空间比较大 增加和删除节点相对麻烦,极不方便修改:修改基本需要通过删除+插入方式完成
适用场景
适用于纵向结构不是很深,查询较多,增删操作不频繁的场景
方案六 前序遍历树(不太推荐)
关于前序遍历树的讲解大家可以看下这篇文章:https://blog.kaciras.com/article/6/store-tree-in-database#%E5%89%8D%E5%BA%8F%E9%81%8D%E5%8E%86%E6%A0%91
优点
略
缺点
添加文件夹、删除文件夹、修改文件夹层级结构,文件夹排序等操作不方便维护 无法查询某一级的所有子文件夹,和查询文件夹是哪一级 不够直观,后期如果需要调整代码可能会比较复杂
方案七 多叉树转二叉树(不推荐)
将多叉树转为二叉树进行存储
列出此方案仅供思路拓展,虽然技术上可以实现,但是不便于他人快速理解
优点
便于存储:转为二叉树之后便于存储
缺点
查询时需要查出整棵树,然后进行转化 实现时对编码人员数据结构与算法要求较高
方案八 整体存储(不推荐)
将整个文件夹层级结构整体存储(例如保存为 JSON)
列出此方案仅供思路拓展,不建议使用在正式项目之中
优点
维护起来很方便
缺点
只适用于小项目,不适合并发修改场景 无法查出项目所在文件夹:对于多个用户的场景,无法查出某个项目在哪个用户的文件夹下 无法实现无限分级,文件夹数量受限:由于字段长度限制,文件夹整体数量和能够保存的文件夹属性受限 不便于权限管理:例如加密文件夹、为不同用户设置不同访问权限等场景
扩展方案一:支持排序
在 基础方案一(parent_id) 或 基础方案四(文件夹路径) 的基础上添加排序字段,以保存文件夹顺序。
一、添加 order
添加一个
order
/index
字段(保存文件夹排序后的索引位置)
优点
包含原方案的优点
缺点
层级结构调整后,需要同时更新 order
排序后, order
需要重新调整,需要更新的行会比较多
适用场景
适用于文件夹层级结构及排序改动较少,查询较多的场景
二、添加前/后指针
添加一个
before
/after
字段(保存前一个或者后一个文件夹id)
优点
调整文件夹排序方便
缺点
查询顺序时不太方便
适用场景
适用于文件查询较少,修改层级结构或排序操作较多的场景
扩展方案二:提高查询效率
一、添加所属层级
在 基础方案一 ~ 基础方案四 的基础上添加添加
level
(所属层级) 字段查询时首先查出根文件夹下所有层次的子文件夹,然后按照 parent_id + level 过滤条件即可查出对应文件夹下的子文件夹
优点
不需要递归查询:一步到位即可查出结果
缺点
增加维护成本:多添加了一个字段,增加了更新维护成本
适用场景
适用于分层查询文件夹层级结构,而不需要一次性查出整个文件夹结构的场景。
二、使用 parent_id + 文件夹路径
同时采用 parent_id 和文件夹路径
优点
文件夹路径可以缓解层级较多时递归的性能问题
缺点
不遵守数据库范式 因为保存了文件夹路径,所以受字段长度限制,无法实现真正意义上的无限分类
其他方案
按照根文件夹/用户维度进行隔离
在以上部分方案的基础上添加 master_id
字段(保存根目录id)或者可以隔离用户的字段(例如user_id
),可以方便的通过该字段查出这个根文件夹/用户下的所有层级的文件夹,实现根文件夹/用户隔离。
参考资料:
数据库无限层级分类设计:https://www.cnblogs.com/LoveShare/p/14203206.html
在数据库中存储一棵树,实现无限级分类:https://segmentfault.com/a/1190000014284076
数据库模式设计之层次结构:https://blog.csdn.net/qq_44202160/article/details/123541746
mysql(多级分销)无限极数据库设计方法:https://www.jianshu.com/p/a2c67fd33338
本站文章除注明转载/出处外,均为原创,若要转载请务必注明出处。转载后请将转载链接通过邮件告知我站,谢谢合作。本站邮箱:admin@only4.work
尊重他人劳动成果,共创和谐网络环境。点击版权声明查看本站相关条款。