大文本和文件在数据库中如何设计?为什么不建议直接存大文件?
简化版
大文件通常不建议直接存数据库 BLOB,而是存对象存储或文件系统,数据库只保存文件元数据和访问地址。大文本要看访问模式:频繁查询的摘要字段列化,正文可单独拆表;避免大字段拖慢列表查询、索引和备份。
详细版
数据库适合管理结构化数据和事务关系,不适合承载大量文件内容。文件、图片、视频常用对象存储,数据库保存 file_id、url、size、hash、content_type、created_at 等元数据。
大文本字段如文章正文、商品详情、日志内容,可以考虑:
- 列表页只放摘要,不查正文。
- 正文拆到详情表。
- 文件内容放对象存储。
- 保存 hash 用于去重和完整性校验。
- 控制大字段不要进入高频索引和热查询路径。
面试重点是:数据库保存元数据,文件内容交给更适合的存储系统。
完整版教学
一、大字段问题不只是占空间
很多人觉得 BLOB 或 TEXT 只是字段大一点,但它会影响查询、缓存、备份、网络传输和页面性能。列表页本来只需要标题,却把几 MB 的正文或图片一起查出来,整个链路都会变慢。
数据库 Buffer Pool 或缓存页被大字段占用后,真正高频的小字段命中率可能下降。备份和复制也会因为大字段变重。
记忆钩子:大字段会拖慢的不只是存储,还有查询路径、缓存、备份和复制。
二、文件内容通常放对象存储
图片、PDF、视频、附件这类文件更适合放对象存储或文件系统,数据库保存元数据。
create table file_meta (
id bigint primary key,
storage_key varchar(256) not null,
file_name varchar(256) not null,
content_type varchar(128) not null,
size_bytes bigint not null,
sha256 varchar(64) not null,
created_at timestamp not null
);
这样数据库负责事务、权限和检索,文件系统或对象存储负责大对象读写、CDN 分发和生命周期管理。两者各做擅长的事。
三、大文本可以拆冷热字段
文章、商品详情、协议内容这类大文本,有时仍然适合放数据库,但要避免进入高频列表查询。可以把主表和正文表拆开。
article(id, title, summary, status, created_at)
article_body(article_id, content)
列表页查 article,详情页再查 article_body。如果正文平均 50KB,列表一次查 20 条,不拆表可能额外传输约 1MB 内容,而这些内容列表页根本不用。
拆表的目的不是追求形式,而是把热字段和冷字段分开,让高频路径更轻。
四、数据库存 BLOB 不是绝对禁止
有些场景可以把文件存数据库,比如小文件、强事务一致性、内网管理系统、部署环境不方便引入对象存储。但要知道代价。
| 方案 | 优点 | 代价 |
|---|---|---|
| 数据库存 BLOB | 事务一致,备份统一 | 表膨胀,复制和备份变重 |
| 对象存储 | 扩展好,适合 CDN | 要处理元数据和文件一致性 |
| 文件系统 | 简单直观 | 分布式和备份治理较麻烦 |
面试里不要绝对说“不能存”,而要说“大多数互联网业务不建议直接存大文件,除非有明确事务或运维理由”。
五、元数据和文件内容的一致性要处理
文件放对象存储后,会出现数据库记录和文件内容不一致的问题。比如文件上传成功但数据库插入失败,或者数据库记录已删除但对象存储文件没删。
常见流程:
上传临时文件 -> 写数据库元数据 -> 标记文件正式可用
删除记录 -> 标记文件待删除 -> 异步清理对象存储
定时任务 -> 扫描孤儿文件和失效记录
不要在事务里直接依赖外部对象存储的强一致回滚。更稳的是用状态字段、异步清理和定期对账。
六、hash、大小和类型字段很重要
文件元数据里 size_bytes、content_type、sha256 很实用。大小用于限制上传和展示;类型用于安全校验和响应头;hash 可用于去重、完整性校验和秒传。
同一个 sha256 + size_bytes -> 可能是同一文件
content_type=image/png -> 展示和下载策略
size_bytes=5242880 -> 5MB,便于限额统计
安全上不能只信客户端传来的文件名和 MIME 类型,服务端应做校验,避免上传可执行脚本或伪造类型。
七、常见误区与追问
- 误区:BLOB 在数据库里,所以一定更安全。 安全取决于权限、访问控制和审计,不是只看存在哪里。
- 误区:大文本和普通字段放一起没影响。 高频列表查询会被大字段拖慢,缓存和网络传输都会受影响。
- 误区:文件放对象存储就不需要数据库记录。 仍然需要元数据、权限、归属关系和生命周期状态。
- 追问:数据库记录和文件不一致怎么办? 用临时状态、异步清理、定时对账处理孤儿文件和失效记录。
- 追问:文章正文要不要拆表? 如果正文大且列表页高频不需要正文,拆表能减少热查询负担。
- 追问:hash 字段有什么用? 用于去重、秒传、完整性校验和排查文件是否被篡改。
八、面试中可以这样落地
如果设计附件系统,可以说文件内容放对象存储,数据库保存文件元数据、业务归属和权限信息。上传时先临时态,业务确认后改为正式态。
create table biz_file (
id bigint primary key,
biz_type varchar(64) not null,
biz_id bigint not null,
storage_key varchar(256) not null,
size_bytes bigint not null,
sha256 varchar(64) not null,
status varchar(32) not null,
created_at timestamp not null
);
如果是文章正文,则主表放标题摘要,正文表单独存,列表页不查正文。这样回答能同时覆盖性能和一致性。
九、加强记忆
大文本和文件设计记住“内容外置、元数据入库、热冷拆分、一致性对账”。数据库擅长结构化关系和事务,不擅长大量文件传输;对象存储擅长大对象和分发。面试时不要绝对化,而要根据文件大小、访问频率和事务要求解释取舍。