如何优化ThinkPHP中大字段ThinkPHPText的存储与查询?
- 内容介绍
- 文章标签
- 相关推荐
本文共计756个文字,预计阅读时间需要4分钟。
大段落(TEXT、BLOB、JSON)必须和主表分离,否则一查就拖整条SQL。 这不是加索引能解决的,是数据物理布局和MySQL执行机制决定的——只要SELECT里包含TEXT字段,何必只取1行,MySQL就可能放弃内存临时表,转而写磁盘临时表,IO直接翻倍。
TEXT 字段为什么会让查询变慢
MySQL 对大字段的处理很“实在”:它不会像普通字段那样走内存缓存,而是按需从磁盘读取完整内容。一旦 SQL 中出现 SELECT * 或没限制字段的 select(),哪怕你模板里只用 $user->name,数据库仍要把 content、description 这类字段整个捞出来、序列化、传网络、PHP 再解析——三重浪费。
- 宽表 + TEXT 字段 → 容易触发
Using temporary; Using filesort - 分页查 20 条,每条带 100KB content → 光传输就多占 2MB,Redis 缓存体积也跟着膨胀
-
ORDER BY或GROUP BY涉及 TEXT 字段 → 索引完全失效,必走全表扫描
怎么把 TEXT 字段从主查询里摘出去
核心原则:列表页、聚合页、搜索页,一律不查大字段;只在详情页按需加载。
- 列表页用
field('id,title,created_at,status'),显式排除content、remark等 - 详情页再单独查:
ArticleModel::where('id', $id)->field('id,title,content,cover')->find() - 如果非要一起查(比如导出),改用子查询或 JOIN 拆开:
->field('a.id,a.title,a.created_at,(SELECT content FROM article_ext WHERE article_ext.article_id = a.id) as content') - 模型关联时,
with('ext')的关联模型里必须写死field('article_id,content'),否则照样全字段拉
数据库层面怎么设计才不踩坑
别指望 ORM 层补救,结构不合理,再怎么 field() 都是亡羊补牢。
立即学习“PHP免费学习笔记(深入)”;
- 把
content、html、json_config等大字段单独拆成_ext表,用外键关联,主表保持轻量 -
_ext表的外键字段(如article_id)必须加索引,否则 JOIN 变全表扫 - 避免在大字段上建索引,哪怕只
WHERE content LIKE '%xxx%',也会让索引膨胀、写入变慢 - TEXT 字段默认值设为
NULL,不是空字符串 —— MySQL 对 NULL 的存储更省空间
最常被忽略的一点:字段缓存(optimize:schema)会把 SHOW COLUMNS 结果固化,但如果你后期加了 TEXT 字段又没重新生成,ORM 仍按旧结构解析,可能漏掉字段或报错。上线前务必确认 runtime/schema/ 下的缓存文件已更新。
本文共计756个文字,预计阅读时间需要4分钟。
大段落(TEXT、BLOB、JSON)必须和主表分离,否则一查就拖整条SQL。 这不是加索引能解决的,是数据物理布局和MySQL执行机制决定的——只要SELECT里包含TEXT字段,何必只取1行,MySQL就可能放弃内存临时表,转而写磁盘临时表,IO直接翻倍。
TEXT 字段为什么会让查询变慢
MySQL 对大字段的处理很“实在”:它不会像普通字段那样走内存缓存,而是按需从磁盘读取完整内容。一旦 SQL 中出现 SELECT * 或没限制字段的 select(),哪怕你模板里只用 $user->name,数据库仍要把 content、description 这类字段整个捞出来、序列化、传网络、PHP 再解析——三重浪费。
- 宽表 + TEXT 字段 → 容易触发
Using temporary; Using filesort - 分页查 20 条,每条带 100KB content → 光传输就多占 2MB,Redis 缓存体积也跟着膨胀
-
ORDER BY或GROUP BY涉及 TEXT 字段 → 索引完全失效,必走全表扫描
怎么把 TEXT 字段从主查询里摘出去
核心原则:列表页、聚合页、搜索页,一律不查大字段;只在详情页按需加载。
- 列表页用
field('id,title,created_at,status'),显式排除content、remark等 - 详情页再单独查:
ArticleModel::where('id', $id)->field('id,title,content,cover')->find() - 如果非要一起查(比如导出),改用子查询或 JOIN 拆开:
->field('a.id,a.title,a.created_at,(SELECT content FROM article_ext WHERE article_ext.article_id = a.id) as content') - 模型关联时,
with('ext')的关联模型里必须写死field('article_id,content'),否则照样全字段拉
数据库层面怎么设计才不踩坑
别指望 ORM 层补救,结构不合理,再怎么 field() 都是亡羊补牢。
立即学习“PHP免费学习笔记(深入)”;
- 把
content、html、json_config等大字段单独拆成_ext表,用外键关联,主表保持轻量 -
_ext表的外键字段(如article_id)必须加索引,否则 JOIN 变全表扫 - 避免在大字段上建索引,哪怕只
WHERE content LIKE '%xxx%',也会让索引膨胀、写入变慢 - TEXT 字段默认值设为
NULL,不是空字符串 —— MySQL 对 NULL 的存储更省空间
最常被忽略的一点:字段缓存(optimize:schema)会把 SHOW COLUMNS 结果固化,但如果你后期加了 TEXT 字段又没重新生成,ORM 仍按旧结构解析,可能漏掉字段或报错。上线前务必确认 runtime/schema/ 下的缓存文件已更新。

