在事务数据库中拥有涉及多个表的计算列的最佳方法是什么?
背景
我们的事务型 MySQL 数据库包括如下简化模型的事实表:
CREATE TABLE LU_PROJECT(
PROJECT_ID int,
PROJECT_DESC varchar(60)
);
CREATE TABLE F_PROJECT_BUDGET(
PROJECT_ID int,
BUDGET numeric(15, 2)
);
CREATE TABLE F_TASK_BUDGET(
TASK_ID int,
BUDGET numeric(15,2)
);
CREATE TABLE F_MONEY_USED(
REPORTED_TIME datetime,
TASK_ID int,
MONEY_USED numeric(15, 2)
);
Task
是 的孩子Project
。任务 ID 不是唯一的,但项目/任务对是唯一的。
要求
我们需要保持项目所有任务的总预算<=项目预算的不变性。
此外,我们经常需要运行一个查询,该查询返回一个包含以下列的结果集:
PROJECT_ID
, PROJET_DESC
, PROJECT_BUDGET
, TASK_COUNT
,PROJECT_MONEY_USED
问题
我们关心性能。最简单的解决方案需要更新以命中三个事实表:
- 检查添加到
F_MONEY_USED
不会使我们超出任务的预算F_TASK_BUDGET
。 - 检查添加资金不会使我们超出项目的总预算
- 写到
F_MONEY_USED
。
而我们需要获取统计信息的查询会命中三个事实表(除了查找表,上面的模型中没有):
- 加入
LU_PROJECT
到PROJECT_BUDGET
从F_PROJECT_BUDGET
组中PROJECT_ID
- 加入
LU_PROJECT
以F_TASK_BUDGET
获取TASK_COUNT
分组依据PROJECT_ID
- 加入
LU_PROJECT
F_MONEY_USED
以获取PROJECT_MONEY_USED
分组依据PROJECT_ID
- 加入
LU_PROJECT
上面的中间结果并得到PROJECT_DESC
。
问题是加入的次数很多,读写都会频繁发生。
潜在解决方案
我们正在考虑的一种解决方案是添加一个PROJECT_MONEY_USED
字段,F_PROJECT_BUDGET
该字段将在写入时更新F_TASK_BUDGET
。这会减慢写入速度,但会加快读取速度。
该解决方案还将引入复杂性和数据完整性问题,因为事实表将不再是“基本的”。这违反了数据仓库原则,但我无法确定它是否符合事务数据库的犹太教规。
如果我们可以在 UI 中进行乐观渲染,那么写入速度的下降可能不是什么大问题,但这会带来更多的复杂性。
考虑的其他解决方案
- 对于写入,我们正在考虑使用触发器来保留不变量。
- 对于读取,计算列看起来很有希望,但它们不允许在 MySQL 中访问多个表。
- 对于读取,物化视图可能不是一个选项,数据需要实时更新。
概括
是否有更好的解决方案以安全、简单和高性能的方式进行读写?