数据建模实战
问题
电商、社交、内容平台等常见系统的数据模型如何设计?
答案
电商核心模型
ecommerce.sql
-- 商品 SKU(规格)
CREATE TABLE skus (
id SERIAL PRIMARY KEY,
product_id INT REFERENCES products(id),
spec JSONB NOT NULL, -- {"颜色": "黑色", "内存": "256GB"}
price DECIMAL(10,2) NOT NULL,
stock INT NOT NULL DEFAULT 0,
UNIQUE (product_id, spec)
);
-- 订单(快照设计:下单时记录商品信息,不受后续修改影响)
CREATE TABLE order_items (
id SERIAL PRIMARY KEY,
order_id INT REFERENCES orders(id),
sku_id INT REFERENCES skus(id),
product_name VARCHAR(200), -- 快照
product_price DECIMAL(10,2), -- 快照
spec JSONB, -- 快照
quantity INT NOT NULL
);
社交平台模型
social.sql
-- 关注关系(多对多)
CREATE TABLE follows (
follower_id INT REFERENCES users(id),
following_id INT REFERENCES users(id),
created_at TIMESTAMP DEFAULT NOW(),
PRIMARY KEY (follower_id, following_id)
);
-- 动态/帖子
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
user_id INT REFERENCES users(id),
content TEXT,
media JSONB, -- [{"type": "image", "url": "..."}]
likes_count INT DEFAULT 0, -- 冗余计数(高频读)
created_at TIMESTAMP DEFAULT NOW()
);
-- 点赞(防重复)
CREATE TABLE likes (
user_id INT REFERENCES users(id),
post_id INT REFERENCES posts(id),
PRIMARY KEY (user_id, post_id)
);
内容平台模型
cms.sql
-- 标签系统(多态)
CREATE TABLE tags (
id SERIAL PRIMARY KEY,
name VARCHAR(50) UNIQUE NOT NULL
);
CREATE TABLE taggables (
tag_id INT REFERENCES tags(id),
taggable_type VARCHAR(50), -- 'article', 'video', 'course'
taggable_id INT,
PRIMARY KEY (tag_id, taggable_type, taggable_id)
);
-- 评论(树形结构)
CREATE TABLE comments (
id SERIAL PRIMARY KEY,
user_id INT REFERENCES users(id),
post_id INT REFERENCES posts(id),
parent_id INT REFERENCES comments(id), -- 父评论
content TEXT NOT NULL,
path VARCHAR(500), -- 物化路径 '/1/3/7'
created_at TIMESTAMP DEFAULT NOW()
);
常见面试问题
Q1: 树形评论怎么实现?
答案:
| 方案 | 写入 | 查询 | 适用 |
|---|---|---|---|
| 邻接表(parent_id) | 递归查询 | 层级浅 | |
| 路径枚举(path) | LIKE 前缀 | 层级深 | |
| 闭包表 | 查询频繁 |
推荐:邻接表 + 路径枚举组合使用。
Q2: 为什么订单要做快照?
答案:
商品信息(名称、价格)可能随时修改,但历史订单必须保留下单时的数据。所以在 order_items 中冗余商品名和价格,形成快照。
Q3: 计数器怎么设计?
答案:
高频读:在主表冗余计数字段(likes_count),用 Redis 缓存。定期同步到数据库。避免每次 COUNT(*) 查询。