我们正在开发一个监控系统。在我们的系统中,值由在不同服务器上运行的代理报告。报告的观察结果可以是如下值:
- 一个数值。例如“CPU USAGE”= 55。意思是 55% 的 CPU 在 使用)。
- 触发了特定事件。例如“备份完成”。
- 状态:例如SQL Server 离线。
我们想要存储这些观察结果(这些观察结果是事先不知道的,将被动态添加到系统中而无需重新编译)。
我们正在考虑像这样向观察表添加不同的列:
IntMeasure -> INTEGER
FloatMeasure -> FLOAT
Status -> varchar(255)
因此,如果我们希望存储的值是一个数字,我们可以根据类型使用 IntMeasure 或 FloatMeasure。如果该值是一个状态,我们可以存储状态文字字符串(如果我们决定添加一个 Statuses(id, name) 表,则可以存储一个状态 id)。
我们假设有一个更正确的设计是可能的,但由于连接和动态表名取决于类型,可能会变得缓慢和黑暗?如果我们不能在查询中提前指定表,连接将如何工作?
最佳答案
我没有做过正式的研究,但根据我自己的经验,我猜测超过 80% 的数据库设计缺陷是在设计时将性能作为最重要(如果不是唯一)的考虑因素而产生的。
如果好的设计需要多个表,请创建多个表。不要自动假设连接是应该避免的。它们很少是性能问题的真正原因。
在数据库设计的所有阶段,首要考虑的是数据完整性。 “答案可能并不总是正确的,但我们可以很快为您提供答案”不是任何商店都应该努力的目标。一旦数据完整性被锁定,如果性能成为问题,它就可以得到解决。不要牺牲数据完整性,尤其是为了解决可能不存在的问题。
考虑到这一点,看看您需要什么。您有需要存储的观察结果。这些观察在属性的数量和类型上可能会有所不同,可以是测量值、事件通知和状态变化等,还可以添加 future 观察的可能性。
这似乎符合标准的“类型/子类型”模式,“观察”条目是类型,每种类型或观察类型是子类型,并建议某种形式的类型指示符字段,例如:
create table Observations(
...,
ObservationKind char( 1 ) check( ObservationKind in( 'M', 'E', 'S' )),
...
);
但是在检查约束中对这样的列表进行硬编码具有非常低的可维护性级别。它成为模式的一部分,只能使用 DDL 语句进行更改。这不是您的 DBA 所期待的。
所以在他们自己的查找表中有各种观察:
ID Name Meaning
== =========== =======
M Measurement The value of some system metric (CPU_Usage).
E Event An event has been detected.
S Status A change in a status has been detected.
(char 字段也可以是 int 或 smallint。我在这里使用 char 来说明。)
然后用 PK 和所有观察共有的属性填写观察表。
create table Observations(
ID int identity primary key,
ObservationKind char( 1 ) not null,
DateEntered date not null,
...,
constraint FK_ObservationKind foreign key( ObservationKind )
references ObservationKinds( ID ),
constraint UQ_ObservationIDKind( ID, ObservationKind )
);
在 Kind 字段和 PK 的组合上创建唯一索引可能看起来很奇怪,它本身是唯一的,但请耐心等待。
现在每个种类或子类型都有自己的表格。请注意,每种观察都会得到一个表,而不是数据类型。
create table Measurements(
ID int not null,
ObservationKind char( 1 ) check( ObservationKind = 'M' ),
Name varchar( 32 ) not null, -- Such as "CPU Usage"
Value double not null, -- such as 55.00
..., -- other attributes of Measurement observations
constraint PK_Measurements primary key( ID, ObservationKind ),
constraint FK_Measurements_Observations foreign key( ID, ObservationKind )
references Observations( ID, ObservationKind )
);
前两个字段对于其他类型的观察是相同的,除了检查约束将值强制为适当的类型。其他字段的数量、名称和数据类型可能不同。
让我们检查一个可能存在于 Measurements 表中的示例元组:
ID ObservationKind Name Value ...
==== =============== ========= =====
1001 M CPU Usage 55.0 ...
为了使该元组存在于该表中,匹配条目必须首先存在于观察表中,其 ID 值为 1001 且观察类型值为“M”。 ID 值为 1001 的其他条目不能存在于观察表或测量表中,并且根本不能存在于任何其他“种类”表(事件、状态)中。这对所有类型的表都是一样的。
我会进一步建议为每种观察创建一个 View ,它将提供每种类型与主观察表的连接:
create view MeasurementObservations as
select ...
from Observations o
join Measurements m
on m.ID = o.ID;
任何仅与测量一起工作的代码都需要只命中此 View 而不是基础表。使用 View 在应用程序代码和原始数据之间创建抽象墙大大增强了数据库的可维护性。
现在创建另一种观察,例如“错误”,涉及到 ObservationKinds 表的简单 Insert 语句:
F Fault A fault or error has been detected.
当然,您需要为这些错误观察创建一个新表和 View ,但这样做不会对现有表、 View 或应用程序代码产生影响(当然,编写新代码以使用新的观察结果)。
关于mysql - 如何在mysql中存储一个类型可以是数字、日期或字符串的数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37903232/