Appearance
数据库 Schema
IM 系统使用 SQLite 作为数据存储,通过 sql.js(WebAssembly)运行,无需安装额外数据库服务。
表结构总览
| 表名 | 说明 | 主键 |
|---|---|---|
| users | 用户信息 | id |
| groups | 群聊信息 | id |
| group_members | 群成员关系 | (group_id, user_id) |
| messages | 消息记录 | id |
| friends | 好友关系 | (user_id, friend_id) |
| friend_requests | 好友请求 | id |
| push_subscriptions | 推送订阅 | user_id |
| unread_counts | 未读计数 | (user_id, chat_id) |
| blocked_users | 黑名单 | (user_id, blocked_id) |
| config | 系统配置 | key |
users — 用户表
sql
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
username TEXT NOT NULL,
avatar TEXT,
password TEXT NOT NULL DEFAULT '',
nickname TEXT,
email TEXT,
created_at INTEGER,
updated_at INTEGER,
push_enabled INTEGER DEFAULT 1,
dnd_enabled INTEGER DEFAULT 0,
gender TEXT DEFAULT '',
bio TEXT DEFAULT '',
phone TEXT DEFAULT ''
);| 字段 | 类型 | 说明 |
|---|---|---|
| id | TEXT | 用户唯一 ID(UUID) |
| username | TEXT | 登录账号(唯一,2-32 字符) |
| avatar | TEXT | 头像 URL |
| password | TEXT | 密码(bcrypt 加密) |
| nickname | TEXT | 昵称(最长 64 字符) |
| TEXT | 邮箱(最长 128 字符) | |
| created_at | INTEGER | 创建时间戳(毫秒) |
| updated_at | INTEGER | 更新时间戳(毫秒) |
| push_enabled | INTEGER | 是否启用推送:0=关闭,1=开启 |
| dnd_enabled | INTEGER | 是否免打扰:0=关闭,1=开启 |
| gender | TEXT | 性别 |
| bio | TEXT | 个性签名(最长 500 字符) |
| phone | TEXT | 手机号(最长 20 字符) |
索引:idx_users_username ON users (username)
groups — 群聊表
sql
CREATE TABLE IF NOT EXISTS groups (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
creator_id TEXT NOT NULL,
create_time INTEGER NOT NULL,
announcement TEXT DEFAULT '',
avatar TEXT DEFAULT '',
description TEXT DEFAULT ''
);| 字段 | 类型 | 说明 |
|---|---|---|
| id | TEXT | 群唯一 ID |
| name | TEXT | 群名称(最长 64 字符) |
| creator_id | TEXT | 群主用户 ID |
| create_time | INTEGER | 创建时间戳(毫秒) |
| announcement | TEXT | 群公告(最长 2000 字符) |
| avatar | TEXT | 群头像 URL |
| description | TEXT | 群描述(最长 500 字符) |
group_members — 群成员表
sql
CREATE TABLE IF NOT EXISTS group_members (
group_id TEXT,
user_id TEXT,
join_time INTEGER NOT NULL,
role TEXT DEFAULT 'member',
is_muted INTEGER DEFAULT 0,
PRIMARY KEY (group_id, user_id)
);| 字段 | 类型 | 说明 |
|---|---|---|
| group_id | TEXT | 群 ID |
| user_id | TEXT | 用户 ID |
| join_time | INTEGER | 加入时间戳(毫秒) |
| role | TEXT | 角色:creator、admin、member |
| is_muted | INTEGER | 是否被禁言:0=否,1=是 |
索引:idx_group_members_user ON group_members (user_id)
messages — 消息表
sql
CREATE TABLE IF NOT EXISTS messages (
id TEXT PRIMARY KEY,
from_id TEXT NOT NULL,
to_id TEXT NOT NULL,
type TEXT NOT NULL,
content TEXT NOT NULL,
timestamp INTEGER NOT NULL,
content_type TEXT DEFAULT 'text',
reply_to TEXT DEFAULT '',
is_revoked INTEGER DEFAULT 0,
image_url TEXT DEFAULT ''
);| 字段 | 类型 | 说明 |
|---|---|---|
| id | TEXT | 消息唯一 ID(客户端生成的 UUID) |
| from_id | TEXT | 发送者用户 ID |
| to_id | TEXT | 接收者 ID(用户 ID 或群 ID) |
| type | TEXT | 消息类型:single(私聊)或 group(群聊) |
| content | TEXT | 消息内容(最长 10000 字符) |
| timestamp | INTEGER | 发送时间戳(毫秒) |
| content_type | TEXT | 内容类型:text、image |
| reply_to | TEXT | 回复的消息 ID(空字符串表示非回复) |
| is_revoked | INTEGER | 是否已撤回:0=否,1=是 |
| image_url | TEXT | 图片 URL(contentType 为 image 时使用) |
索引:
| 索引名 | 字段 | 用途 |
|---|---|---|
| idx_messages_from_to | (from_id, to_id) | 查询两人之间的消息 |
| idx_messages_to | (to_id) | 查询发给某人的消息 |
| idx_messages_timestamp | (timestamp) | 按时间排序 |
| idx_messages_to_ts | (to_id, timestamp) | 查询某会话的消息(按时间) |
| idx_messages_from_to_ts | (from_id, to_id, timestamp) | 精确查询两人消息(按时间) |
friends — 好友关系表
sql
CREATE TABLE IF NOT EXISTS friends (
user_id TEXT,
friend_id TEXT,
create_time INTEGER NOT NULL,
PRIMARY KEY (user_id, friend_id)
);双向存储
好友关系为双向存储:A 添加 B 为好友时,同时插入 (A, B) 和 (B, A) 两条记录。
friend_requests — 好友请求表
sql
CREATE TABLE IF NOT EXISTS friend_requests (
id TEXT PRIMARY KEY,
from_id TEXT NOT NULL,
to_id TEXT NOT NULL,
status TEXT NOT NULL,
create_time INTEGER NOT NULL
);| 字段 | 类型 | 说明 |
|---|---|---|
| id | TEXT | 请求唯一 ID |
| from_id | TEXT | 发起者用户 ID |
| to_id | TEXT | 接收者用户 ID |
| status | TEXT | 状态:pending(待处理)、accepted(已接受)、rejected(已拒绝) |
| create_time | INTEGER | 创建时间戳(毫秒) |
索引:
| 索引名 | 字段 |
|---|---|
| idx_friend_requests_to | (to_id, status) |
| idx_friend_requests_from | (from_id, status) |
push_subscriptions — 推送订阅表
sql
CREATE TABLE IF NOT EXISTS push_subscriptions (
user_id TEXT PRIMARY KEY,
subscription TEXT NOT NULL
);| 字段 | 类型 | 说明 |
|---|---|---|
| user_id | TEXT | 用户 ID(每个用户最多一条订阅) |
| subscription | TEXT | PushSubscription 对象的 JSON 字符串 |
unread_counts — 未读计数表
sql
CREATE TABLE IF NOT EXISTS unread_counts (
user_id TEXT NOT NULL,
chat_id TEXT NOT NULL,
chat_type TEXT NOT NULL,
count INTEGER DEFAULT 0,
PRIMARY KEY (user_id, chat_id)
);| 字段 | 类型 | 说明 |
|---|---|---|
| user_id | TEXT | 用户 ID |
| chat_id | TEXT | 聊天对象 ID(私聊为对方用户 ID,群聊为群 ID) |
| chat_type | TEXT | single 或 group |
| count | INTEGER | 未读消息数 |
索引:idx_unread_user ON unread_counts (user_id)
blocked_users — 黑名单表
sql
CREATE TABLE IF NOT EXISTS blocked_users (
user_id TEXT NOT NULL,
blocked_id TEXT NOT NULL,
create_time INTEGER NOT NULL,
PRIMARY KEY (user_id, blocked_id)
);config — 系统配置表
sql
CREATE TABLE IF NOT EXISTS config (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
);存储系统级配置项,如 single_sign_on 等。
数据库优化
WAL 模式
服务启动时自动开启 WAL(Write-Ahead Logging)模式,允许读写并发执行,显著提升并发性能。
防抖写入
写操作不是立即持久化到磁盘,而是先缓存在内存中,100ms 防抖后批量写入。服务关闭时执行 forceSave 确保数据完整。
索引策略
共 10 个索引,覆盖主要查询路径:
| 索引 | 表 | 优化场景 |
|---|---|---|
| idx_messages_from_to | messages | 查询两人聊天记录 |
| idx_messages_to | messages | 查询发给某人的消息 |
| idx_messages_timestamp | messages | 按时间排序 |
| idx_messages_to_ts | messages | 会话消息分页查询 |
| idx_messages_from_to_ts | messages | 精确两人消息分页 |
| idx_users_username | users | 按用户名查找 |
| idx_friend_requests_to | friend_requests | 查询收到的好友请求 |
| idx_friend_requests_from | friend_requests | 查询发出的好友请求 |
| idx_unread_user | unread_counts | 查询用户未读计数 |
| idx_group_members_user | group_members | 查询用户加入的群 |