Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > MySQL数据表设计

一文详解MySQL数据表设计的五大黄金原则

作者:程序员大华

这篇文章主要为大家详细介绍了MySQL数据库设计数据表的五大黄金原则,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以了解下

新功能要上线,产品经理说要加个字段。你一看表结构,倒吸一口凉气——这表已经30多个字段了,而且好几个字段还是用逗号分隔存一堆值。改还是不改?这是个问题。

类似的问题还有很多,比如:

其实,这些问题很大程度上都可以在数据库设计阶段解决。

一、好设计 VS 坏设计

先看一个反面教材:

CREATE TABLE user (
    id INT PRIMARY KEY,
    name VARCHAR(255),
    phone VARCHAR(50),
    email VARCHAR(255),
    address VARCHAR(500),
    hobby VARCHAR(500),
    friend_list VARCHAR(1000),
    register_time DATETIME,
    last_login_time DATETIME,
    login_count INT,
    is_delete TINYINT
);

这个设计有什么问题?

这样的设计,项目初期可能没问题,但随着数据量增长,会带来无数麻烦。下面,我们就从几个核心原则开始,学习正确的设计方法。

二、数据库设计的五大黄金原则

1. 规范化设计

什么是规范化?

简单说,就是把相关数据拆分到不同的表中,避免数据冗余和异常。

第三范式(3NF)通俗解释:

每张表只描述一个主题,并且所有字段都必须直接依赖于主键。

例子:

错误设计:

CREATE TABLE order (
    order_id INT,
    customer_name VARCHAR(100),
    customer_phone VARCHAR(20),
    product_name VARCHAR(100),
    product_price DECIMAL(10,2),
    order_date DATETIME
);

正确设计:

CREATE TABLE customer (
    customer_id INT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    phone VARCHAR(20) NOT NULL
);

CREATE TABLE product (
    product_id INT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    price DECIMAL(10,2) NOT NULL
);

CREATE TABLE order (
    order_id INT PRIMARY KEY,
    customer_id INT NOT NULL,
    order_date DATETIME NOT NULL,
    total_amount DECIMAL(12,2) NOT NULL,
    FOREIGN KEY (customer_id) REFERENCES customer(customer_id)
);

CREATE TABLE order_item (
    item_id INT PRIMARY KEY,
    order_id INT NOT NULL,
    product_id INT NOT NULL,
    quantity INT NOT NULL,
    price DECIMAL(10,2) NOT NULL,
    FOREIGN KEY (order_id) REFERENCES order(order_id),
    FOREIGN KEY (product_id) REFERENCES product(product_id)
);

为什么这样做更好?

2. 命名规范:让人一眼看懂

好的命名就像一本说明书,让团队协作更顺畅。遵循以下规则:

反例:

正例:

3. 字段类型选择:精准匹配数据

选择合适的数据类型不仅节省空间,还能提高查询效率。下面是一些常见场景的最佳实践:

数据类型适用场景示例避免的错误
TINYINT(1)布尔值(是/否)is_active TINYINT(1) DEFAULT 1用VARCHAR存"yes"/"no"
INT一般ID、数量user_id INT, view_count INT用BIGINT存小范围数据
BIGINT大型系统ID、高并发计数器order_id BIGINT小型应用过度使用
VARCHAR(N)长度可变的字符串,N应合理设置name VARCHAR(50)所有字符串都用VARCHAR(255)
CHAR(N)固定长度字符串country_code CHAR(2)用CHAR存变长内容
DECIMAL(M,D)金额、精确小数price DECIMAL(10,2)用FLOAT/DOUBLE存金额
DATETIME需要日期和时间created_at DATETIME用字符串存时间
TIMESTAMP需要自动更新的时间戳updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP混淆DATETIME和TIMESTAMP
ENUM固定选项(谨慎使用)status ENUM('pending','paid','shipped')选项经常变化的场景
JSON确实需要存储结构化但不常查询的数据config JSON代替关系型设计

重点提醒: 金额一定要用DECIMAL,而不是FLOAT/DOUBLE!后者在计算时可能有精度丢失问题。

4. 索引设计:加速查询

索引就像书的目录,能让数据库快速定位数据。但索引不是越多越好,每个索引都会增加写入开销。

基本原则:

常见场景:

