当前位置: 首页 > news >正文

VARCHAR(50) vs VARCHAR(500):存储一样大,排序却慢了 3 倍

👉这是一个或许对你有用的社群

🐱 一对一交流/面试小册/简历优化/求职解惑,欢迎加入「芋道快速开发平台」知识星球。下面是星球提供的部分资料:

  • 《项目实战(视频)》:从书中学,往事中“练”

  • 《互联网高频面试题》:面朝简历学习,春暖花开

  • 《架构 x 系统设计》:摧枯拉朽,掌控面试高频场景题

  • 《精进 Java 学习指南》:系统学习,互联网主流技术栈

  • 《必读 Java 源码专栏》:知其然,知其所以然

👉这是一个或许对你有用的开源项目

国产Star破10w的开源项目,前端包括管理后台、微信小程序,后端支持单体、微服务架构

RBAC权限、数据权限、SaaS多租户、商城、支付、工作流、大屏报表、ERP、CRMAI大模型、IoT物联网等功能:

  • 多模块:https://gitee.com/zhijiantianya/ruoyi-vue-pro

  • 微服务:https://gitee.com/zhijiantianya/yudao-cloud

  • 视频教程:https://doc.iocoder.cn

【国内首批】支持 JDK17/21+SpringBoot3、JDK8/11+Spring Boot2双版本

  • 一个被忽视的设计规范:能短就别长

  • 实验 1:存储空间——确实没区别

  • 实验 2:索引查询——也基本没区别

  • 实验 3:全表 + 排序——差距 3 倍出来了

  • 真凶:sort_buffer 内存预估按声明长度算

  • 决策矩阵:常见字段该给多少长度

  • 4 个真实生产坑,按踩到概率从高到低

  • 说到底


一个被忽视的设计规范:能短就别长

很多团队的建表规范里都有这一条:

「对可变长度字段,在满足业务的前提下,尽可能使用较短的长度」

但具体短多少?为什么要短?大部分人答不上来。最常见的「直觉式回答」是:

  • VARCHAR(50)VARCHAR(500)节省存储」—— 错。

  • 「短一点查得快」—— 对,但只在特定 SQL 模式下成立。

要回答清楚,得做实验。下面这套实验从存储、索引查询、全表排序三个维度走一遍,最后会发现:90% 时间没区别,但有一种 SQL 模式差距 3 倍以上——这才是规范要短的真正原因

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/ruoyi-vue-pro

  • 视频教程:https://doc.iocoder.cn/video/

实验 1:存储空间——确实没区别

建两张结构完全一样、只差name字段长度的表:

CREATE TABLE category_info_varchar_50 ( id BIGINT NOTNULL AUTO_INCREMENT, name VARCHAR(50) NOTNULL, is_show TINYINT(4) NOTNULLDEFAULT0, sort INT NOTNULLDEFAULT0, -- 其他业务字段省略 PRIMARY KEY (id), KEY idx_name (name) ) ENGINE=InnoDBDEFAULTCHARSET=utf8mb4; CREATETABLE category_info_varchar_500 ( id BIGINT NOTNULL AUTO_INCREMENT, name VARCHAR(500) NOTNULL, -- ... 其他字段同上 PRIMARY KEY (id), KEY idx_name (name) ) ENGINE=InnoDBDEFAULTCHARSET=utf8mb4;

各灌 100 万条同样数据(写入数据的存储过程略,实际数据完全一致),然后查information_schema.TABLES

SELECT table_name AS "表名", table_rows AS "记录数", TRUNCATE(data_length / 1024 / 1024, 2) AS "数据(MB)", TRUNCATE(index_length / 1024 / 1024, 2) AS "索引(MB)" FROM information_schema.TABLES WHERE table_schema = 'test_mysql_field';

VARCHAR(50)表:

VARCHAR(500)表:

两张表的数据容量、索引容量完全相同——VARCHAR 是变长的,实际存储看的是写入内容长度,不是声明的最大长度。「存储空间不一样」的直觉第一关就废。

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://github.com/YunaiV/yudao-cloud

  • 视频教程:https://doc.iocoder.cn/video/

实验 2:索引查询——也基本没区别

走索引的等值查询、IN 查询、ORDER BY 索引列查询:

-- 等值 SELECTnameFROM category_info_varchar_50 WHEREname = 'name100000'; -- 0.012s SELECTnameFROM category_info_varchar_500 WHEREname = 'name100000'; -- 0.012s -- ORDER BY 走索引 SELECTnameFROM category_info_varchar_50 ORDERBYname; -- 0.370s SELECTnameFROM category_info_varchar_500 ORDERBYname; -- 0.379s -- IN 查询(45 个值) SELECT * FROM category_info_varchar_50 WHEREnameIN (...); -- 0.011 ~ 0.014s SELECT * FROM category_info_varchar_500 WHEREnameIN (...); -- 0.012 ~ 0.014s

