核心是避免大字段拖垮SQL,须垂直分表+显式字段查询。ThinkPHP 5.1中应将TEXT/BLOB移至扩展表,主表仅存轻量字段;列表页用field()限定字段,详情页按需join;一律用MEDIUMTEXT并指定utf8mb4字符集;压缩在应用层完成,禁用BLOB存文本。

ThinkPHP 5.1 中处理 TEXT/BLOB 类长文本字段,核心不是“怎么查得快”,而是“怎么不让它拖垮整条 SQL”。InnoDB 的物理存储机制决定了:只要 SELECT 语句里带了这些字段,哪怕只取 1 行,MySQL 就可能触发多次随机 I/O、污染 Buffer Pool、甚至引发 PHP 内存溢出。优化必须从存储结构和查询逻辑双线入手。
垂直分表:强制把大字段搬出主表
这是 ThinkPHP 5.1 下最有效、也最容易落地的硬性优化。不要试图在原表加索引或调参数——数据物理布局没变,一切优化都收效甚微。
- 新建扩展表(如 article_content),仅存
article_id(加普通索引)和content(MEDIUMTEXT) - 主表(article)彻底移除 TEXT 字段,只保留 ID、标题、状态、时间等轻量字段
- 列表页用
Db::name('article')->field('id,title,status,created_at')->select(),完全不碰大字段 - 详情页按需关联:
Db::name('article')->alias('a')->join('article_content c','a.id=c.article_id')->where('a.id', $id)->find()
字段类型与字符集必须显式指定
BLOB 不走字符集校验,会导致 LIKE、ORDER BY、前缀索引全部失效;而默认的 TEXT 可能隐式使用低效 collation。
- 一律改用 MEDIUMTEXT(上限 16MB),避免盲目用 LONGTEXT 增加管理成本
- 执行 DDL 显式声明字符集:
ALTER TABLE article_content MODIFY content MEDIUMTEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; - 禁止用 BLOB 存文本——即使内容是 JSON 或 HTML,也应选 TEXT 并设对 collation
应用层压缩 + 按需解压
数据库内压缩(如 COMPRESS())会破坏可读性、增加客户端解压负担,且无法配合 ORM 自动映射。压缩必须由 ThinkPHP 应用层控制。
立即学习“PHP免费学习笔记(深入)”;
- 写入前压缩:
$compressed = gzencode($rawText, 9);,存入 MEDIUMTEXT(注意字段长度足够容纳二进制) - 读取后解压:
$rawText = gzdecode($compressed);,仅在真正需要展示/处理时才执行 - 纯文本、日志、HTML 压缩率高(60%~80%),已压缩文件(PNG/JPEG/PDF)再压无效,跳过
- 压缩后字段禁止建索引——二进制无语义,LIKE 和前缀索引全失效
查询时绕开溢出页的实操写法
即使未做垂直分表,也能通过 SQL 写法减少伤害。ThinkPHP 5.1 支持链式调用,关键在 field() 和 join() 的精准控制。
- 列表页绝对不用
->select()无参调用,必须显式列出字段:->field('id,title,cover_img,summary') - 关联查询时限定字段:
->with(['author' => function($q){ $q->field('id,nickname,avatar'); }]) - 需要模糊匹配但又不想加载全文?建虚拟列哈希:
ALTER TABLE article ADD COLUMN content_md5 CHAR(32) AS (MD5(content)) STORED;,再建索引并用WHERE content_md5 = ? - 超大内容导出场景,用流式读取:
Db::query("SELECT content FROM article_content WHERE id = ?", [$id], true)(第三个参数 true 启用 PDO::FETCH_COLUMN,避免整字段载入内存)



















