news 2026/10/2 9:30:49

MySQL数据类型实战避坑指南:选型错误如何拖垮性能与存储

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MySQL数据类型实战避坑指南:选型错误如何拖垮性能与存储

先声明一下,这篇不是什么新手教程,也不是把官方文档抄一遍的科普贴。今天就想聊点实在的:MySQL 数据类型用不好,后面有多少坑等着你。我见过太多线上事故,索引失效、表锁死、存储膨胀、查询慢出天际,追根溯源,就是建表那会儿“类型没选对”或者“类型选对了但用得不对”。所以这篇全按实战场景来讲,结合我这些年踩坑的切身经历,把 MySQL 各数据类型掰开揉碎说清楚,适合正在写业务、建库表、优化慢 SQL 的朋友。看完你至少能避开一多半常见的类型坑。

1. 数据类型全貌:先搞懂类型为什么值得死磕

1.1 类型选型直接影响性能和存储

MySQL 存储数据是按行再按页(16KB 默认)组织的,一个表塞了太多没必要的空间,单个页能放下得行数就少,InnoDB 聚簇索引的层数加深,B+ 树查询多访问几层,IO 自然就涨上去了。更关键的是索引对类型的敏感度:VARCHAR 与 CHAR 长度计算方式不同,DATETIME 和 TIMESTAMP 字节数不同,ENUM 内部是按整型存的,类型不对就像穿错了鞋,跑起来怎么都不舒服。

MySQL 官方文档一直强调“数据类型选择要基于实际存储需求和查询模式”,但实务中很多人是拍脑袋:数字就无脑 INT,字符串就 Varchar(255),日期就用 DATETIME。没有想清楚底层占用、精度损耗、隐式转换、排序冲突这些事。我们一项项过,把每个类型的度量衡和边界都摸清楚。

1.2 类型族谱:整体有哪些大门类

日常建表基本逃不出这几大类:

类别主要类型代表用途
整数TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT主键、状态、计数、外键
小数DECIMAL、FLOAT、DOUBLE金额、比例、科学计算
字符串CHAR、VARCHAR、TEXT、ENUM、SET昵称、文章、标签、状态码
二进制BINARY、VARBINARY、BLOB加密串、图片、文件内容
日期时间DATE、TIME、DATETIME、TIMESTAMP、YEAR生日、创建时间、更新时间
复合与扩展JSON、空间类型 GEOMETRY接口存储、地理坐标

1.3 任何场景都通用的三条铁律

第一,能用最小满足需求的类型,就不用大的。人的年龄 0-120 岁,TINYINT 无符号就够,非要用 INT 就是浪费 3 个字节/行,一亿行就是浪费 300MB 磁盘和内存。这不是抠,是实打实的热数据加载代价。

第二,精度是硬需求时,尽量用不丢精度的类型。商品价格、订单金额必须是 DECIMAL,FLOAT/DOUBLE 这一秒算出来对,下一秒可能就不对了,后面细说。

第三,MyISAM 退坑后,大部分类型约束都跟着 InnoDB 走。你写的 CREATE TABLE 最终要理解在 InnoDB 行格式(DYNAMIC/COMPACT)下是怎么存储的,尤其是 VARCHAR/TEXT 的物理存储行为。

2. 数值类型:整数与小数各有各的讲究

2.1 整数类型选型与 INT(11) 的著名误解

整数类型六个兄弟,先看表和范围:

类型字节数有符号范围无符号范围
TINYINT1-128~1270~255
SMALLINT2-32768~327670~65535
MEDIUMINT3-8388608~83886070~16777215
INT4-2147483648~21474836470~4294967295
BIGINT8±9.22×10¹⁸0~1.84×10¹⁹

选型时第一看业务上限,第二看有没有负数可能。一个用户积分表,积分不会为负,无符号 INT 就能扛 42 亿;如果业务激进日增百万条流水,几年后要破上限,建议直接 BIGINT,不要中途再改 ALTER。因为改主键或唯一索引类型在千万级大表上代价很高,Online DDL 也要吃额外 IO,不划算。