-- 用户经常按用户名和邮箱搜索
CREATE INDEX idx_user_name ON users(name);
CREATE INDEX idx_user_email ON users(email);

-- 订单表经常按用户ID和创建时间查询
CREATE INDEX idx_order_user_time ON orders(user_id, created_at);

-- 商品表经常按分类和价格排序
CREATE INDEX idx_product_category_price ON products(category_id, price);

索引使用注意事项:

5. 软删除 和 硬删除:数据安全策略

硬删除: 直接从数据库移除记录

DELETE FROM users WHERE id = 1001;

软删除: 标记记录为已删除,实际数据保留在数据库中

ALTER TABLE users ADD COLUMN is_deleted TINYINT(1) DEFAULT 0;
ALTER TABLE users ADD COLUMN deleted_at DATETIME DEFAULT NULL;

-- "删除"操作
UPDATE users SET is_deleted = 1, deleted_at = NOW() WHERE id = 1001;

软删除的优势:

什么情况下用软删除?

什么情况下用硬删除?

三、高级技巧:为未来做准备

1. 预留扩展字段

为未来可能的需求变化预留一些通用字段:

ALTER TABLE users 
ADD COLUMN ext_info JSON COMMENT '扩展信息,存储不常用字段',
ADD COLUMN version INT DEFAULT 1 COMMENT '乐观锁版本号';

注意: 不要过度使用扩展字段,它只适用于少量不常用且无需查询的属性。

2. 分库分表的前期准备

即使初期不需要分库分表,也可以提前做些准备:

3. 事务边界设计

在设计表结构时,就要考虑事务边界:

-- 转账操作需要保证原子性
UPDATE accounts SET balance = balance - 100 WHERE user_id = 1001;
UPDATE accounts SET balance = balance + 100 WHERE user_id = 1002;

良好的表设计应该让一个业务操作尽可能在一个事务内完成,避免分布式事务。

四、评论系统的设计

假设我们要设计一个文章评论系统,支持多级评论(评论可以回复评论),应该如何设计?

需求分析:

设计思路:

实体分析:用户、文章、评论、点赞

关系分析

表结构设计

CREATE TABLE articles (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    title VARCHAR(255) NOT NULL,
    content TEXT NOT NULL,
    user_id BIGINT NOT NULL,
    comment_count INT DEFAULT 0 COMMENT '评论数量,冗余字段提高查询效率',
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    is_deleted TINYINT(1) DEFAULT 0
) COMMENT='文章表';

CREATE TABLE comments (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    article_id BIGINT NOT NULL COMMENT '所属文章ID',
    user_id BIGINT NOT NULL COMMENT '评论用户ID',
    parent_id BIGINT DEFAULT 0 COMMENT '父评论ID,0表示一级评论',
    content VARCHAR(1000) NOT NULL COMMENT '评论内容',
    like_count INT DEFAULT 0 COMMENT '点赞数量',
    depth TINYINT DEFAULT 1 COMMENT '评论深度,1表示一级评论',
    path VARCHAR(255) DEFAULT '' COMMENT '路径,格式: 0,10,25 表示层级关系',
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    is_deleted TINYINT(1) DEFAULT 0,
    KEY idx_article (article_id, created_at),
    KEY idx_parent (parent_id),
    FOREIGN KEY (article_id) REFERENCES articles(id)
) COMMENT='评论表';

CREATE TABLE comment_likes (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    comment_id BIGINT NOT NULL,
    user_id BIGINT NOT NULL,
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    UNIQUE KEY uniq_comment_user (comment_id, user_id),
    FOREIGN KEY (comment_id) REFERENCES comments(id)
) COMMENT='评论点赞表';

设计说明:

查询所有一级评论及直接回复:

SELECT c1.*, c2.* 
FROM comments c1
LEFT JOIN comments c2 ON c2.parent_id = c1.id AND c2.depth = 2
WHERE c1.article_id = 100 AND c1.depth = 1 AND c1.is_deleted = 0
ORDER BY c1.created_at DESC, c2.created_at ASC;

这种设计既支持高效查询,又能适应业务变化,是经过实战检验的可靠方案。

五、工具推荐:提高设计效率

数据库设计工具

SQL规范检查

版本管理

六、总结

以上就是一文详解MySQL数据表设计的五大黄金原则的详细内容,更多关于MySQL数据表设计的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文