走索引的 SQL 性能差距在 1ms 量级——可以认为没差。原因很简单:InnoDB 在 DYNAMIC / COMPRESSED 行格式下,索引 key 的单字段上限是 3072 字节——VARCHAR(500) utf8mb4满打满算 2000 字节,完整能进索引;查询走 B+Tree,比较时按实际值长度来。索引这条路上 50 和 500 没毛病

仅在老的 REDUNDANT / COMPACT 行格式下,索引 key 上限是 767 字节——这种情况下VARCHAR(500) utf8mb4才会被截断成 prefix 索引,但MySQL 5.7+ 默认就是 DYNAMIC,绝大多数项目不用担心。

实验 3:全表 + 排序——差距 3 倍出来了

不走索引的全表扫描 + 排序,差距瞬间出现:

无排序,两张表表现一致:

ORDER BY name

SELECT * FROM category_info_varchar_50 ORDER BY name; -- 1.498s SELECT * FROM category_info_varchar_500 ORDER BY name; -- 4.875s

VARCHAR(50) 全表+排序耗时:

VARCHAR(500) 全表+排序耗时:

VARCHAR(500)慢了 3.25 倍。一个看着无所谓的字段长度选择,在排序场景下变成了真问题。

真凶:sort_buffer 内存预估按声明长度算

为什么会差这么多?看 SQL 执行 profile。

VARCHAR(50)的 profile——86% 时间在数据传输(Sending data):

关注两个状态变量:Created_tmp_filessort_merge_passes

  • Created_tmp_files = 3

  • sort_merge_passes = 95

VARCHAR(500)的 profile——多了「临时表排序」环节:

  • Created_tmp_files = 4

  • sort_merge_passes = 645—— 比 50 的版本暴涨 6.8 倍

sort_merge_passes是 MySQL 归并排序的次数——越大说明sort_buffer塞不下,越要靠磁盘临时文件归并,磁盘 IO 上来性能就崩。

关键来了:MySQL 在做排序前,会根据字段声明的最大长度估算每条记录在sort_buffer里的占位——VARCHAR(500)单条估到 500 字节 + 元数据开销,VARCHAR(50)估到 50 字节。同样 8MB 的sort_buffer_size

字段长度

估算单条

装得下行数

100 万行需要归并次数

VARCHAR(50)

~58 字节

~144,000

~7 轮

VARCHAR(500)

~558 字节

~15,000

~67 轮

实际数据明明都是name123456这种十几字节的字符串,但 MySQL 不看这个——它只看声明长度。这就是规范让你「能短就短」的真正动机:在排序、分组、临时表场景下,声明长度直接决定内存利用率

决策矩阵:常见字段该给多少长度

不是越短越好,太短了改起来更痛苦。给个参考:

字段类型

推荐长度

理由

用户名 / 昵称

VARCHAR(64)

主流业务 32-50 够用,留点余量

邮箱

VARCHAR(128)

RFC 5321 上限 254,但实际 < 128

手机号

CHAR(20)

定长且固定,CHAR 比 VARCHAR 更适合

URL

VARCHAR(512)

VARCHAR(1024)

长 URL 现实存在,但避免 VARCHAR(2000)

标题 / 名称

VARCHAR(128)

业务分类、文章标题、商品名都够

描述 / 富文本

TEXT

一旦超过 1KB 用 TEXT,别拿 VARCHAR(8000) 死撑

UUID / 雪花 ID

CHAR(36)

/BIGINT

定长用 CHAR,雪花用 BIGINT

极不确定的开放字段JSON

或拆出独立表

别用 VARCHAR(N) 当万能桶

为什么不无脑用 VARCHAR(255)?历史遗留——MySQL 5.0.3 之前 VARCHAR 上限是 255 字节,模板默认 255。现在 InnoDB 上 VARCHAR 最长 65535 字节,但越长在排序场景代价越大

4 个真实生产坑,按踩到概率从高到低

坑 1:分页查询带排序,TP99 突然劣化(最常见)

ORDER BY name LIMIT 100, 10看起来很轻——前端列表分页谁不写?但只要 ORDER BY 字段没走索引、且字段是 VARCHAR(500+),sort_buffer 直接爆。监控里 TP99 飙升、慢查询日志炸出一堆Using filesort; Using temporary

