简介:这份《数据库设计文档.pdf》面向人资信息管理系统的开发、测试与数据库设计人员,用于统一后台数据库的概念模型与物理模型规范,并明确各表的数据字典结构,是编码与测试阶段的重要参考依据。文档围绕数据库环境说明、命名规则、逻辑设计与数据库实施四部分展开:环境部分采用SQL Server数据库管理系统,借助Visio绘制ER图并生成DDL脚本,登录采用混合身份验证;命名规则遵循三范式,库名与表名统一大写,表以RSH_开头加中文拼音缩写,如职工基本信息表RSH_ZHGJB。逻辑设计按面向对象思想由实体类生成数据表,实施部分基于SQL Server 2008 R2,库名DB_OA,包含SendMessage、ReadMessage、Role、RolePrivilege、Privilege、User、RecordBackUp、Plan、Company等表,并逐表给出功能说明与字段类型、长度、约束等细节。资源包为1个PDF文件,约296KB,结构紧凑便于查阅,已有673人学习下载,适合需要参考建库规范、表结构设计与数据字典编写的读者。
1. 数据库设计文档.pdf:一份能落地的设计文档到底该写什么
很多人第一次接手“数据库设计文档.pdf”这个交付物时,以为它就是把建表语句截图贴进 Word。真到联调阶段才发现,前端问字段能不能为空、后端问索引为什么没走、DBA 问这列为什么用 MD5 存密码,全都没写清楚。数据库设计文档不是存档用的,它是开发、测试、运维三方对齐口径的唯一凭据。它要回答四件事:有哪些表、字段什么类型和约束、表之间怎么关联、关键字段(比如密码、订单号)用什么规则生成。ER 图负责讲清关系,DDL 负责讲清结构,字段说明负责讲清业务含义,三者缺一不可。这篇笔记按“先立结构、再写 DDL、最后校验”的顺序,把一份能直接交给团队用的设计文档拆开讲,适合正在做课程设计、外包交付或企业内部系统重构的工程师。
2. 从 ER 图到表结构:先把关系画对再动手建表
一份设计文档翻车,八成不是 SQL 写错,而是 ER 图阶段关系就没理清。ER 图(实体-关系图)是把业务语言翻译成数据库语言的第一道关口,画错了后面全是返工。常见做法是先用纸笔或工具把实体、属性、关系列出来,再转成表。工具上,Rational Rose 是老牌选择,现在更多人用 MySQL Workbench 反向导出、draw.io 手绘,或者直接用 Mermaid 写文本版 ER 图方便进 Git 版本管理。
2.1 实体、属性、关系的三步入手法
拿到需求先别急着建表。第一步找实体:需求里反复出现的名词大概率是实体,比如“用户”“订单”“商品”。第二步定属性:每个实体有哪些字段,哪些是必须的、哪些可空。第三步定关系:一对一、一对多还是多对多,多对多必须拆中间表。
以博客系统为例,用户和文章是一对多,文章和标签是多对多。多对多如果不拆中间表,就会出现一列存多个标签 ID 的反范式设计,后面查询和更新都会很痛苦。下面用 Mermaid 语法写一份可进版本库的 ER 图,注意这里只是文本描述,实际渲染交给支持 Mermaid 的工具。
erDiagram USER ||--o{ ARTICLE : writes ARTICLE ||--o{ COMMENT : has ARTICLE }o--o{ TAG : tagged USER { bigint id PK varchar username varchar password_hash datetime created_at } ARTICLE { bigint id PK bigint user_id FK varchar title text content datetime created_at }这段描述里,||--o{表示一对多,}o--o{表示多对多。USER 和 ARTICLE 之间靠user_id外键关联,ARTICLE 和 TAG 的多对多需要额外一张article_tag中间表,ER 图里没画出来但建表时必须补上。参数上,主键统一用bigint而不是int,是因为自增 ID 在数据量上来后int会溢出;时间字段统一datetime,别混用时间戳和字符串。
2.2 用 DDL 把 ER 图钉死成可执行脚本
ER 图是给人看的,DDL 是给数据库执行的。设计文档里必须附一份能直接跑的建表脚本,否则文档和实际库迟早对不上。下面这份脚本覆盖用户表、文章表和中间表,字段注释、索引、约束都写全。
-- 用户表:存账号基础信息,密码只存哈希不存明文 CREATE TABLE `user` ( `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键', `username` VARCHAR(64) NOT NULL COMMENT '登录名,唯一', `password_hash` CHAR(32) NOT NULL COMMENT 'MD5后的密码,固定32位', `email` VARCHAR(128) DEFAULT NULL COMMENT '邮箱,可空', `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表'; -- 文章表:user_id 关联用户,建索引加速按作者查询 CREATE TABLE `article` ( `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键', `user_id` BIGINT NOT NULL COMMENT '作者ID', `title` VARCHAR(200) NOT NULL COMMENT '标题', `content` TEXT COMMENT '正文', `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_user_id` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章表'; -- 文章标签中间表:多对多拆解,联合主键防重复 CREATE TABLE `article_tag` ( `article_id` BIGINT NOT NULL, `tag_id` BIGINT NOT NULL, PRIMARY KEY (`article_id`, `tag_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章标签关联表';逻辑说明:password_hash用CHAR(32)是因为 MD5 输出固定 32 位十六进制,用变长VARCHAR反而浪费。article_tag用联合主键而不是自增 ID,是因为这张表只做关联,联合主键天然防止同一篇文章重复打同一个标签。参数上,字符集统一utf8mb4才能存 emoji,引擎统一 InnoDB 才有事务和外键支持。索引不是越多越好,user_id建索引是因为“查某人的文章”是高频操作,而content这种大字段绝不建索引。
2.3 字段命名和类型选择的取舍
命名上,表名用单数还是复数、字段用下划线还是驼峰,团队内必须统一。我一般用单数表名加下划线字段,比如user、created_at,因为跨数据库迁移时大小写敏感问题少。类型选择上,金额绝不用float,用DECIMAL(10,2);状态字段用TINYINT配注释,别用字符串;布尔值用TINYINT(1)而不是BIT,兼容性更好。这些细节不写进文档,后面每个人建表风格都不一样,维护成本翻倍。
3. 密码字段与 MD5:设计文档里必须写清的加密约定
设计文档里最容易被忽略、出事最严重的就是密码字段。很多文档只写“密码 varchar”,没写存的是明文还是哈希、用什么算法、长度多少。结果开发直接存明文,或者用 MD5 存了但没加盐,被拖库后彩虹表一查就全出来了。这一章把 MD5 在设计文档里的正确写法讲清楚。
3.1 MD5 是什么,为什么设计文档要指定它
MD5 是一种摘要算法,把任意长度输入压成固定 128 位、也就是 32 位十六进制字符串的输出。它不可逆,所以数据库里存的是哈希值,登录时把用户输入再算一次 MD5 比对。设计文档指定 MD5,是为了让所有开发对“密码字段存什么”有统一预期。但要注意,MD5 本身已经不安全,因为计算太快、彩虹表齐全。正确做法是 MD5 加盐,或者直接用 bcrypt。如果项目历史原因必须用 MD5,文档里必须写明加盐规则。
import hashlib def hash_password(raw: str, salt: str) -> str: # 盐拼在明文后面再算MD5,避免相同密码产生相同哈希 return hashlib.md5((raw + salt).encode('utf-8')).hexdigest() # 示例:同一密码不同盐,输出完全不同 print(hash_password("123456", "u_1001")) print(hash_password("123456", "u_1002"))逻辑说明:salt每个用户独立,通常用用户 ID 或随机串。这样即使两个用户密码相同,库里存的哈希也不同,彩虹表失效。参数上,encode('utf-8')保证中文密码也能正确计算;hexdigest()输出小写十六进制,和数据库CHAR(32)对应。设计文档里要明确写:盐值存哪一列、怎么生成、登录校验时怎么取。
3.2 设计文档里密码字段的写法模板
不要只写“密码”两个字。规范写法是给出字段名、类型、长度、算法、盐规则、示例。下面这张表可以直接抄进文档。
| 字段名 | 类型 | 长度 | 说明 | 示例 |
|---|---|---|---|---|
| password_hash | CHAR | 32 | MD5(明文+盐)后的十六进制 | e10adc3949ba59abbe56e057f20f883e |
| salt | CHAR | 16 | 每用户随机串,注册时生成 | a1b2c3d4e5f6g7h8 |
有了这张表,后端知道怎么存,测试知道怎么造数据,安全审计也知道你做了什么。MD5 校验工具在联调时也常用,比如比对文件完整性,但密码场景一定要加盐,这是血泪经验。
3.3 从明文到哈希的迁移路径
如果老系统已经存了明文,设计文档要给出迁移方案:新增password_hash和salt两列,登录时先按明文比对,成功后立刻算出哈希写回,逐步把明文列废弃。这个过程不能一刀切,否则老用户全登不上。文档里写清迁移步骤和回滚方案,比事后救火强得多。
4. 避坑与排查:数据库设计文档落地时的五个翻车现场
文档写得再漂亮,落地时照样踩坑。这一章按“现象 → 原因 → 解决”记录五个高频问题,都是我在实际项目里遇到过的。
现象一:文档里的字段类型和实际库不一致。原因:有人直接改库没同步文档,或者文档是手写的没跟 DDL 脚本联动。解决:把 DDL 脚本作为文档唯一来源,改库必须先改脚本再执行,文档里的表结构直接从脚本生成。
现象二:MD5 存了但没加盐,测试用彩虹表秒破。原因:文档只写了“用 MD5”,没写盐规则,开发图省事直接算明文 MD5。解决:文档强制写明盐字段和拼接规则,代码评审时重点检查密码相关逻辑。
现象三:多对多关系没拆中间表,一列存多个 ID。原因:ER 图阶段偷懒,觉得拆表麻烦。解决:只要出现“一个文章多个标签”这类描述,立刻拆中间表,联合主键防重。
现象四:索引建了一堆,查询反而变慢。原因:给大字段或低区分度字段建索引,写入时维护成本高。解决:只给高频查询条件建索引,content、description这类字段绝不建。
现象五:字符集不统一,中文和 emoji 存进去变问号。原因:建表时用了utf8而不是utf8mb4。解决:文档里强制规定所有表统一utf8mb4,建表脚本模板里写死。
5. 让设计文档可执行:校验脚本与版本管理技巧
文档最大的敌人是“写完就过期”。我现在的习惯是:设计文档里的表结构不手写,而是从数据库反向导出,再用脚本校验文档和实际库是否一致。下面这段 Python 用information_schema拉出实际表结构,和文档里声明的字段做比对,不一致就报错,接进 CI 就能防止文档漂移。
import pymysql # 连接实际库,拉取指定表的字段定义 conn = pymysql.connect(host='localhost', user='root', password='xxx', database='blog') cursor = conn.cursor() cursor.execute(""" SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, IS_NULLABLE FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'blog' AND TABLE_NAME = 'user' """) actual = {row[0]: row[1:] for row in cursor.fetchall()} # 文档里声明的期望结构,实际项目可从YAML或Markdown表格解析 expected = { 'id': ('bigint', None, 'NO'), 'username': ('varchar', 64, 'NO'), 'password_hash': ('char', 32, 'NO'), } for col, spec in expected.items(): if col not in actual: print(f"缺失字段: {col}") elif actual[col] != spec: print(f"字段不一致: {col}, 实际={actual[col]}, 期望={spec}")逻辑说明:information_schema.COLUMNS是 MySQL 自带的元数据表,能查到每个字段的类型、长度、是否可空。把文档里的期望结构写成字典,逐字段比对,任何不一致都打印出来。参数上,TABLE_SCHEMA换成你的库名,TABLE_NAME换成要校验的表。这个脚本放进 CI,每次提交 DDL 变更就跑一次,文档和库再也不会对不上。
版本管理上,ER 图用 Mermaid 文本、DDL 用.sql文件、字段说明用 Markdown 表格,全部进 Git。每次变更走 PR,评审通过再合并。这样设计文档不再是某个人的 Word 文件,而是团队共享的、可追溯的活文档。我踩过最深的坑就是文档和库分家,后来强制脚本校验才治住。希望帮到你。
本文还有配套的精品资源,点击获取