减少 select * 能显著提升跨库查询速度,因其大幅降低网络传输量、序列化/反序列化开销及中间件处理压力,避免oom和超限报错,并需从查询意图出发精准选取最小必要字段。

为什么减少 SELECT * 能显著提升跨库查询速度
跨库查询(比如 MySQL 联合多个物理实例、PostgreSQL Foreign Data Wrapper、或应用层分库路由)本质是网络 I/O + 序列化开销叠加。每次查 *,数据库不仅要读更多页、做更多磁盘寻道,还要把所有字段序列化成协议包发到另一端——字段越多,网络传输时间越长,反序列化压力越大,中间代理(如 MyCat、ShardingSphere)也更易成为瓶颈。
常见错误现象:SELECT * 在单库下延迟 20ms,跨库后飙到 300ms+,但只查 2–3 个字段就回落到 50ms 内。
- 别依赖 ORM 默认的
findAll()或select * from users—— 它们在跨库场景下等于主动放大延迟 - 即使目标表只有 10 行,
*仍会拉取所有列定义、触发元数据协商、增加连接池等待 - 某些中间件(如 Vitess)对宽表跨库查询有默认字段数限制,超限直接报错
VT01001: too many columns in result set
怎么精准写出最小必要字段列表
不是“挑几个看着用的”,而是从查询意图出发反推:你真正要渲染/计算/校验的是哪些?其他都是噪声。
- 查用户头像和昵称 → 只要
id,nickname,avatar_url;别带created_at,last_login_ip,settings_json - 做跨库 JOIN 统计(如订单数)→ 主表只取
user_id,关联表只取order_id,连COUNT(*)都比COUNT(*)+SELECT *快得多 - 如果用的是视图或物化查询,确保底层定义里也没写
*—— 很多 DBA 会忽略这点,以为“视图封装了逻辑就安全”
示例对比:
-- 慢(跨库时多传 8 个字段,含 TEXT 类型) SELECT * FROM orders WHERE user_id = 123; <p>-- 快(只传业务强依赖的 3 个字段) SELECT id, status, paid_at FROM orders WHERE user_id = 123;</p>
JOIN 跨库时字段裁剪比单表更关键
跨库 JOIN 不是 SQL 优化器能自动“推条件下推”的场景,多数中间件会把两边全量结果拉到本地再关联。这意味着:A 库返回 1000 行 × 15 字段,B 库返回 1000 行 × 20 字段,内存里就要处理 100 万 × 35 字段的数据结构 —— 很容易 OOM 或触发 GC 暂停。
- 务必用
USING或明确ON a.id = b.user_id,避免隐式笛卡尔积 - JOIN 前先用子查询或 CTE 把每边字段压到最少,例如:
(SELECT id, user_id FROM orders WHERE status = 'paid') o - 警惕
GROUP BY *或DISTINCT *—— 这类操作在跨库中几乎必然失败,或退化为单点聚合,失去分布式意义
工具链里哪些地方会悄悄加回 *
开发时肉眼看不到,但运行时字段就变多了:ORM 的懒加载、日志插件的审计字段注入、监控 SDK 的 trace_id 自动拼接……这些都会让原本精简的 SQL 在执行前被改写。
- MyBatis 的
<select></select>标签若没写死字段,用${}拼接或<include></include>引入通用片段,极易混入冗余列 - Spring Data JPA 的
@Query若写成"SELECT u FROM User u",Hibernate 默认生成SELECT *,必须显式写成SELECT u.id, u.name FROM User u - 某些 APM 工具(如 SkyWalking)开启 SQL 参数采样后,会强制重写语句加
/*+ trace_id=xxx */注释——看似无害,但部分中间件解析注释失败,导致字段推导异常
最稳的做法:在数据库代理层(如 ProxySQL)或应用网关上开启慢 SQL 日志,抓出真实执行的语句,而不是信 IDE 里写的那条。
跨库查询没有银弹,但砍掉一个不用的字段,往往比调索引、升配置见效更快。最容易被忽略的,是开发阶段根本没意识到——那条在本地跑得飞快的 SELECT *,到了生产跨库链路里,已经不是同一个东西了。











