惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

爱范儿
爱范儿
博客园 - 叶小钗
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
腾讯CDC
S
SegmentFault 最新的问题
D
DataBreaches.Net
Hugging Face - Blog
Hugging Face - Blog
L
LangChain Blog
Recent Announcements
Recent Announcements
阮一峰的网络日志
阮一峰的网络日志
N
Netflix TechBlog - Medium
大猫的无限游戏
大猫的无限游戏
M
MIT News - Artificial intelligence
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - 三生石上(FineUI控件)
F
Fortinet All Blogs
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Martin Fowler
Martin Fowler
雷峰网
雷峰网
J
Java Code Geeks
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
小众软件
小众软件
云风的 BLOG
云风的 BLOG
T
Tailwind CSS Blog

博客园 - buguge

交易成功还要记 3 笔账?我们直接砍到 1 笔—— 网络红包记账的一次自顶向下业务层重构 分享一次针对hardcode的小重构:不要把 package 名写死在代码里 聚合系统设计:如何为银行代付通道抽象出公共的代付接口能力 如何自定义 MyBatis枚举处理器(EnumTypeHandler):分享从 “同包同名 shadow 覆盖” 到 “官方扩展点” 的重构经历 这儿也改状态,那儿也改状态,连AI都理不清楚了… 不妨试试这个状态机good-practice 业务解耦的经典实践:从订单表解耦开票业务说起(以滴滴网约车开票为例) 从 `int` 到 `Duration`:一个缓存 API 的三次演进教会我的事 最差实践(bad-practice):开发者在方法里直接实例化线程池对象,然后...(应用gg了) 【HttpClient最差实践(bad-practice)】开发者在 http 工具方法中直接实例化 HttpClient,然后…… Crypto、Cipher与Password:Java加密开发的三个核心概念 知识VS技能:如何优雅判空? 20260604SR超时问题排查 推敲见文章:从 `try..catch` 看异常日志打印的正确姿势 #解决问题要彻底# 慢SQL治理完成后,如何防止同类问题“死灰复燃”? 从合同甲方是荒谬的“JD”谈起:软件开发不应遗忘的“常识” 别留小尾巴/尽快剪掉小尾巴:从一次“ABA”字段重命名,谈谈“解决问题要彻底” 常见的OOM错误 ( OutOfMemoryError全类型详解) 开发者暴露了一个无需授权访问的裸接口,我问:如果有人暴力请求怎么办? 【SQL性能优化篇】有了!治理慢SQL“WHERE create_time ORDER BY id”的良药---规避“Using filesort”性能杀手 高效查询商户日终余额:一个SQL的优化实践 Hutool 的 `TimedCache` 到期会自动清理吗? ——————hutool cache的"惰性清理"和"定期清理" Fastjson枚举反序列化:当字符串不是枚举常量名时,会发生什么? fastjson-EnumDeserializer类及源码分析 随笔20260309:我们都是围城里的人 `UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only` 异常解析 认识2个单词:goal/target —————— 为什么Maven是 "goal" 而不是 "target"? 聚合系统设计:策略模式(Strategy Pattern)在银行通道对接场景中的应用 这样构建对象,太帅了!—— 阶梯式Builder模式与代码整洁之道 还在用ArrayList?用HashSet吧!--性能对比 分页查询还在用create_time去做降序?
注意!字段数据类型不匹配,这个sql会很慢
buguge · 2026-02-13 · via 博客园 - buguge

在数据库优化中,我们经常会关注索引的创建、SQL的写法,但有一个隐蔽的“性能杀手”却容易被忽略——字段数据类型与查询值类型不匹配

一、问题现象:一个“简单”查询的异常性能

我们有一张交易表 plat_order,其中 order_id 字段定义为 VARCHAR(20),并且在该字段上建立了索引 index_order_id

当执行以下查询时:

-- 查询A:使用数字字面量
SELECT * FROM plat_order WHERE order_id = 1669233832303042562;

这条看似简单的查询却不会走索引,而是进行了全表扫描,执行时间可能长达数秒甚至更久。

而当我们稍作修改:

-- 查询B:使用字符串字面量  
SELECT * FROM plat_order WHERE order_id = '1669233832303042562';

同样的查询条件,只是将数字改为字符串,执行时间立即降至毫秒级。

