Tigase 是基於 XMPP 協議的開源即時通訊服務器,其數據庫設計採用極度泛化的實體-屬性-值(EAV)模型。本文解析三大核心表結構、層次關系、XML 數據存儲格式,並說明 roster、離線消息、隱私列表等業務映射邏輯。
CREATE TABLE `tig_users` (
`uid` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
`user_id` VARCHAR(255) NOT NULL, -- 用戶名(JID 本地部分)
`user_pw` VARCHAR(255) NOT NULL, -- 密碼雜湊
`last_login` BIGINT DEFAULT 0,
`last_logout` BIGINT DEFAULT 0,
`online_status` INT DEFAULT 0,
UNIQUE KEY `uk_user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

CREATE TABLE `tig_nodes` (
`nid` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
`parent_nid` BIGINT UNSIGNED DEFAULT 0, -- 父節點
`uid` BIGINT UNSIGNED NOT NULL, -- 歸屬用戶
`node` VARCHAR(255) NOT NULL, -- 節點類型
`key` VARCHAR(255) DEFAULT NULL, -- 可選鍵
KEY `idx_uid` (`uid`),
KEY `idx_parent_nid` (`parent_nid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

每個用戶(uid)固定擁有 5 個根節點:
root:根節點,掛載 rosterprivacy:隱私列表父節點offline:離線消息父節點invisible:隱身狀態節點msgoffline:離線消息存儲節點(掛在 offline 下)層次關系:
root
└─ privacy
└─ invisible
└─ offline
└─ msgoffline
CREATE TABLE `tig_pairs` (
`uid` BIGINT UNSIGNED NOT NULL,
`nid` BIGINT UNSIGNED NOT NULL,
`pkey` VARCHAR(255) NOT NULL,
`pval` LONGTEXT, -- XML 格式存儲
PRIMARY KEY (`uid`, `nid`, `pkey`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

關鍵行示例(uid=14):
| pkey | pval (XML 片段) | 業務含義 |
|---|---|---|
| privacy-list | <list name="default">...</list> | 隱私列表(invisible 節點) |
| roster | <roster><item jid="user2@domain" subscription="both"/>...</roster> | 好友列表(root 節點) |
| messages | <message from="...">...</message> (3 條) | 離線消息(msgoffline 節點) |
-- 獲取用戶 roster
SELECT pval FROM tig_pairs
WHERE uid = (SELECT uid FROM tig_users WHERE user_id = 'user1')
AND pkey = 'roster';
-- 獲取離線消息
SELECT pval FROM tig_pairs
WHERE uid = 14 AND pkey = 'messages'
ORDER BY nid; -- 按存儲順序
| 優點 | 缺點 |
|---|---|
| 極度靈活:新增業務屬性無需 ALTER TABLE | 查詢複雜:需多表 JOIN + XML 解析 |
| Schema-less:適應協議擴展 | 索引受限:pval 為 TEXT,無法高效索引內部字段 |
| 水平擴展友好:按 uid 分片 | 事務一致性難:跨節點操作需應用層協調 |
tigase-db/src/main/java/tigase/db/