提到 INT(11),这是 MySQL 新手的经典认知坑。INT(11) 里的 11 是显示宽度(display width),不是存储上限。它不影响存储字节,也不会限制你能存多大,只有配合 ZEROFILL 填充时才有展示意义,例如 INT(5) ZEROFILL 存 42 会显示为 00042。注意 MySQL 8.0.17 开始显示宽度已经被标记废弃,建表建议直接写 INT,别加括号。

主键选择上也有讲究。表主键常见自增 INT/BIGINT,但如果是分布式 ID、雪花 ID,或者未来数据量明确会超过 21 亿,就必须用 BIGINT。以前有个同事在订单表用了 INT 主键,线上跑到 21 亿差点溢出,紧急 ALTER 主键类型,大半夜搞了半天。这种问题是设计期就埋雷的最好例子。

2.2 小数与精度陷阱:别让 FLOAT 毁掉金额数据

FLOAT 和 DOUBLE 是浮点近似类型,存的是二进制近似值。DECIMAL 是定点数,按十进制数字串存储,计算时是精确十进制运算。为什么强调这个?因为浮点运算时 0.1+0.2 这种看似简单的计算会变成 0.30000000000000004 之类的结果。在金额累计、对账、费率计算场景,误差一旦累计起来会引发严重对账不平。

我处理过的真实案例:一张优惠表用 DOUBLE 存储 discount_rate,某个订单 0.1+0.2 的折扣结果差出 0.00000000000000004,虽然单笔极小,但月度汇总时对不上账,排查半天最后定位到类型问题。从那以后,凡是金额、费率、单价,一律 DECIMAL。

DECIMAL 使用要点:格式 DECIMAL(M,D),M 是总位数(最大 65),D 是小数位。比如 DECIMAL(10,2) 表示整数部分最多 8 位,小数 2 位,最大 99999999.99。设置 D 时想清楚业务精度:金额一般 2 位,但汇率可能 4 位,利率可能 5-6 位。太小会四舍五入吞数据,太大浪费存储(DECIMAL 每 9 位数字用 4 字节,D 也占空间)。

FLOAT/DOUBLE 也不是完全没用。科学计算、指标趋势、GPS 坐标准确率这类对精度不敏感但对范围敏感的场景,DOUBLE 是合理的。但你在 SELECT 它们之后要记得 ROUND,不要直接拿原始值做等值比较。

2.3 实务经验:布尔值用 TINYINT(1) 还是 BOOLEAN ?

MySQL 不像 PostgreSQL 有原生 BOOLEAN。MySQL 里的 BOOL/BOOLEAN 只是 TINYINT(1) 的同义词,存 TRUE/FALSE 实际是 1/0。很多 ORM 框架会自动把 Java Boolean / Python bool 映射为 TINYINT(1),这在大多数场景是默认标配。

但要注意:不要试图用 TINYINT(1) 存大于 127 的值,虽然它本质是 1 字节整数,你传个 200 进去严格模式直接报错,宽松模式会截断为 127。我看到过有人把 TINYINT 当状态字典用,塞 1/2/3/4 没问题,超过 127 就傻眼了。所以状态字段最好先想清未来状态数量:不超过 2 直接用 TINYINT(1),可能超过 127 用 SMALLINT 更稳。

还有一类业务常量很有迷惑性:原价、折扣、库存变化量、重试次数,看起来是整数,但未来极可能要小数。我个人经验:能明确不会变整数语义的使用 INT/BIGINT,但凡有一点可能涉及小数,设计期直接 DECIMAL 反而省心。

3. 字符串与文本:长度算错,一夜回到解放前

3.1 CHAR 与 VARCHAR:差一个字节,差一个世界

CHAR(N) 是定长字符串,存 N 个字符,不够的右侧补空格,取出时再把尾部空格去掉。VARCHAR(N) 是变长字符串,额外用 1-2 个字节记录实际长度(<=255 字节用 1 字节,更大用 2 字节)。两者哪个更快?

短的、经常更新的、长度恒定的字段(订单号、MD5、手机号、固定编码)适合 CHAR。CHAR 定长意味着行内位置固定,InnoDB 访问时不需要解析长度前缀,更新时不会重排行结构,轻微性能优势。但大量都是变长业务字段(昵称、邮箱、地址),就必须 VARCHAR。

