MongoDB 文件夹树结构重构技术方案
当前问题分析
当前系统使用嵌套的树形结构存储文件夹关系,导致:
- 并发修改时需要锁定整个树结构,成为性能瓶颈
- 大规模数据时查询和修改效率低下
- 难以实现部分树的加载和更新
重构方案:离散化存储
1. 数据模型重构
当前模型 (嵌套树)
1 | class RootFolder(Document): |
新模型 (离散存储)
1 | class Folder(Document): |
2. 迁移方案
迁移脚本
1 | def migrate_tree_to_discrete(customer_id): |
3. 并发控制优化
新模型下不再需要全局锁,可以采用:
- 文档级锁 (只锁定正在修改的文件夹)
- 乐观并发控制 (使用版本号字段)
1 | class Folder(Document): |
4. 查询优化
获取子树
1 | def get_subtree(folder_id, depth=None): |
获取路径
1 | def get_path(folder_id): |
5. 文档模型调整
1 | class Doc(Document): |
6. API调整
创建文件夹:
1
2
3
4
5
6
7
8
9
10
11
12def create_folder(customer_id, domain_id, name, parent_id=None):
parent = Folder.objects.get(id=parent_id) if parent_id else None
path = f"{parent.path}{name}/" if parent else f"/{name}/"
folder = Folder(
customer=customer_id,
domain=domain_id,
name=name,
parent=parent,
path=path
).save()
return folder移动文件夹:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21def move_folder(folder_id, new_parent_id):
folder = Folder.objects.get(id=folder_id)
new_parent = Folder.objects.get(id=new_parent_id)
# 更新路径
new_path = f"{new_parent.path}{folder.name}/"
# 更新所有子文件夹路径
def update_children_paths(parent_folder, old_base, new_base):
children = Folder.objects(parent=parent_folder)
for child in children:
child.path = child.path.replace(old_base, new_base, 1)
child.save()
update_children_paths(child, old_base, new_base)
old_path = folder.path
folder.parent = new_parent
folder.path = new_path
folder.save()
update_children_paths(folder, old_path, new_path)
实施步骤
- 备份数据: 完整备份当前数据库
- 开发环境测试: 在开发环境实现并测试迁移脚本
- 性能测试: 对新模型进行并发性能测试
- 分阶段部署:
- 先部署新模型代码,保持双写
- 运行迁移脚本
- 验证数据一致性
- 切换读取到新模型
- 移除旧模型代码
- 监控: 部署后密切监控性能和数据一致性
回滚方案
- 保留旧模型代码和数据
- 如果出现问题,切换回旧模型
- 使用备份恢复数据
性能预期
- 并发性能提升: 从全局锁变为文档级操作
- 查询效率: 简单查询更快,复杂树查询可能需要优化索引
- 存储空间: 可能略有增加,但换来更好的扩展性
这种重构将使系统能够更好地处理并发操作和大规模数据,同时保持数据一致性。