数据库表结构设计:设备信息表、设备分组表、设备认证日志表、策略表
这一章我们聊聊数据库表结构。说实话,很多物联网项目死在第一步——表没设计好。我见过一个项目,设备信息表里塞了三十多个字段,查询慢得像蜗牛。后来重构时发现,一半字段根本用不上。
设备注册与身份管理系统,核心就四张表:设备信息表、设备分组表、设备认证日志表、策略表。它们之间的关系,我画了张图,你先感受一下。
设备信息表:一切的基础
设备信息表是整个系统的基石。每台设备注册时,都会在这里生成一条记录。我个人习惯把核心字段控制在10个以内,太多了反而难维护。
先看表结构:
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | BIGINT UNSIGNED | 自增主键 |
| device_id | VARCHAR(64) | 设备唯一标识,全局唯一 |
| product_key | VARCHAR(32) | 产品型号标识 |
| device_name | VARCHAR(128) | 设备名称,可读性字段 |
| device_secret | VARCHAR(256) | 设备密钥,BCrypt加密存储 |
| status | TINYINT | 0=未激活 1=在线 2=离线 3=禁用 |
| firmware_version | VARCHAR(32) | 当前固件版本 |
| last_online_time | DATETIME | 最后上线时间 |
| created_at | DATETIME | 注册时间 |
| updated_at | DATETIME | 更新时间 |
设计要点:
- device_id 用 VARCHAR 而不是 INT。为什么?设备标识可能包含字母和符号,比如 "SN-2024-A001" 这种格式很常见
- device_secret 绝对不能明文存储。我见过有人直接存明文,结果数据库泄露后所有设备都被控制了
- status 用 TINYINT 而不是 ENUM。扩展性更好,以后加个 "维护中" 状态不用改表结构
建表脚本如下:
CREATE TABLE device_info (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
device_id VARCHAR(64) NOT NULL COMMENT '设备唯一标识',
product_key VARCHAR(32) NOT NULL COMMENT '产品型号标识',
device_name VARCHAR(128) DEFAULT '' COMMENT '设备名称',
device_secret VARCHAR(256) NOT NULL COMMENT '设备密钥(BCrypt)',
status TINYINT DEFAULT 0 COMMENT '0未激活 1在线 2离线 3禁用',
firmware_version VARCHAR(32) DEFAULT '' COMMENT '固件版本',
last_online_time DATETIME DEFAULT NULL COMMENT '最后上线时间',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_device_id (device_id),
INDEX idx_product_key (product_key),
INDEX idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='设备信息表';
注意:device_id 一定要建唯一索引。我曾经遇到过一个bug,因为没加唯一约束,同一台设备注册了两次,导致认证时出现冲突。排查了整整一天才发现是数据重复了。
设备分组表:树形结构怎么设计
设备分组是个经典问题。你想想看,一个工厂可能有几千台设备,不分组根本没法管理。分组表的核心是支持树形结构,也就是分组下面还能有子分组。
我推荐用 parent_id 方式实现树形,简单直接:
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | BIGINT UNSIGNED | 自增主键 |
| group_id | VARCHAR(32) | 分组唯一标识 |
| group_name | VARCHAR(64) | 分组名称 |
| parent_id | VARCHAR(32) | 父分组ID,根节点为NULL |
| description | VARCHAR(256) | 分组描述 |
| sort_order | INT | 排序序号 |
| created_at | DATETIME | 创建时间 |
建表脚本:
CREATE TABLE device_group (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
group_id VARCHAR(32) NOT NULL COMMENT '分组唯一标识',
group_name VARCHAR(64) NOT NULL COMMENT '分组名称',
parent_id VARCHAR(32) DEFAULT NULL COMMENT '父分组ID',
description VARCHAR(256) DEFAULT '' COMMENT '分组描述',
sort_order INT DEFAULT 0 COMMENT '排序序号',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_group_id (group_id),
INDEX idx_parent_id (parent_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='设备分组表';
避坑指南:parent_id 允许为 NULL,表示根节点。查询时用递归CTE(MySQL 8.0+)或者业务层递归。我建议如果分组层级不超过3层,业务层递归就够了,SQL写起来简单,也容易维护。
设备和分组是多对多关系,所以还需要一张关联表:
CREATE TABLE device_group_rel (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
device_id VARCHAR(64) NOT NULL COMMENT '设备ID',
group_id VARCHAR(32) NOT NULL COMMENT '分组ID',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_device_group (device_id, group_id),
INDEX idx_group_id (group_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='设备分组关联表';
设备认证日志表:审计的命脉
认证日志表,说白了就是记录设备每次登录的痕迹。安全审计、故障排查都靠它。我见过一个项目,设备出问题了查不到日志,最后只能一台一台去现场看——太痛苦了。
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | BIGINT UNSIGNED | 自增主键 |
| log_id | VARCHAR(32) | 日志唯一ID,UUID格式 |
| device_id | VARCHAR(64) | 设备ID |
| auth_type | TINYINT | 1=密钥认证 2=证书认证 3=Token认证 |
| auth_result | TINYINT | 0=失败 1=成功 |
| fail_reason | VARCHAR(128) | 失败原因,成功时为NULL |
| client_ip | VARCHAR(45) | 客户端IP,支持IPv6 |
| auth_time | DATETIME | 认证时间 |
建表脚本:
CREATE TABLE device_auth_log (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
log_id VARCHAR(32) NOT NULL COMMENT '日志唯一ID',
device_id VARCHAR(64) NOT NULL COMMENT '设备ID',
auth_type TINYINT NOT NULL COMMENT '1密钥 2证书 3Token',
auth_result TINYINT NOT NULL COMMENT '0失败 1成功',
fail_reason VARCHAR(128) DEFAULT NULL COMMENT '失败原因',
client_ip VARCHAR(45) DEFAULT '' COMMENT '客户端IP',
auth_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '认证时间',
UNIQUE KEY uk_log_id (log_id),
INDEX idx_device_id (device_id),
INDEX idx_auth_time (auth_time),
INDEX idx_auth_result (auth_result)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='设备认证日志表';
性能提醒:日志表的数据量增长很快。一台设备每小时认证一次,1000台设备一年就是876万条记录。建议按月份做分区,或者定期归档。我习惯用 auth_time 做 RANGE 分区,查询最近一个月的日志时效率很高。
策略表:灵活的权限控制
策略表是系统的"大脑"。它决定了设备能做什么、不能做什么。策略可以绑定到单个设备,也可以绑定到整个分组。这样设计的好处是——灵活。
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | BIGINT UNSIGNED | 自增主键 |
| policy_id | VARCHAR(32) | 策略唯一标识 |
| policy_name | VARCHAR(64) | 策略名称 |
| target_type | TINYINT | 1=设备 2=分组 |
| target_id | VARCHAR(64) | 设备ID或分组ID |
| policy_content | JSON | 策略内容,JSON格式 |
| effect | TINYINT | 0=拒绝 1=允许 |
| priority | INT | 优先级,数字越小优先级越高 |
| enabled | TINYINT | 0=禁用 1=启用 |
| created_at | DATETIME | 创建时间 |
| updated_at | DATETIME | 更新时间 |
建表脚本:
CREATE TABLE policy (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
policy_id VARCHAR(32) NOT NULL COMMENT '策略唯一标识',
policy_name VARCHAR(64) NOT NULL COMMENT '策略名称',
target_type TINYINT NOT NULL COMMENT '1设备 2分组',
target_id VARCHAR(64) NOT NULL COMMENT '目标ID',
policy_content JSON NOT NULL COMMENT '策略内容',
effect TINYINT NOT NULL DEFAULT 1 COMMENT '0拒绝 1允许',
priority INT DEFAULT 100 COMMENT '优先级',
enabled TINYINT DEFAULT 1 COMMENT '0禁用 1启用',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_policy_id (policy_id),
INDEX idx_target (target_type, target_id),
INDEX idx_enabled (enabled)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='策略表';
关于JSON字段:MySQL 5.7+ 支持 JSON 类型,可以直接在数据库里做 JSON 查询。比如 policy_content 里存了 {"actions": ["read", "write"], "topics": ["/data/#"]},查询时可以用 JSON_CONTAINS 函数。不过要注意,JSON 字段不适合做频繁更新的场景。
四张表的关系总结
嗯,到这里四张表都讲完了。我最后总结一下它们之间的关系:
- 设备信息表是核心,其他表都直接或间接引用它
- 设备分组表通过关联表与设备建立多对多关系
- 设备认证日志表记录设备的行为,用于审计和排查
- 策略表通过 target_type + target_id 灵活绑定到设备或分组