puk*_*puk 5 rdbms postgresql database-design
我需要对涉及交换机(24 端口、48 端口、POE 以及这些的组合)和边缘设备(计算机、IP 摄像机、电话)的网络建模的帮助。从面向对象的角度来看,我可以在睡梦中用 C++ 编写代码。例如,交换机将继承自TwentyFourSwitch,如果它们有 POE,则来自POESwitch,那么在每个类中我将创建 24 个EthernetPort实例,每个实例都有一个连接功能,确保不会将以太网端口连接到 SFP 端口,或非 POE 端口到 POE 设备等。
使用 RDBMS,我们不断遇到问题。首先,我们组织了一切以最小化数据冗余。这是我们将要做的示例(为了保持代码简短,我使用了 2 和 4 端口交换机而不是 24 和 48)。
CREATE TABLE MACAddress{
id SERIAL INT PRIMARY KEY,
mac1 INT,
mac2 INT,
mac3 INT,
mac4 INT,
mac5 INT,
mac6 INT,
UNIQUE(mac1,mac2,mac3,mac4,mac5,mac6)
);
CREATE TABLE IPAddress{
id SERIAL INT PRIMARY KEY,
ip1 INT,
ip2 INT,
ip3 INT,
ip4 INT,
UNIQUE (ip1,ip2,ip3,ip4)
);
CREATE TABLE NetworkDevice{
id SERIAL INT PRIMARY KEY,
ip INT REFERENCES (IPAddress),
mac INT REFERENCES (MACAddress),
);
CREATE TABLE TwoPortSwitch{
id SERIAL INT PRIMARY KEY,
eth0 INT REFERENCES (NetworkDevice),
eth1 INT REFERENCES (NetworkDevice),
netId REFERENCES (NetworkDevice)
);
CREATE TABLE FourPortSwitch{
id SERIAL INT PRIMARY KEY,
eth0 INT REFERENCES (NetworkDevice),
eth1 INT REFERENCES (NetworkDevice),
eth0 INT REFERENCES (NetworkDevice),
eth1 INT REFERENCES (NetworkDevice),
netId REFERENCES (NetworkDevice)
);
CREATE TABLE Camera{
id SERIAL INT PRIMARY KEY,
eth0 INT REFERENCES (NetworkDevice),
netId REFERENCES (NetworkDevice)
);
Run Code Online (Sandbox Code Playgroud)
但是我们遇到的问题是它需要很多时间joins,unions而且很快就变得非常混乱。例如,如果您想知道 eth0 连接到什么 ip,则必须在 TwoPortSwitch、FourPortSwitch、Camera、NetworkDevice 和 IPAddress 上进行连接/联合。请注意,我们有大约 10 个不同的摄像头、10 个不同的开关和 5 台不同的计算机。
我们只是厌倦了把所有东西都压平了,所以现在所有的开关都在一个大的开关表中。它有 48 个 1 gig 以太网端口、48 个 1 gig SFP 端口和 48 个 10 gig SFP Plus 端口。总共有1000多列!
有没有更好的方法来解决这个问题?我正在认真考虑构建一个网络服务器并让 C++ 服务器始终运行。
Cra*_*ger 10
TL;DR:归一化,归一化,归一化。加入好,工会坏。有很多行的窄表好,宽表有几行坏。更少的通用表格好,很多非常具体的表格不好。
您正在尝试将关系建模为对象。这可以工作,但很快就会变得笨拙。它看起来像是 C++ 类的非常直接的映射,因此它有很多用于特定类型事物的表,并且没有规范化。
PostgreSQL 具有表继承功能,如果您真的想追求它,可以使这种方法更易于管理。然而,由于缺乏跨继承基关系的所有子关系的唯一索引支持,表继承的使用基本上与外键的使用不兼容。这使得继承远不如其他方式有用。
而是尝试对其进行关系建模。这里有两种思想流派,自底向上和自顶向下建模。在实践中,大多数情况下您会混合使用这两种方法。您的目标是建立一个规范化的数据库结构。
不过首先...
IPAddress并且MACAddress是不必要的您也在建模IPAddress并MACAddress作为实体,而它们不是。它们只是标量值。据我所知,这些可能是 C++class或 C 的直接翻译struct ,将 IP 地址存储为四个uint8_t八位字节,MAC 地址存储为六个uint8_t八位字节等。这在数据库中是完全没有必要的,应该完全取消。PostgreSQL 提供了满足这些特定需求的原生数据类型:
inetmacaddr......但如果你必须保持跨数据库移植,你将得到更好的建议储存您的IP地址为numeric与boolean显示IPv4或IPv6,而不是将它们存储为字节。您需要,numeric因为bigint(有符号的 64 位整数)对于 IPv6 地址来说不够大。
顺便说一句,在网络中,一个 MAC 地址可能有多个 IP 地址。虽然给定连接的子网中没有 IP 地址可能有多个 MAC 地址,但相同的 IP 地址可能出现在具有不同 MAC 地址的离散网络中,因此我不会尝试在模式级别对此进行建模。如果我这样做了,我会创建一个主键为的 NetworkIP 关系,(network_id integer REFERENCES Network, ipaddr)以允许相同的 IP 仅存在于断开连接的网络上。但是你必须考虑多级 NAT,它变得可怕。不要去那里。我会把它们当作标量。
现在我们已经去掉了 MACAddress 和 IPAddress,让我们看看结构。
你应该展平一切,但不是这样:
它有 48 个 1 gig 以太网端口、48 个 1 gig SFP 端口和 48 个 10 gig SFP Plus 端口。总共有1000多列!
与其“宽”,不如说“高”。不要害怕加入,加入是你的朋友。
您有各种特定类型的网络设备,其中每种都有各种附加特性。每个设备可能有一个或多个分配给它的 IP 地址和 MAC 地址(例如交换机管理地址)。此外,每个设备可能有零个或多个端口,每个端口有一个 MAC 地址和零个或多个 IP 地址。为了简化事情,我们将管理接口定义为虚拟端口。
因此,让我们对该核心结构进行建模。
CREATE TABLE device (
id serial primary key,
device_type text not null,
device_name text,
description text
);
-- A port is a logical port, which may correspond to a physical
-- plug, or a virtual port like an internal management interface,
-- IPMI controller, etc.
CREATE TABLE port (
device_id integer not null references device(id),
port_name text not null,
port_mac macaddr not null,
port_is_virtual boolean not null default 'f',
PRIMARY KEY (device_id, port_name),
UNIQUE(device_id, port_mac)
);
-- Now the IP address(es) for a port
CREATE TABLE port_ip (
port_id integer not null references port(id),
port_ipaddr ipaddr not null
);
-- A socket is a physical plug, which generally has a logical
-- port associated with it.
CREATE TABLE socket (
device_id integer references device(id),
socket_index integer not null,
socket_name text,
-- This might be a good use for an enum type, but for now
-- just use a check constraint
socket_type text not null check (socket_type IN ('rj45', 'sfp', 'sfpplus'),
PRIMARY KEY(device_id, socket_index)
);
-- A port may have a socket or not, and a socket may have a port
-- or not, so model via a join table. If you want to prevent
-- a socket having multiple ports or a port having multiple sockets
-- you could use additional unique constraints.
CREATE TABLE port_sockets (
device_id integer references device(id),
port_name text references port(port_name),
socket_index integer references socket(socket_index),
PRIMARY KEY (device_id, port_name, socket_index)
);
Run Code Online (Sandbox Code Playgroud)
看看它是如何“垂直”的——我们有一个port表,其中包含每个主机的每个端口的条目,等等。
所以现在你想对开关进行建模。这些是网络设备。他们有一定数量的基于模型的端口。
您可以直接在实体中跟踪端口数,也可以port在想知道时计算该交换机的s数。虽然跟踪交换机实体中的端口计数是一种重复数据的非规范化,但在这种情况下可能是值得的。所以我会做这样的事情:
CREATE TABLE switch (
switch_id serial primary key,
device_id integer references device(id),
nports integer not null,
model text,
management_port_name text,
constraint switch_management_port
foreign key (device_id, management_port_name)
references port(device_id, port_name)
);
Run Code Online (Sandbox Code Playgroud)
等等。
交换机是一个网络设备,端口通过设备表与它相关联。
所有这一切都有些摇摆不定,因为我不确切知道您需要建模什么,但应该可以帮助您开始以一种让您再次动起来的方式思考它。
学会爱加入。
要对设备之间的连接建模,您可能需要使用邻接表将其建模为无向循环图。这听起来很毛骨悚然,但实际上并非如此,它只是一个简单的表格,上面写着“这个与这个相连”,而无需费心说出哪个是“从”,哪个是“到”,将它们视为对称。
有很多关于从 SQL 查询邻接列表的既定文献。如果您想有效地执行递归公用表表达式 ( WITH RECURSIVE),您可能希望对递归公用表表达式 ( )变得友好,但对于简单的情况,您可以在 C 中执行循环,或者获取邻接列表的相关子集并使用boost::graph.
例如
CREATE TABLE socket_connection (
first_device_id integer references device(id),
first_socket_index integer,
second_device_id integer references device(id),
second_socket_index integer,
PRIMARY KEY (first_device_id, first_socket_index,
second_device_id, second_socket_index),
UNIQUE(first_device_id, first_socket_index),
UNIQUE(second_device_id, second_socket_index),
FOREIGN KEY (first_device_id, first_socket_index)
REFERENCES socket(device_id, socket_index),
FOREIGN KEY (second_device_id, second_socket_index)
REFERENCES socket(device_id, socket_index),
-- A simple way to prevent both (1, 1, 2, 1) and (2, 1, 1, 1)
-- being stored as separate connections:
CONSTRAINT first_device_id_is_always_lowest
CHECK (first_device_id < second_device_id)
);
Run Code Online (Sandbox Code Playgroud)
...这看起来很可怕,但实际上只是两个套接字之间的映射加上大量的健全性检查。
我在上面使用(device_id, socket_index)了很多复合键。
有些人喜欢他们,有些人不喜欢。它们的使用有优点也有缺点。如果您想在任何地方使用生成的代理键,那也没关系。
您可能会想“嘿,以前做过吗?”。
我很确定它有。现有模型是否符合您的需求尚不确定,但我建议您在从头开始创建自己的模型之前先查看其中的一些模型。
| 归档时间: |
|
| 查看次数: |
1032 次 |
| 最近记录: |