经典反例:用户表邮箱用 CHAR(100),大部分邮箱实际不到 30 个字符,每行就白占 70+ 字符空间,如果一个字符在 utf8mb4 下最多 4 字节,浪费非常夸张。表一大,页分裂、缓存命中率都会受影响。

行格式下 VARCHAR 的最大长度受 65535 字节行大小限制,注意是“字节”不是“字符”。utf8mb4 一个汉字最多 4 字节,理论最大 VARCHAR 长度 = (65535 - 其他列占用 - 长度前缀字节) / 最大字节数。我们不搞极限,更重要的是实践中的经验和教训。

3.2 utf8mb4 下 VARCHAR(255) 的坑

在设计索引时,你一定会遇到 VARCHAR(255) 的“诅咒”。InnoDB 默认 16KB 页时,单列索引最大支持 3072 字节(DYNAMIC 行格式)。utf8mb4 下 VARCHAR(255) 最多占 255×4 + 2 = 1022 字节,单列建索引没问题。但如果你要建联合索引,比如 (a,b,c) 三个都是 VARCHAR(255),那 3×1022=3066 字节,勉强没超;要是再加一个字段,直接报 “Specified key was too long; max key length is 3072 bytes”,你就得回头改。

更隐蔽的坑在老版本或特定配置下:如果你还在用 COMPACT 行格式或者小页,索引长度限制会掉到 767 字节,这时候 VARCHAR(255) 在 utf8mb4 下连单列索引都建不了。所以很多老项目里看到 VARCHAR(191),因为 191×4+2=766,正好卡在 767 的圈内。这也解释了为什么很多“经验文档”里写 varchar 最大 191 建索引。

我的建议是:除非业务明确需要长字符串,否则:

  • 状态、短码、枚举值直接 VARCHAR(32) 或更短;
  • 名称、标题等常规短文本 VARCHAR(64/128) 足矣;
  • 邮箱、URL 虽然可长可短,但 VARCHAR(255) 一般没问题,因为单列索引常见、联合索引很少参与;
  • 联合索引里的字符串列,先估算业务实际长度,能短则短。

3.3 TEXT/BLOB 与兜底思路

TEXT 系列:TINYTEXT、TEXT、MEDIUMTEXT、LONGTEXT,最大字节数分别为 255、65535、16777215、4G-1。BLOB 类似但按二进制存储。TEXT 可变长、按前 N 字节排序和比较,InnoDB 下大数据会放到溢出页,主键行里只存前 768 字节(DYNAMIC 行格式下存 20 字节指针加局部内容)。

TEXT 的麻烦:不能直接在 TEXT 上建普通索引,只能建前缀索引(如 VARCHAR(20))来保证索引长度可控。行内数据不固定,SELECT * 查 TEXT 会把大字段拉到内存,如果只是列表页,等于每次都在搬运一堆没人看的数据,缓存和内存压力直线上升。所以,文章内容、日志详情、接口返回值这类大文本,要养成“列表查询不带上 TEXT 字段”的习惯。

BLOB 现在业务场景越来越少了,图片文件基本走对象存储,只有偏传统的系统会存二进制流。如果你确实要存加密串(哈希、密钥),优先 VARBINARY 而不是 CHAR/VARCHAR,因为二进制字节流没有字符集转换干扰,长度精确。但要注意 VARBINARY 等值比较就是字节比较,应用层拿到手记得还原编码。还有一个点:BLOB/TEXT 在 MySQL 8.0.13 之前不能有默认值;8.0.13 之后可以用表达式默认值,但直接 DEFAULT 'xxx' 仍不受支持。建表时别乱写默认值,否则报错。

3.4 ENUM 与 SET:好用,但别碰扩展地狱

ENUM 是定义在固定列表里选一个值的类型,内部以整数索引存储(1 开始),展示时按列表文本输出。它的天然优势:省空间,一个 ENUM 列在排序和比较时按索引整数走,比 VARCHAR 快不少,而且约束规范。比如订单状态:PENDING、PAID、SHIPPED、COMPLETED、CANCELLED,用 ENUM 建列可读性极好,存一个字符在 1 字节内搞定(列表 <= 255 项)。

