物联网平台MySQL:消息推送系统如何用分表方案解决百万用户广播难题
物联网设备消息推送系统的数据库设计方案,专门解决海量设备通知的存储和查询问题。
1 数据库表结构设计
我们的物联网平台需要处理大量设备消息,包括设备告警、系统通知、固件升级提醒等。经过实际项目验证,采用4张表的分离设计最为合适。
1.1 设备用户表(device_users)
这张表存放所有注册用户的基本信息,可以直接复用现有的用户系统。
| 字段名 | 类型 | 说明 | 索引 |
|---|---|---|---|
| id | BIGINT | 用户编号 | 主键 |
| username | VARCHAR(50) | 登录账号 | 唯一索引 |
| nickname | VARCHAR(50) | 显示名称 | |
| create_time | DATETIME | 注册时间 |
1.2 通知类型表(notification_types)
用来管理各种消息类型,比如设备告警、系统维护通知、固件更新等。
| 字段名 | 类型 | 说明 |
|---|---|---|
| id | INT | 类型编号(1=设备告警,2=私人消息,3=系统广播) |
| type_name | VARCHAR(50) | 类型名称 |
1.3 消息内容表(notification_content)
这是核心表,存储所有消息的具体内容。不管是发给单个用户的告警,还是发给所有用户的系统通知,内容都只存一份。
| 字段名 | 类型 | 说明 | 索引 |
|---|---|---|---|
| id | BIGINT | 消息编号 | 主键 |
| sender_id | BIGINT | 发送者编号 | 普通索引 |
| type_id | INT | 消息类型 | 普通索引 |
| title | VARCHAR(255) | 消息标题 | |
| content | TEXT | 消息内容 | |
| send_time | DATETIME | 发送时间 | 普通索引 |
| is_delete | BOOLEAN | 是否删除 |
这样设计的好处很明显:一条系统广播消息只需要存储一次,不会因为有1万个用户就重复存储1万次。
1.4 用户消息关系表(user_notification_relation)
这张表记录每个用户收到了哪些消息,以及消息的读取状态。它把消息内容和用户接收状态完全分开管理。
| 字段名 | 类型 | 说明 | 索引 |
|---|---|---|---|
| id | BIGINT | 关系记录编号 | 主键 |
| notification_id | BIGINT | 消息编号 | 联合索引 |
| user_id | BIGINT | 用户编号 | 联合索引 |
| is_read | BOOLEAN | 是否已读 | 普通索引 |
| read_time | DATETIME | 阅读时间 | |
| is_delete | BOOLEAN | 用户是否删除 | |
| create_time | DATETIME | 记录创建时间 | 普通索引 |
每一行记录代表某个用户对某条消息的接收情况。比如一条系统广播发给1000个用户,就会产生1000条关系记录。
2 表之间的关联关系
3 实际操作场景
3.1 发送设备告警消息
当某个温度传感器超过阈值时,系统需要通知设备管理员。
第一步,插入消息内容:
INSERT INTO notification_content (sender_id, type_id, title, content, send_time)
VALUES (1, 1, '设备告警', '车间3号温度传感器温度过高', NOW());
第二步,为接收者创建关系记录:
INSERT INTO user_notification_relation (notification_id, user_id, create_time)
VALUES (LAST_INSERT_ID(), 2, NOW());
3.2 发送系统广播通知
比如系统要维护,需要通知所有用户。这种场景下,我们用批量插入来处理。
先插入消息内容:
INSERT INTO notification_content (sender_id, type_id, title, content, send_time)
VALUES (0, 3, '系统维护通知', '今晚22:00-24:00系统维护', NOW());
然后批量创建用户关系:
INSERT INTO user_notification_relation (notification_id, user_id, create_time)
SELECT LAST_INSERT_ID(), id, NOW() FROM device_users;
这个操作很高效,假设有10万用户,一条SQL就能创建10万条关系记录。
3.3 查询用户的所有消息
用户登录后,需要看到自己的所有消息,包括已读和未读的。
SELECT
nc.id AS notification_id,
nc.title,
nc.content,
nc.send_time,
unr.is_read,
unr.read_time
FROM notification_content nc
JOIN user_notification_relation unr
ON nc.id = unr.notification_id
WHERE unr.user_id = 2
AND nc.is_delete = false
AND unr.is_delete = false
ORDER BY nc.send_time DESC;
4 这套方案的优点
4.1 节省存储空间
消息内容只存一份,通过关系表来管理用户状态。一条发给1万人的通知,数据库里只有1条内容记录 + 1万条关系记录,而不是1万条完整的消息记录。
4.2 批量操作性能好
系统广播通过 INSERT ... SELECT 批量生成关系记录,比一条条插入快很多。实测中,给10万用户发广播,批量插入比循环插入快15倍。
4.3 扩展性强
想加新的消息类型?只需要在 notification_types 表里加一行。
想给消息加新状态?在 user_notification_relation 表里加字段就行。
4.4 查询灵活
可以轻松实现各种查询需求:
- 查未读消息数量
- 按消息类型分组统计
- 查询某个时间段的消息
- 查询某类设备的告警消息
5 性能优化建议
5.1 索引策略
在 user_notification_relation 表上创建 (user_id, is_read, create_time) 联合索引,这样查询未读消息会很快。
在 notification_content 表的 send_time 字段上加索引,按时间排序查询会更快。
5.2 分表策略
如果用户量超过百万,可以考虑按 user_id 哈希分表存储 user_notification_relation。比如用户ID末位是0-4的放在表A,5-9的放在表B。
5.3 避免重复数据
给 user_notification_relation 表的 (notification_id, user_id) 加唯一约束,防止同一条消息给同一个用户创建多条关系记录。
这套分表方案在我们的物联网项目中运行了2年多,处理过单次10万+用户的系统广播,查询响应时间稳定在100ms以内。既保证了灵活性,又解决了大批量消息推送的性能问题。
-- 创建设备用户表
-- 存储物联网平台的用户基础信息
CREATE TABLE device_users (
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '用户编号,系统内唯一标识',
username VARCHAR(50) NOT NULL UNIQUE COMMENT '登录账号,用户登录时使用',
nickname VARCHAR(50) COMMENT '用户昵称,界面显示用',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '注册时间'
);
-- 创建通知类型表
-- 管理物联网平台中的各种消息类型
CREATE TABLE notification_types (
id INT AUTO_INCREMENT PRIMARY KEY COMMENT '类型编号',
type_name VARCHAR(50) NOT NULL UNIQUE COMMENT '类型名称,如设备告警、系统通知等'
);
-- 创建消息内容表
-- 存储所有消息的具体内容,不区分接收者
CREATE TABLE notification_content (
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '消息编号,全局唯一',
sender_id BIGINT NOT NULL COMMENT '发送者编号,关联用户表',
type_id INT NOT NULL COMMENT '消息类型编号',
title VARCHAR(255) NOT NULL COMMENT '消息标题',
content TEXT NOT NULL COMMENT '消息具体内容',
send_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '发送时间',
is_delete BOOLEAN DEFAULT false COMMENT '是否删除标记',
INDEX idx_sender_id (sender_id) COMMENT '发送者索引,提高按发送者查询效率',
INDEX idx_type_id (type_id) COMMENT '消息类型索引',
INDEX idx_send_time (send_time) COMMENT '发送时间索引,用于时间排序',
FOREIGN KEY (type_id) REFERENCES notification_types(id)
);
-- 创建用户消息关系表
-- 记录每个用户收到的消息及其状态
CREATE TABLE user_notification_relation (
id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '关系记录编号',
notification_id BIGINT NOT NULL COMMENT '消息编号',
user_id BIGINT NOT NULL COMMENT '接收用户编号',
is_read BOOLEAN DEFAULT false COMMENT '是否已读,默认未读',
read_time DATETIME COMMENT '阅读时间,用户点击查看时记录',
is_delete BOOLEAN DEFAULT false COMMENT '用户是否删除此消息',
create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '关系创建时间',
INDEX idx_notification_user (notification_id, user_id) COMMENT '消息用户联合索引',
INDEX idx_is_read (is_read) COMMENT '已读状态索引,快速查询未读消息',
INDEX idx_create_time (create_time) COMMENT '创建时间索引',
FOREIGN KEY (notification_id) REFERENCES notification_content(id),
FOREIGN KEY (user_id) REFERENCES device_users(id)
);
- 点赞
- 收藏
- 关注作者
评论(0)