对于不定列的关系型数据库设计思路

引言

需求:每分钟统计系统信息,包含CPU、内存、网卡、硬盘信息,对于CPU和内存来说,性能指标就那几个,然而对于网卡和硬盘,网卡可能有多个,硬盘可能分区好多种,这样的话,字段设计起来是一件很头痛的事情。
image.png

思路一(⭐)

设计成k-v模式,也就是说键值对模式,表格设计如下

时间(bigint) 类别(varchar) 组(varchar) 名称(varchar) 值(varchar)
20240111171329 CPU信息 null 系统使用率 10.3%
20240111171329 CPU信息 null 用户使用率 13.6%
20240111171329 CPU信息 null 空闲率 76.1%
20240111171329 内存信息 null 接口使用数 2
20240111171329 内存信息 null 内存使用容量 173361.0
20240111171329 内存信息 null 内存剩余容量 86596.0
... ... ... ... ...
20240111171329 网卡信息 上传速度 86596.0
20240111171329 网卡信息 Intel(R) Wi-Fi 6 AX201 160MHz 上传速率 28kb/s
20240111171329 网卡信息 Intel(R) Wi-Fi 6 AX201 160MHz 下载速率度 89kb/s
20240111171329 网卡信息 Intel(R) Wi-Fi 6 AX201 160MHz 接口名称 Intel(R) Wi-Fi 6 AX201 160MHz
20240111171329 网卡信息 Intel(R) Wi-Fi 6 AX201 160MHz IP地址 172.30.8.134
20240111171329 网卡信息 Killer E2600 Gigabit Ethernet Controller 上传速率 0kb/s
20240111171329 网卡信息 Killer E2600 Gigabit Ethernet Controller 下载速率 0kb/s
20240111171329 网卡信息 Killer E2600 Gigabit Ethernet Controller 接口名称 Killer E2600 Gigabit Ethernet Controller
20240111171329 网卡信息 Killer E2600 Gigabit Ethernet Controller IP地址 172.30.8.134
... ... ... ... ...

缺点:对于计算和后续的数据处理稍微有些难度

思路二

定义多张表(防止字段超出范围)和自定义字段,在程序中检查字段,如果不存在,则新增字段

SELECT * FROM information_schema.columns where TABLE_SCHEMA='csm' AND TABLE_NAME = 'system_info' -- 查询表格有哪些字段

ALTER TABLE `csm`.`system_info` 
ADD COLUMN `Intel(R) Wi-Fi 6 AX201 160MHz upload` varchar(255) NULL AFTER `value`; -- 添加字段

缺点:程序代码复杂,数据量大的表处理起来比较耗时

思路三

使用JSON字段存储结果

数据时间 JSON值
20240111172625 {"CPU系统使用率":"10.3","CPU用户使用率":"13.6","CPU空闲率":"76.1","Intel(R) Wi-Fi 6 AX201 160MHz 上传速率":"23.45kb/s","Intel(R) Wi-Fi 6 AX201 160MHz 下载速率":"21.45kb.s","...":"..."}
20240111172725 {"CPU系统使用率":"10.3","CPU用户使用率":"13.6","CPU空闲率":"76.1","Intel(R) Wi-Fi 6 AX201 160MHz 上传速率":"23.45kb/s","Intel(R) Wi-Fi 6 AX201 160MHz 下载速率":"21.45kb.s","...":"..."}

缺点:数据不直观,查询效率低

MySQL的JSON操作

CREATE TABLE `json_test` (
  `id` int NOT NULL,
  `content` json DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
id content
1 {"a": "a", "b": 4}
2 {"a": "b", "b": 44}
3 {"a": "c", "b": 333}
SELECT * FROM test.json_test where content->'$.a' = 'b'
SELECT * FROM test.json_test where content->'$.b' > 15

针对于思路一的Java处理

0x01 定义数据库 entity

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class InfoEntity {
    private Long report_id;
    private String category;
    private String group;
    private String name;
    private String value;
}

0x02 创建结果集列表

List<InfoEntity> result = new ArrayList<>();

0x03 把entity转成HashMap

JSONObject.parseObject(JSONObject.toJSONString(cpuInfo))  // fastjson

0x04 遍历map的entrySet

for (HashMap<String, Object> item : data) {
...
}

0x05 把遍历的实体set到实体对象

result.add(new InfoEntity(dateid, category, group, key, value));

0x06 mybatis生成sql

<insert id="appendRecords" parameterType="java.util.ArrayList">
        INSERT INTO system_info(`report_id`, `category`, `group`, `name`, `value`) values
        <foreach collection="list" item="item" index="index"  separator=",">
            (${item.report_id}, #{item.category}, #{item.group}, #{item.name}, #{item.value} )
        </foreach>
    </insert>

结语

不同思路都有各自的优势和劣势,只要能完美的处理业务数据,那就是好的解决方案。

评论 1

  1. 字节跳动
    不愧是你
提交后需要审核才会公开。