但 ENUM 最坑的是扩展性:你想增加一个新的枚举值时,MySQL 不是只改 frm 文件那么简单,8.0 之前会复制整表数据甚至锁全表,虽然从 8.0 开始支持对 ENUM 的某些操作即时生效,但枚举顺序的调整仍然可能触发表重建。枚举顺序对排序也有影响——它按定义顺序而非字典序排序,例如你定义 ('b','a','c'),ORDER BY 返回的是 b,a,c 的原始顺序,容易让人大跌眼镜。

SET 则是“多选一”场景,实际是位图,每个值占 1 bit,多个值可以组合。比如用户权限字段 SET('READ','WRITE','DELETE'),读出来是逗号分隔的字符串。查询时用 FIND_IN_SET 或 LIKE,SQL 写起来很别扭,只有对空间极度敏感且枚举极稳定的老系统才值得用。新项目我强烈建议别用 SET,一张多对多表结构表达权限,扩展和维护都清晰得多。

4. 日期时间:时间戳不是万能药,字符串存日期是大忌

4.1 DATETIME 与 TIMESTAMP:摆正各自位置

MySQL 日期时间主要两类:DATETIME 和 TIMESTAMP。

DATETIME 占用 8 字节,范围 1000-01-01 00:00:00 到 9999-12-31 23:59:59,存储时不带时区概念。你存的是什么,取出来就是什么,非常直白。TIMESTAMP 占用 4 字节,范围 1970-01-01 到 2038-01-19,它内部按 UTC 存储,展示时会按会话时区转成本地时间。看起来自动做时区转换是优势,但它有两个不省心的点:一是 2038 年问题,很多老系统到现在还在吃这个苦头;二是如果 MySQL 时区设置和应用服务器时区不一致,会出现“存进去 8 点取出来 16 点”这种幽灵时差。

我现在的默认建议很直接:新项目一律用 DATETIME。最重要的原因就是业务展示时间要稳定可控,你不想因为换了机房、改了 global time_zone 导致全站时间错乱。也别指望 4 字节的空间优势,一张表几千万行多出来的 4 字节在日期字段上不值一提。TIMESTAMP 更推荐用在纯机器层面需要自动记录 UTC 变化的地方,比如数据的物理修改时间。

4.2 时区问题与连接配置

使用 TIMESTAMP 前,先确认三处时区:MySQL 的 system_time_zone(服务器系统时区)、global.time_zone、session.time_zone。很多连接池配置会建议 JDBC URL 上加 connectionTimeZone=LOCAL 或者 serverTimezone=Asia/Shanghai,目的就是为了让驱动和实例时区对齐。但如果你这一层没对齐,而代码里又用 new Date() 去参数化查询,就会出现非常隐蔽的日期偏移。

DATETIME 不受这个影响,因为它是纯文本语义。如果你一定要用 TIMESTAMP,线上建议统一用“东八区 + JDBC 显式指定时区”的组合,并且所有环境保持一致。实务里因为时区配置不一致导致的“凌晨数据少了 8 小时”,已经反复成为排查事故的第一嫌疑。

4.3 默认值、自动更新、插入与更新行为

MySQL 5.6.5 之后,DATETIME 也支持 DEFAULT CURRENT_TIMESTAMP 和 ON UPDATE CURRENT_TIMESTAMP。常见写法:

CREATE TABLE orders ( id BIGINT PRIMARY KEY AUTO_INCREMENT, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP );

这里 DEFAULT CURRENT_TIMESTAMP 只负责插入时自动填当前时间,ON UPDATE 负责更新时自动刷新。注意:ON UPDATE 在你手动把该列 UPDATE 成某个固定值时会触发吗?不会。它只在行数据真实发生变更且该字段被更新为 DEFAULT 时才生效。如果你想业务上强制保留历史时间,就不要给 updated_at 这个 ON UPDATE,应用层显式赋值才是你真正能控制的。

