侠义的CMDB都是偏向纯资产管理,但运维系统往往围绕着这些资产中心,从资产进行不断外充扩容

在其基础之外扩展出各功能,通过cmdb 扩展出各个子系统 

涉及工具: workbench


一个例子:设计一个数据库实现主机信息、交换机信息,如何将之间的信息关联起来

初步的传统设计:

主机
交换机
user
资产编号
资产编号
部门
硬件配置信息
 IDC

使用人

机柜位置
 机柜位置
业务线
外连ip
外网IP

业务线
内网IP

备注
业务线


  备注

每种属性都是不同的,就算名称一样,那么字段大小是否考虑过?

到底多少个字段合适,拆表的方式的话,需要拆多少张表?

传统设计来讲,肯定是一个资产一个表,那么关系型数据库多少张合适?

如果真是这样的情况,才能引出CMDB


可以认为不同型号的产品都是一个资产,那么不同产品放在一张表中肯定不现实

每个场景一张表,那么如果新加场景如何考虑?增加字段?增加多少字段?

所以我们要引入一种方式进行实现这些信息,不同信息是不一样的,比如id信息是一种信息,主机名等也是一种信息,即使分析的再好,一旦广泛使用起来,那么需求也陆续膨胀

这样的设计带来很大的问题:字段无法控制,扩展性不够,大量的字段冗余,看似名字差不多,名字也差不多



引入CMDB

初期版本设计太繁琐,需要用到的表关联无计其数,所以需要引入一种新的设计方式来进行,通过字段表进行关联开


虚拟表设计


先建立两个表,Schema 和Filed 表

9921148.png

Schema 用于存放所有表

Filed  用于存放各种表的字段,这样大多数的字段都可以撑得住


设计Schema

10343256.png

这里存放着表信息,但是不存放字段

那么这里的意思肯定是一行对应一个表,相当于Schema 一个id对应一个表

不要看字段,只看行

比如主机表:id=1 name=host  desc="描述主机信息"

比如交换机:id=2 name=sw desc="描述交换机信息"


那么它的具体字段是存放在Filed表

设计Filed表

12460158.png

# meta是元数据

从关联的表,逐渐以每一行来表述,每一行都代表一个字段

首先考虑是一对多还是多对多

要求一个字段只能对应某一个明确的表中,即使字段名称一样,但是类型不一样,类型即使一样其他属性也不一样,即使同一个字段,但是所谓的描述信息不一样,有的int 有的bigint 等等

即使看似像同一字段,可复用的功能性不是很强,但是实现可复用情况一定很高

所以采取简单化的设计:通过不同表的字段和其他表的字段没有任何关系

为了简化这样的关系,这里的meta描述是不一样的


那么这两个表如何建立关联?

一个filed id只能属于一张表,比如只能属于host表

多对一关系的建立

b12106b4-4927-4f8b-9911-9c467f9fe082.png


13186967.png



再来看一下filed表结构,它不但帮我们建了字段还添加了主键


查看外键约束

明确说明了外键是引用了schema 这个表,

13251036.png


而且用的是自己的id 来引用的schema id

image.png


理解这样的关系

假设:

schema 

id =1 

name = host 

meta = 信息为主机

filed

id = 1

name = ip 

meta = 先不管

schema_id BIGINT = 1  

再写一个信息

id = 2

name =hostname 

meta = 先不管

schema_id BIGINT =1


只要schema_id 是 1的 代表生成了一张host表

而这个host表有两个字段 1 和 2

1是描述这个ip的  2 是描述ip的

这就建立了一种关联关系


建立sw表

如果顺序往后排,那么schema id 就是2

如果序号如果对应的话,就是3 和 4,唯一代表一个字段

这种逻辑上的关系被称为虚拟表

再有一个资产来的话,再加一张表就可以了,无非就是加了一行

以上是一对多关系,在多端添加字段schema_id 解决




描述资产比较难,不同资产有不同的属性,比较难在一张表中设计固定个数

所以通过schema 和 filed 组合 在filed表中建立多个记录来描述,只要在filed表中增加一条记录描述即可

这样两者构建成了一个虚拟表,来描述逻辑上的表

  • schema   只描述表是谁

  • filed         只描述字段,动态添加

中间通过外键约束来构建成虚拟表,达到动态增加资产的需求


一个栗子



需求

建立一个业务 ipaddress表,对应schema_id 为 10,它有2个字段,字段的描述在filed中,

filed.id = 1, name 和字段 filed.id = 5,name  这两部分构成一张表的定义


分析

一个字段是一行记录在filed表中,可以跟其形成一张逻辑表,就是ipaddres表