二、执行计划对比:触目惊心的差异

通过 EXPLAIN 分析两条SQL的执行计划,差异一目了然:

查询语句 type key rows Extra 性能评估
WHERE order_id = 16692... ALL (null) 8,533,253 Using where 全表扫描,极慢
WHERE order_id = '16692...' ref index_order_id 1 (null) 索引扫描,极快

从执行计划可以清晰看到:

  • 查询Atype=ALL 表示全表扫描,key=(null) 表示未使用任何索引,需要处理853万行数据
  • 查询Btype=ref 表示使用索引等值查找,key=index_order_id 表示使用了该索引,仅需处理1行数据

三、技术原理:MySQL的隐式类型转换机制

3.1 什么是隐式类型转换?

当SQL语句中参与比较运算的两个数据类型不一致时,MySQL会自动将它们转换为相同类型,这个过程称为隐式类型转换

3.2 数字与字符串比较的转换规则

MySQL遵循一个核心原则:总是尝试将值转换为表达式中另一操作数的类型。具体到数字与字符串的比较:

  1. 当字符串列与数字比较时VARCHAR = 数字):

    • MySQL会将数字转换为字符串
    • 但这种转换是在运行时对每一行数据动态计算
    • 结果:索引失效
  2. 当数字列与字符串比较时INT = '字符串'):

    • MySQL会尝试将字符串转换为数字
    • 如果转换成功(如'123'→123),可能使用索引
    • 如果转换失败(如'abc'→0),按转换结果查询

3.3 为什么索引会失效?

这是问题的关键。当执行 WHERE order_id = 1669233832303042562 时:

  1. MySQL需要将数字 1669233832303042562 转换为字符串
  2. 这个转换不是对索引列的简单函数包裹,而是对比较值的处理
  3. 但由于转换发生在比较操作上,优化器无法确定转换后的值是否仍满足索引的有序性
  4. 出于保守策略,MySQL选择放弃使用索引,改为全表扫描
-- MySQL实际执行的近似逻辑(概念上):
SELECT * FROM plat_order 
WHERE CONVERT(order_id USING utf8mb4) = CONVERT(1669233832303042562 USING utf8mb4);
-- 注意:order_id上的函数操作导致索引失效

3.4 反向问题解答:数字列与字符串比较

如果 order_id 是数字类型(如 BIGINT),执行 WHERE order_id = '1669233832303042562'

  1. MySQL会尝试将字符串 '1669233832303042562' 转换为数字
  2. 如果转换成功,转换仅作用于常量值,不涉及索引列
  3. 因此,可以使用索引
  4. 但是,如果字符串无法转换为数字(如 'ABC123'),会转换为0,可能导致错误的结果
-- 示例:数字列与字符串比较
CREATE TABLE order_num (
    id BIGINT PRIMARY KEY,
    order_id BIGINT,
    INDEX idx_order_id(order_id)
);

-- 可以走索引(字符串转为数字)
EXPLAIN SELECT * FROM order_num WHERE order_id = '1669233832303042562';
-- type: ref, key: idx_order_id

-- 也可以走索引(直接使用数字)
EXPLAIN SELECT * FROM order_num WHERE order_id = 1669233832303042562;
-- type: ref, key: idx_order_id

四、核心规律总结

场景 转换方向 索引是否可用 性能影响
字符串列 = 数字值 数字→字符串 全表扫描,性能极差
数字列 = 字符串值 字符串→数字 正常使用索引
字符串列 = 字符串值 无转换 最佳性能
数字列 = 数字值 无转换 最佳性能

黄金法则永远让查询值的类型与列定义的类型保持一致

五、最佳实践建议

  1. 严格类型匹配:在编写SQL时,确保查询条件的类型与字段类型一致 --- 在CI/CD流程中可加入SQL静态分析,自动化检测类型不匹配模式。
  2. 参数化查询:使用PreparedStatement,避免手动拼接SQL时忽略类型。
  3. 保持一致性:系统中的相同含义字段应使用相同的数据类型。合理选择字段类型也很重要,如果字段存储的是纯数字标识,优先考虑使用数字类型。
  4. 使用EXPLAIN验证:对复杂查询或性能关键查询,使用EXPLAIN确认索引使用情况。