另外要避开一个历史问题:TIMESTAMP 在旧版本默认 NOT NULL 且插入 NULL 时会自动替换为当前时间。这在 5.6+ 中已经不再那么激进,但很多老项目迁移时会出现大量 NULL→当前时间 的脏数据。与此同类的还有“日期用字符串类型存储”,这是我强烈反对的。字符串存日期有三个致命问题:排序是按照字典序,2023-01-31 会排在 2023-02-01 前面,看起来好像碰巧对,但一旦日期格式不一致,排序就完蛋;范围查询和日期函数计算要隐式转换,索引基本废掉;更难得维护校验规则。所以日期就老老实实用 DATE/DATETIME,不要自作聪明存 VARCHAR。

4.4 日期还是要按粒度选类型

一天以内的业务(营业时间、会议时段)用 TIME,比如 09:00-18:00;只关心年月日的生日、契约日用 DATE;要精确到秒且跨年度用 DATETIME。我的习惯是这类字段能少则少,能用 DATE 就不用 DATETIME,因为 DATE 只占 3 字节,而且在界面展示上天然少一截。

5. 实战边界:JSON、BIT、BINARY 和其他冷门类型

5.1 JSON:真香,但别当万能表丹

MySQL 5.7 引入原生 JSON 类型,8.0 大幅增强。JSON 列存储的是解析后的二进制格式(不是纯文本字符串),查询时可以走虚拟列索引(Generated Column),也支持常用 JSON 函数:JSON_EXTRACT、JSON_UNQUOTE、-> 与 ->> 操作符、JSON_CONTAINS、JSON_ARRAYAGG 等。

实际业务里 JSON 很合适保存“结构不固定的扩展属性”:渠道来源的额外参数、埋点事件自定义字段、第三方回调原始报文。建表示例:

CREATE TABLE event_logs ( id BIGINT PRIMARY KEY AUTO_INCREMENT, event_name VARCHAR(64) NOT NULL, event_data JSON NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP );

查询时:

SELECT event_name, event_data->>'$.userId' AS user_id FROM event_logs WHERE event_data->>'$.channel' = 'app';

JSON 类型不是银弹,最大的坑是:JSON 列无法建普通索引;只能用生成列向 JSON 中的数据建虚拟索引。比如想给 user_id 建索引,必须先建生成列。另一个问题:JSON 更新是整体替换,MySQL 内部并不支持高效的“局部更新”,你在应用层修改 JSON 字符串再 UPDATE 回去,可能造成整行重写和版本链膨胀。另外 JSON 与关系型模型是两种哲学,滥用 JSON 会让后续统计分析和 JOIN 变成噩梦。一条 JSON 里塞 20 个字段,看起来灵活,等你要按其中一个字段做过滤时就开始后悔。

5.2 BIT、BINARY 与空间类型:需要用时候再深挖

BIT(M) 可以存位串,比如权限组合、开关位,但实际用 SET 或整数位运算往往更方便,绝大多数情况建议避开,因为 SQL 里处理位操作不是常规人类擅长的事。BINARY/VARBINARY 主要存二进制定长内容,例如长度固定的哈希摘要 32/64,VARBINARY(32) 比 CHAR(32) 更精确占用也小;但要注意查询比较时是大小写敏感、字节序比较,和字符串写起来有点区别。

空间类型(GEOMETRY、POINT、LINESTRING、POLYGON)用得相对少,但做地图/门店周边/轨迹类业务时是正经利器。它依赖空间索引(SPATIAL INDEX),MySQL 对空间索引支持在 InnoDB 上也已经成熟。注意:空间索引不支持 NULL,建表时空间列都要 NOT NULL,查询用 MBRContains / ST_Distance_Sphere 这类函数而非普通字段比较。

5.3 BIT、BOOL、ENUM 的 ORM 映射核对

一个容易炸的应用层坑:ORM 框架(MyBatis、Hibernate、JPA)对 MySQL 数据类型的映射。

  • Java 的 Boolean 对应 TINYINT(1) 没问题,但如果你把 TINYINT 误映射到 Boolean,值 2 会被转成 true 或者直接报错;
  • Java Long 对应 BIGINT,Integer 对应 INT/INT UNSIGNED;用 INT 存雪花 ID 一定溢出,这不需要解释;
  • Python 的 datetime.date 对应 DATE,datetime.datetime 对应 DATETIME,字符串日期传入时框架容易出时区解析问题;
  • 枚举字段 ENUM 在 MyBatis 里如果不做 TypeHandler,默认按字符串处理,插入 ENUM 的值超出定义列表时直接报错。

