MySQL 存储 DAG 简单工程实践
MySQL 存储 DAG 简单工程实践 https://cui1994.github.io/2020/04/21/mysqldag/
MySQL 存储 DAG 简单工程实践
项目中需要使用 DAG 结构表示离线任务、数据源血缘关系及数据流向,采用 MySQL 来存储 DAG 数据结构并完成增删改查等常规功能。这里对 MySQL 存储、操作、遍历 DAG 图的方法进行记录。
这部分内容部分参照了《Designing Data-Intesive Applications》书中对于图状数据模型的描述。
名词解释
- DAG:有向无环图,包含节点(Vertex)和边(Edge)。
数据模型
需要两张表存储 DAG 的结构信息,节点表(vertex)和边表(edge)。
Insert
新增节点,在节点表和边表中新增相应记录即可。
Delete
删除节点,节点记录以及与其关联的边记录全部删除。
Update
编辑节点,对于节点本身的变更,对节点表相应记录 update 即可。涉及边的调整,比较简单的方式是先将所关联的边全部删除,再重新创建,并引入事务保证其原子性和可见性。
Get
对单一节点进行 DAG 构建,需要根据查看出度/入度以及拓扑深度对拓扑进行递归遍历。由于 MySQL 8+ 才有递归查询语法支持,所以只能在代码中实现递归逻辑。这里以查看入度(向上遍历)为例。
额外考量
基本逻辑确定后,需要结合具体业务分析需要调整和注意的 case。
1.节点自依赖
节点自依赖实际上破坏了 DAG 的模型(因为形成了环),要支持这种场景,可以将自依赖看做正常的边(start_vertext 和 end_vertext 相同),在节点遍历时如果碰到自依赖的边,不要将其压入递归队列中继续遍历即可。
2.循环依赖
节点间接引用了自身,同样属于破坏 DAG 模型,但与自依赖(直接引用自身)不同,这种场景一般都是需要避免的。 一般做法是在节点变更操作(Insert、Update)之前进行循环依赖检查,即先将节点的完整 DAG 构建出来,如果 DAG 中不包含要新增的节点,则可以进行操作。
3.线程安全
MySQL 数据模型将整个 DAG 的信息分散在了不同记录中,而且这些记录之间只能逐层识别关联,无法利用数据库事务做到隔离。 因此,需要借助锁实现线程安全,但锁的粒度需要和数据的预先分组相同,在我的项目中,任务在不同业务组中完全不可见,任务之间不能跨业务组建立关联,因此我的锁粒度就是业务组级别。 最坏的情况是,如果你不对这些数据进行分组,系统中同时只能发生一个 DAG 操作。
参考资料:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
· 使用C#创建一个MCP客户端
2022-03-07 nginx 静态文件 文件乱码
2022-03-07 结构体嵌套 树形结构 json字符串与结构体相互转换
2019-03-07 summary
2019-03-07 函数对象与闭包的误区 函数的指针 高阶函数 将一部分处理以函数对象的形式转移到外部从而实现了算法的通用化
2019-03-07 Leaf:美团分布式ID生成服务开源
2019-03-07 出于性能考虑,C语言自动地以传地址的方式将数组传递给被调函数 const 编译错误 最小权限原则
2019-03-07 好的软件工程 与 高的性能 冲突 耗费处理器时间的函数调用