ipaddress表对应的schema_id 为10

必须定义两个ipaddress,id肯定不一样的,但是描述不一样,这样肯定搞混,建议再加一个UQ 保证名字不重复

还需要根据提交的逻辑表名称,万一尾部多敲空格,那么是否需要检验空格



逻辑表设计


加入一个表entity,用于记录主机,一对多关系,一个主机的描述信息只属于一个

设计字段




idBIGINT唯一标识虚拟表的记录
keyVARCHAR用于存放UUID


image.png


添加value

idBIGINT自增
valueVARCHAR存什么?

16477122.png

现在的我们想的是将这些信息存放于value表中

image.png


这样就引出了两个字段:IP和HOSTNAME

那么回到entity中,既然是存值,那么是必须有id 唯一标识,新加字段

entity 与 value关联关系:

一对多关系,entity只有一个id,value中有多个字段,那么为了描述这一个id对应的一行记录,这两个字段都要放在value中

如何描述字段,如何知道是哪个字段?那么filed还需要与value建立关系

建立表关系

image.png


记录哪个虚拟表完全靠schema_id 来表示,比如schema_id = 1 那么就相当于找host表

假如描述这边的字段该如何去关联?

描述字段关系

这里value 对应的就是就是entity_id 的而entity 又是唯一对应了schema_id

虽然是多端,这样看只能对应一个,但是关联起来之后从schema表中就能够充分体现出是对应多个表id

只不过是中间做了一层过度,这样每个表只对应一个id即可。 但是不能描述对应的哪些字段,所需还需要与filed进行关联

image.png

这样后期加字段或者表的时候直接加一行记录即可

如果在使用的一张表在使用中,添加字段的话,物理结构也跟随发生改变,那如果是虚拟表在filed表中添加字段,那么就又生成了filed表的id来对应一些信息

那这样的话添加的代价会下降很多,假如字段描述为空的话原来的虚拟表生成的记录可以不动,如果不为空可以做一系列缺省值的设置


由filed表中的meta中进行定义字段 多类型

可以有约束也可以没有 代码中写明确即可,看实际场景,设计的时候先设计主表,将最关联的几张表列出,这几个表中又有其他描述性的信息在其他表里依次做join就可以了,其他信息再进行关联


生成工程



-- MySQL Workbench Forward Engineering
SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL,ALLOW_INVALID_DATES';
-- -----------------------------------------------------
-- Schema cmdb
-- -----------------------------------------------------
-- -----------------------------------------------------
-- Schema cmdb
-- -----------------------------------------------------
CREATE SCHEMA IF NOT EXISTS `cmdb` DEFAULT CHARACTER SET utf8 ;
USE `cmdb` ;
-- -----------------------------------------------------
-- Table `cmdb`.`schema`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `cmdb`.`schema` (
  `id` BIGINT NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(45) NOT NULL,
  `desc` VARCHAR(45) NULL,
  PRIMARY KEY (`id`))
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `cmdb`.`field`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `cmdb`.`field` (
  `id` BIGINT NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(45) NOT NULL,
  `meta` TEXT NULL,
  `schema_id` BIGINT NOT NULL,
  PRIMARY KEY (`id`),
  INDEX `fk_field_schema_idx` (`schema_id` ASC),
  CONSTRAINT `fk_field_schema`
    FOREIGN KEY (`schema_id`)
    REFERENCES `cmdb`.`schema` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `cmdb`.`entity`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `cmdb`.`entity` (
  `id` INT NOT NULL,
  `key` VARCHAR(45) NOT NULL COMMENT '唯一描述',
  `schema_id` BIGINT NOT NULL,
  PRIMARY KEY (`id`),
  INDEX `fk_entity_schema1_idx` (`schema_id` ASC),
  CONSTRAINT `fk_entity_schema1`
    FOREIGN KEY (`schema_id`)
    REFERENCES `cmdb`.`schema` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `cmdb`.`value`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `cmdb`.`value` (
  `id` BIGINT NOT NULL AUTO_INCREMENT,
  `value` VARCHAR(45) NOT NULL,
  `field_id` BIGINT NOT NULL,
  `entity_id` INT NOT NULL,
  PRIMARY KEY (`id`),
  INDEX `fk_value_field1_idx` (`field_id` ASC),
  INDEX `fk_value_entity1_idx` (`entity_id` ASC),
  CONSTRAINT `fk_value_field1`
    FOREIGN KEY (`field_id`)
    REFERENCES `cmdb`.`field` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_value_entity1`
    FOREIGN KEY (`entity_id`)
    REFERENCES `cmdb`.`entity` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB
COMMENT = '';
SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;