建议建表时同步输出一份类型与 ORM 映射对照文档,哪怕就是 README 的表格。这比上线后扒数据修复强一百倍。

6. 避坑战场:类型引发的典型故障全家桶

6.1 隐式转换:性能杀手的头号种子

这是最容易被忽略的坑。MySQL 中当字符串列与数值列比较,或者字符串列与日期比较时,会发生隐式类型转换。最常见的是: where varchar_code = 12345,MySQL 会尝试把 varchar_code 转成数字来比较,等于对列上加了 CAST 运算,索引直接失效,全表扫描。

实例:用户表 user_id 存的是 VARCHAR(32)(外系统 ID),业务侧 Java 里传了个 Long,SQL 就变成 WHERE user_id = 1234567890123456789。这列原本有唯一索引,但“字符串转数字”导致索引失效,一个本该 0.1ms 的查询跑到 3 秒+,整个接口拖垮。改法就是 SQL 中的参数严格按列类型传:VARCHAR 列就传字符串。

还有数值列反着来:WHERE id = '123abc' 这种,MySQL 会把字符串转换成数值,取前导数字 123,能匹配到 id=123 的行,看起来“SQL 没报错还挺智能”,实际很多脏查询正则都抓不到。所有传给数据库的 ID、状态码,应用层先统一做好类型校验和转换。

6.2 大表改类型的痛苦:ALTER 之前想清楚

如果一开始没设计好,后来要 ALTER TABLE ALTER COLUMN,可能触发表重建或者 Online DDL。InnoDB 的 ALTER 在 5.7+ 很多是 ALGORITHM=INPLACE,但改变类型长度超过阈值、或者修改为 TEXT、增加默认值等,仍可能触发 COPY,期间占用双倍磁盘、IO 打满、锁表风险。几千万行大表改一个类型,深夜跑 40 分钟乃至个小时都很正常。

我的经验:上线前把类型设计评审做成强制项。实在要改,务必评估:

  • 预估表行数与磁盘容量;
  • 使用 pt-online-schema-change 这类工具分批 DDL,降低锁风险;
  • 回填数据要灰度:先用 JOIN 统计新旧类型转换失败的行数,例如把 VARCHAR 转 BIGINT 时会遇到非数字串,这一步不做必然失败。

6.3 前缀索引:能救 TEXT,但别乱用

不能给 TEXT/BLOB 建完整索引时,前缀索引是常见方案。例如:

ALTER TABLE articles ADD INDEX idx_content (content(20));

但也要理解原理:索引里只存前缀字符,排序时先按前缀,相同前缀再回表来对比剩余内容。如果前缀太短,区分度太低,优化器可能直接放弃索引。选前缀长度至少要覆盖 95% 以上的高选择性组合,可以通过 COUNT(DISTINCT LEFT(col, N)) 比例来验证。VARCHAR 上也可以建前缀索引,但一般只有该列超长(比如很长的 URL)才有必要。

6.4 DBA 视角:建表自检清单

最后分享一张我自己实战中经常使用的建议清单,每次写 DDL 前过一遍:

检查项正确做法
数字主键有多大超过 21 亿选 BIGINT,不要 INT+UNSIGNED 赌上限
金额/费率精度一律 DECIMAL,别用 FLOAT/DOUBLE
状态字段枚举化稳定小枚举用 ENUM/TINYINT,不稳定大类用 VARCHAR(32) 加字典
字符串长度能短则短,联合索引字段尤其谨慎
日期时间新库默认 DATETIME,秒粒度,少用 TIMESTAMP
时区配置统一东八区,连接串参数显式声明 serverTimezone
大字段隔离TEXT/JSON 不进主表或者列表查询禁用
ORM 映射核对 BOOLEAN、LONG、DATE 在框架层的行为