修法:要么给 ORDER BY 字段加索引,要么把字段长度收紧,要么调大sort_buffer_size(但调大全实例都受影响,要慎重)。

坑 2:临时表 GROUP BY 拼字段(常见)

SELECT category, COUNT(*) FROM ... GROUP BY category;

GROUP BY 也走 sort_buffer 或临时表,逻辑同 ORDER BY。长 VARCHAR 字段做 GROUP BY 同样吃亏

坑 3:UNION 把多张表的字段长度并起来(少见但破坏力大)

SELECT name FROM table_a -- VARCHAR(50) UNION SELECT name FROM table_b; -- VARCHAR(500)

UNION 结果集字段长度按最大的那个算——本来 50 就够,UNION 一接,结果集里全按 500 估。复杂报表 SQL 把多个表的字段拼起来,性能突然崩就是这种问题。

坑 4:JSON字段当 VARCHAR 用(高级场景)

「业务字段不确定,先放 JSON」——三年后字段平均 5KB,涉及它的查询、排序、临时表全炸。JSON 比 VARCHAR(N) 对 sort_buffer 更不友好。该拆表必须拆,别拿 JSON 顶。

说到底

VARCHAR(50)VARCHAR(500)在存储、走索引的查询上没区别——这条规范的真正意义在 sort_buffer 上好的字段长度设计不是「能存就行」,是「让 MySQL 在排序时不被声明长度欺骗到磁盘上」


欢迎加入我的知识星球,全面提升技术能力。

👉 加入方式,长按”或“扫描”下方二维码噢

星球的内容包括:项目实战、面试招聘、源码解析、学习路线。

文章有帮助的话,在看,转发吧。 谢谢支持哟 (*^__^*)
http://www.cnnetsun.cn/news/2477337.html

相关文章:

  • Windows安卓应用安装器:3分钟快速上手APK安装器完整指南
  • AI时代劳动力市场的结构性变革
  • YOLOv11【第四章:巅峰前沿与融合篇·第17节】联邦学习 YOLOv11:多机构隐私保护联合训练!
  • 在 Taotoken 模型广场中根据任务与预算进行多模型选型的思路
  • 深入Activiti 5.22内核:从命令模式与拦截器链看流程引擎的执行机制
  • Flutter 3.29.3+ 项目实战:用 amap_map 插件搞定高德地图与定位(保姆级避坑指南)
  • 【程序源代码】穿越红楼趣味人格测试微信小程序系统(含源码)
  • 新加坡 ONE Pass 与香港高才通对比:2027年海外名校生直接落户亚太双子星的 ROI 算账
  • 从模型网关到智能体平台
  • Vue3 + TS项目里Element Plus图标死活不显示?别慌,这5个排查步骤帮你搞定
  • 保姆级教程:用Simulink Embedded Coder生成可部署的嵌入式C代码(附避坑指南)
  • 2026年热门录音实时转文字软件盘点:如何选择适合你的转写工具?
  • 嵌入式系统软硬件本质重构:从思维固化到构件化设计
  • 快速傅里叶变换(FFT)原理与工程实践:从算法内核到音频、振动分析应用
  • KMS智能激活工具终极指南:三步永久激活Windows和Office的完整解决方案
  • 用HC-SR501和LM358给18650电池供电的感应灯做个“大脑”:手把手教你设计驱动电路
  • 别再只懂翻转和裁剪了!聊聊Mixup、CutMix这些花式数据增强,到底怎么选?
  • 如何在macOS上享受完美的歌词同步体验:LyricsX全方位指南
  • 企业AI算力工作站/深度学习推理工作站DLTM零代码私有化重塑智慧农业AI模型训练体系
  • 从零构建:基于YOLOv8/YOLOv10的智能游戏瞄准系统深度解析
  • 避开Buck电路仿真‘坑’:为什么你的电感电流会振荡?加个电阻就搞定
  • 麒麟KYLINOS V10 SP1上systemd-resolved服务挂了?别慌,三步搞定DNS解析故障
  • 3分钟搞定静态文件服务?零配置http-server的极简哲学
  • 华硕笔记本性能优化利器:三分钟掌握G-Helper完整使用指南
  • 从Capability链表到TLP传输:图解PCIE配置空间如何决定你的数据包大小
  • 如何在3分钟内将Chrome变成专业的Markdown阅读器?
  • 当金属学会“作画”——优之彩蚀刻不锈钢蜂窝板的空间艺术
  • 从实验室到生产线:手把手教你用Python为近红外光谱模型做‘压力测试’
  • HarmonyOS通知开发全解析:从渠道创建到高级应用
  • HTML转Word文档的终极解决方案:html-to-docx详解