侠义的CMDB都是偏向纯资产管理,但运维系统往往围绕着这些资产中心,从资产进行不断外充扩容
在其基础之外扩展出各功能,通过cmdb 扩展出各个子系统
涉及工具: workbench
一个例子:设计一个数据库实现主机信息、交换机信息,如何将之间的信息关联起来
初步的传统设计:
主机 | 交换机 | user |
资产编号 | 资产编号 | 部门 |
硬件配置信息 | IDC | 使用人 |
机柜位置 | 机柜位置 | 业务线 |
外连ip | 外网IP | |
业务线 | 内网IP | |
备注 | 业务线 | |
备注 |
每种属性都是不同的,就算名称一样,那么字段大小是否考虑过?
到底多少个字段合适,拆表的方式的话,需要拆多少张表?
传统设计来讲,肯定是一个资产一个表,那么关系型数据库多少张合适?
如果真是这样的情况,才能引出CMDB
可以认为不同型号的产品都是一个资产,那么不同产品放在一张表中肯定不现实
每个场景一张表,那么如果新加场景如何考虑?增加字段?增加多少字段?
所以我们要引入一种方式进行实现这些信息,不同信息是不一样的,比如id信息是一种信息,主机名等也是一种信息,即使分析的再好,一旦广泛使用起来,那么需求也陆续膨胀
这样的设计带来很大的问题:字段无法控制,扩展性不够,大量的字段冗余,看似名字差不多,名字也差不多
引入CMDB
初期版本设计太繁琐,需要用到的表关联无计其数,所以需要引入一种新的设计方式来进行,通过字段表进行关联开
虚拟表设计
先建立两个表,Schema 和Filed 表
Schema 用于存放所有表
Filed 用于存放各种表的字段,这样大多数的字段都可以撑得住
设计Schema
这里存放着表信息,但是不存放字段
那么这里的意思肯定是一行对应一个表,相当于Schema 一个id对应一个表
不要看字段,只看行
比如主机表:id=1 name=host desc="描述主机信息"
比如交换机:id=2 name=sw desc="描述交换机信息"
那么它的具体字段是存放在Filed表
设计Filed表
# meta是元数据
从关联的表,逐渐以每一行来表述,每一行都代表一个字段
首先考虑是一对多还是多对多
要求一个字段只能对应某一个明确的表中,即使字段名称一样,但是类型不一样,类型即使一样其他属性也不一样,即使同一个字段,但是所谓的描述信息不一样,有的int 有的bigint 等等
即使看似像同一字段,可复用的功能性不是很强,但是实现可复用情况一定很高
所以采取简单化的设计:通过不同表的字段和其他表的字段没有任何关系
为了简化这样的关系,这里的meta描述是不一样的
那么这两个表如何建立关联?
一个filed id只能属于一张表,比如只能属于host表
多对一关系的建立
再来看一下filed表结构,它不但帮我们建了字段还添加了主键
查看外键约束
明确说明了外键是引用了schema 这个表,
而且用的是自己的id 来引用的schema id
理解这样的关系
假设:
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,用于记录主机,一对多关系,一个主机的描述信息只属于一个
设计字段
id | BIGINT | 唯一标识虚拟表的记录 |
key | VARCHAR | 用于存放UUID |
添加value表
id | BIGINT | 自增 |
value | VARCHAR | 存什么? |
现在的我们想的是将这些信息存放于value表中
这样就引出了两个字段:IP和HOSTNAME
那么回到entity中,既然是存值,那么是必须有id 唯一标识,新加字段
entity 与 value关联关系:
一对多关系,entity只有一个id,value中有多个字段,那么为了描述这一个id对应的一行记录,这两个字段都要放在value中
如何描述字段,如何知道是哪个字段?那么filed还需要与value建立关系
建立表关系
记录哪个虚拟表完全靠schema_id 来表示,比如schema_id = 1 那么就相当于找host表
假如描述这边的字段该如何去关联?
描述字段关系
这里value 对应的就是就是entity_id 的而entity 又是唯一对应了schema_id
虽然是多端,这样看只能对应一个,但是关联起来之后从schema表中就能够充分体现出是对应多个表id
只不过是中间做了一层过度,这样每个表只对应一个id即可。 但是不能描述对应的哪些字段,所需还需要与filed进行关联
这样后期加字段或者表的时候直接加一行记录即可
如果在使用的一张表在使用中,添加字段的话,物理结构也跟随发生改变,那如果是虚拟表在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;