其中“大字段隔离”是我近年感受最深的一条。我有个项目把一条 JSON 扩展字段直接放主业务表,平时列表查询全走了 select *,每次把几百 KB 的 JSON 拖出来过滤,内存暴涨。后来把 JSON 拆出去放在副表,用主键关联懒加载,性能直接翻倍。类似的经验:不要把主表和可有可无的重数据绑死,宁可拆表也别偷懒。

还有个通用的口诀:能用整型表达的绝不用字符串,能用定长表达的绝不用变长,能用小类型表达的绝不用大类型。说白了,建表时给每个字段三秒想一下“它未来 3 年最大会变成什么样”,基本不会错到哪里去。

数据类型的坑不像慢 SQL 那样容易在压测时暴露,往往要等数据量涨起来、业务扩展之后才爆发。设计期多花十分钟,后面省的是磁盘、索引、查询上无数个通宵。希望大家下次写 CREATE TABLE 的时候,都先想想自己选类型的理由,而不是顺手抄上次的模板。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 9:30:33

鸿蒙Flutter环境配置:dart_dotenv适配踩坑与替代方案

先把结论放前面&#xff1a;dart_dotenv 这个库在鸿蒙 Flutter 工程里并不是“复制粘贴就能跑”&#xff0c;真正折腾人的地方在于 .env 文件根本不在它能读取的位置。这篇博文把适配过程、踩坑记录和三种替代方案一次性讲清楚&#xff0c;适合正在把 Flutter 工程往鸿蒙端迁移…

作者头像 李华
网站建设 2026/10/2 9:30:12

MySQL 8.0 WITH AS 语法详解:从子查询到递归CTE的实战指南

写 SQL 写到想摔键盘&#xff0c;十有八九是栽在子查询嵌套上。我说的不是 WHERE 里面简单加个 IN&#xff0c;而是 FROM 里套一层、外面再套一层&#xff0c;三层起步那种意大利面式写法。前阵子接一个报表需求&#xff0c;逻辑其实不算复杂&#xff1a;先按部门算平均工资&am…

作者头像 李华
网站建设 2026/10/2 9:28:33

用Deepseek开发丧尸射击肉鸽游戏:Token成本与PyGame实战

都说 2025 年什么工程问题最难估&#xff1f;Token 账单绝对算一个。标题里那句“使用 Deepseek 花费 49 亿 Token 打造丧尸射击肉鸽”&#xff0c;先不较真是真实数据还是夸张梗&#xff0c;它起码戳中了两件事&#xff1a;第一&#xff0c;大模型辅助开发一个可玩的游戏已经不…

作者头像 李华
网站建设 2026/10/2 9:28:29

Excel AVERAGEIFS函数详解:多条件平均值计算实战指南

在Excel里&#xff0c;像AVERAGEIFS这种函数&#xff0c;表面上只是个求平均值的工具&#xff0c;实际用起来却特别能体现“条件思维”。我在处理销售数据、成绩统计、费用分析时&#xff0c;靠它解决的多条件平均值计算问题&#xff0c;比用其他方案都要快。这篇指南就把它从语…

作者头像 李华
网站建设 2026/10/2 9:27:59

Python实战:从零搭建旅游推荐系统的完整路线

学完 Python 基础语法之后&#xff0c;我一度陷入很典型的迷茫&#xff1a;代码能看懂&#xff0c;教程跟得住&#xff0c;但真让我自己搭一个项目&#xff0c;不知道从哪下手。后来我逼着自己做了个完整的旅游推荐系统&#xff0c;才真正把 pandas、向量化、相似度计算、离线评…

作者头像 李华
网站建设 2026/10/2 9:27:23

Spring扩展点实战:从BeanPostProcessor到配置加密与动态注册

搞后端这么多年&#xff0c;你迟早会遇到一个逃不掉的场景&#xff1a;框架写好了&#xff0c;业务也要往上堆&#xff0c;但代码就是不能全塞在 Service 里。我们组的项目从单体到微服务&#xff0c;经历了各种“大泥球”改造&#xff0c;最后把核心的流量治理、数据脱敏、配置…

作者头像 李华