尧图精选

MySQL数据类型选型指南:避开索引失效与精度陷阱

🕒 发布时间:2026/10/2 0:07:55 📁 来源:尧图网络
“MySQL 数据类型”五个字看着像基础课里最平平无奇的一节但我在生产环境翻过太多因为类型选错而埋雷的表了金额字段用 FLOAT 存对账对到怀疑人生status 字段用 VARCHAR(50) 存“0”“1”“2”索引等于是白建手机号用 BIGINT 存用户填了个 010 开头的座机前导零直接丢了时间字段清一色 VARCHAR排序按字典序硬生生把“2023-9-1”排在“2023-10-1”后面。这些问题的根源都在建表那一刻没把数据类型当回事。这篇文章想聊透的就是 MySQL 数据类型这件事。不打算逐条背手册而是讲清楚每种类型在存储引擎里怎么存、比较规则是什么、选型时该盯住哪几个点再给出一张订单表的完整建表实例、类型和主流开发语言的映射、常见线上事故排查方法以及面试高频问题。适合所有跟 MySQL 打交道的人写业务 SQL 的后端、做数据同步的 BI、刚入门想系统补一遍的运维以及准备面试的候选人。1. 为什么数据类型选型决定一张表的“后半生”很多人以为建表时把类型随便定一下后续反正能 ALTER TABLE 改。这话对了一半小表随便改几百万上千万行的大表改一个列类型可能要 rebuild 整张表锁表期间业务写入全停。更麻烦的是改完之后数据校验、应用兼容、历史数据清洗都要跟着做成本远远超过建表时多想三分钟。1.1 页与行类型越小页密度越高InnoDB 的默认页大小是 16KB可以通过 innodb_page_size 参数调整但绝大多数系统按默认值跑。数据页是 InnoDB 读写磁盘的最小单位B 树的每个节点对应一个页。一个页能放多少行取决于行均长而行均长主要由列类型决定。举两个极端例子全是 TINYINT 或小 VARCHAR 的表单行可能只有几十字节一个 16KB 页能装几百上千行全是 LONGTEXT、JSON 大字段的表单行轻松超过 1KB一个页可能只放十几行。聚簇索引的叶子节点存整行二级索引的叶子节点存主键值两者都吃页。字段越宽同样的页里能扫到的行越少内存命中率越低磁盘 I/O 次数越往上飙。所以选型第一条直觉能用小类型就不要用大类型能用定长就不要用变长。别小看这一两个字节的差距一亿行就是几百 MB 甚至更多的空间差距还会连带让二级索引体积翻倍缓冲池里能缓存的数据量肉眼可见地缩水。提示对 InnoDB 来说16KB 页和索引性能是互相绑定的。任何“无所谓反正多几个字节”的类型随意选择在千万级行数上都会放大成明显的性能问题。1.2 三个维度空间、语义、比较规则我选类型时习惯对着三个问题过一遍。第一个问题是值域。这个字段的最大值和最小值是否确定用户 id、订单 id 这类长期只会涨不会缩的值要判断未来五年可能到多少量级。一次性活动表里的临时自增 id可能 INT 足够核心业务表主键直接上 BIGINT 更稳妥。第二个问题是语义。这个字段是数值还是要计算是编码标识不参与运算还是纯文本内容手机号存数字就是这个问题的经典翻车案例。手机号、身份证号、订单号这一类“看起来像数字但其实只是编号”的数据用整数类型存会丢前导零、会参与无意义的算术还会在排序时产生诡异结果。第三个问题是操作。这个字段会参与什么操作排序、去重、等值查询、范围查询还是 JOIN不同类型比较规则不同比如 ENUM 按内部索引值排序而不是按字符串值排序VARCHAR 和 INT 比较会触发隐式转换可能直接把索引废掉。想清楚这三个点类型基本就定下来了剩下的只是查一下具体类型的边界和细节。2. 六大类内建类型逐一拆解含易错点这一章是干货重点。把整数、小数、字符串、日期时间、JSON 和空间类型过一遍穿插实际踩坑经验。每个类型我都会给出“到底该怎么用”的结论而不是罗列手册。2.1 数值类型整数覆盖主流小数注意精度先给整数家族一张表类型字节数有符号范围无符号范围TINYINT1-128 ~ 1270 ~ 255SMALLINT2-32768 ~ 327670 ~ 65535MEDIUMINT3-8388608 ~ 83886070 ~ 16777215INT4-2147483648 ~ 21474836470 ~ 4294967295BIGINT8-2^63 ~ 2^63-10 ~ 2^64-1实际业务里 INT 和 BIGINT 出现频率最高。TINYINT 非常适合状态位、开关位SMALLINT 适合枚举值较少但类型要明确的场景MEDIUMINT 用的人少但存中等量级 ID 比 INT 省 1 个字节某些场景能派上用场。关于 INT(11) 是全网最典型的误解。INT 本身占 4 字节范围固定跟括号里的数字没关系。括号里只是“显示宽度”配合 ZEROFILL 用零填充位数比如 INT(5) 存 12 会显示成 00012但这不影响存储和取值范围。MySQL 8.0.17 开始已经把整数显示宽度标记为废弃属性所以看到历史代码里的 INT(11) 不用太纠结它既不表示能存 11 位也不表示只能存 11 位。小数的坑比整数多得多FLOAT 占 4 字节约 7 位有效数字DOUBLE 占 8 字节约 15 位有效数字。它们是浮点数用二进制近似表达十进制小数0.1 这种值存进去就是不精确的。DECIMAL(M,D) 是定点数按十进制精确计算适合金额、库存、费率等对精度敏感的数据。M 是总位数最大 65D 是小数位数最大 30。DECIMAL(12,2) 的意思是整数部分最多 10 位、小数 2 位最大能存 9999999999.99。看到有人用 FLOAT 存金额我都会直接拦下来。写过支付的同学都知道浮点误差在对账时是灾难级的0.1 加 0.2 和 0.3 不相等线上为了排查这种误差要搭进去大量工时。金额类字段老老实实用 DECIMAL即使多占几个字节也不亏。布尔值方面MySQL 没有单独的 BOOL 类型官方建议用 TINYINT(1)1 表示真0 表示假。不要真建一个 BOOL 列MySQL 只是把 BOOL 当作 TINYINT(1) 的语法糖。2.2 字符串类型CHAR、VARCHAR 的边界要拿捏字符串是重灾区尤其是 VARCHAR(255) 一统天下的那张表。CHAR(M) 是定长字符串M 范围 0~255 字符空间固定存取时如果长度不够会在尾部补空格取出时默认去掉尾部空格。适合 MD5、订单号这类长度几乎不变的字段。VARCHAR(M) 是变长字符串M 表示最多字符数需要额外 1~2 字节记录长度适合长度波动大的字段比如备注、地址、名称。VARCHAR(255) 和 VARCHAR(256) 在旧版 MySQL 下的差异是出了名的。旧版本 InnoDB 的索引单列最大字节数限制是 767 字节utf8mb4 下一个字符最多 4 字节所以 VARCHAR(191) 是那个年代常见索引长度上限。到 8.0默认动态行格式 DYNAMIC单列索引字节上限提升到 3072VARCHAR(255) 在 utf8mb4 下占 1020 字节没问题VARCHAR(256) 在 utf8mb4 下占 1024 字节也没超过 3072。但联合索引累计不能超 3072 字节索引列多了就得精打细算。很多老开发坚持用 255 而不用 256源自这套索引限制的历史但不代表 256 完全不可用要看具体索引上下文。我的建议是普通业务字段别往大了给VARCHAR(64) 能解决的别上 VARCHAR(255)行均长直接决定页密度和索引体积。TEXT 系列是真正的“文本堆”类型TINYTEXT 最大 255 字节TEXT 最大 64KBMEDIUMTEXT 最大 16MBLONGTEXT 最大 4GB。注意两点TEXT 类型不能有默认值而且大字段内容容易落到溢出页查询时要额外 I/O能用 VARCHAR 就别碰 TEXT单行查询如果碰了大字段很容易触发随机的行读取。ENUM 和 SET 我现在用得很克制。ENUM 适合值集合非常稳定、且明确不会经常扩展的场景比如星期、性别。但 ENUM 的排序不是按字母顺序而是按内部索引值排序中间插入新值要改表结构很麻烦。SET 是多个枚举用逗号组合的集合最多 64 项业务里真正合适的场景不多。我更建议状态位用 TINYINT 或 SMALLINT可读性交给代码层和字典表扩展性和灵活度都好得多。字符集必须单独提一句。MySQL 的 utf8 在 5.5 之后实际上是 utf8mb3只支持三字节编码真正的 Emoji 四字节必须用 utf8mb4。建库建表时我统一用 utf8mb4排序规则在 8.0 下习惯用 utf8mb4_0900_ai_ci老版本用 utf8mb4_general_ci 或 utf8mb4_unicode_ci。如果线上已经全是 utf8 编码遇到 emoji 入库报错就是字符集问题后续改库表字符集代价不小所以建表当天就要把字符集钉死。2.3 日期时间类型DATETIME 与 TIMESTAMP 的选择MySQL 的日期时间家族有 DATE、TIME、DATETIME、TIMESTAMP、YEAR。DATE3 字节范围 1000-01-01 到 9999-12-31只存日期。TIME3 字节范围 -838:59:59 到 838:59:59可以存时间间隔甚至超过一天。DATETIME8 字节范围 1000-01-01 00:00:00 到 9999-12-31 23:59:59存日期和时间不受时区影响存什么就是什么。TIMESTAMP4 字节范围 1970-01-01 00:00:01 UTC 到 2038-01-19 03:14:07 UTC存储时把会话时区的时间转成 UTC读出来再转回当前会话时区。受时区影响也受 2038 年问题影响。YEAR1 字节范围 1901~2155传统场景中用得不多。业务上最常用的选择是 DATETIME 还是 TIMESTAMP。我的经验很简单单一机房、单一业务时区优先 DATETIME因为它直观、范围大、没有时区换算的坑跨时区业务优先 TIMESTAMP因为可以跟随会话时区自动转换。要注意 TIMESTAMP 的 2038 问题对很多系统和业务合同年限来说2038 年其实不算远。另一个常见坑是 JDBC 连接串里的 serverTimezone。Spring Boot 项目连 MySQL 后时间差 8 小时、13 小时、14 小时八成是驱动、会话时区、JVM 默认时区三者没对齐。用 DATETIME 类型时只要连接串写了serverTimezoneAsia/Shanghai一般能稳定。用 TIMESTAMP 时应用服务器时区和数据库会话时区不一致出来就是错乱的。排查方式很简单执行SELECT NOW();和SELECT session.time_zone;看看结果如果不是当前时间先SET time_zone 08:00临时验证一下。2.4 JSON 类型好用但有边界MySQL 从 5.7 开始支持 JSON 类型内部不是简单字符串而是二进制格式可以高效读取和校验 JSON 文档。8.0 里 JSON 已经比较成熟可以配合生成列和函数索引使用。但 JSON 不能无脑用。第一JSON 列参与不了常规索引要在 JSON 上做查询条件得先建生成列再对生成列加索引。第二JSON 数据的变更要整字段重写频繁更新的字段放 JSON 里性能并不好。第三JSON 里的字段类型是宽松的金额、数量这类敏感数据塞进 JSON校验成本很高容易出现脏数据。什么时候适合 JSON典型场景是字段集合不确定、结构需要随版本演进比如前端表单配置、第三方回调报文、埋点扩展属性。固定的核心业务字段老老实实用真实列别为了一时的灵活把整张表变成一个“大 JSON”。空间类型GEOMETRY、POINT、LINESTRING、POLYGON 等在 8.0 里已经支持空间索引主要服务于地理围栏、POI 查找等场景。日常业务库用到的不多但如果你做地图相关应用绝对比把经纬度塞成 VARCHAR 再用范围查询专业得多。3. 实战一张订单表把类型选明白理论说再多不如直接建一张表。下面这张订单表几乎覆盖了普通业务系统里最常见的字段类型主键、业务单号、金额、状态、时间、备注。我会逐字段解释为什么这么选。3.1 订单表的 DDL 与字段逐项解析CREATE TABLE t_order ( id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 订单ID, order_no VARCHAR(32) NOT NULL COMMENT 订单号, user_id BIGINT UNSIGNED NOT NULL COMMENT 用户ID, total_amount DECIMAL(12,2) NOT NULL DEFAULT 0.00 COMMENT 订单总金额, status TINYINT NOT NULL DEFAULT 0 COMMENT 订单状态0待支付 1已支付 2已发货 3已完成 4已取消, pay_time DATETIME NULL DEFAULT NULL COMMENT 支付时间, remark VARCHAR(500) NULL DEFAULT NULL COMMENT 备注, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), KEY idx_user_id (user_id), KEY idx_order_no (order_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_0900_ai_ci COMMENT订单表;逐个字段说id用 BIGINT UNSIGNED。电商订单量很容易过千万INT 的 21 亿上限看着大但做主键一旦用光后续 ALTER 的成本是灾难性的。用无符号等于把上限翻倍而且是自增主键不存在负数场景省得以后再改。order_no用 VARCHAR(32) 而不是 BIGINT。订单号通常是“业务前缀日期序列号”的格式比如 DD20240901120001它只用于等值查询和展示不参与加减乘除存字符串才是语义正确。有人为了省几个字节把它拆成数字反而丢失可读性和扩展性。total_amount用 DECIMAL(12,2)最大 9999999999.99覆盖绝大多数业务。订单金额可能过百亿时把总位数加大到 DECIMAL(16,2) 即可绝不用 FLOAT 或 DOUBLE。status用 TINYINT 不用 ENUM。状态机几乎一定会新增状态TINYINT 扩到 127 个状态没有 DDL 成本ENUM 改一次就要 ALTER。状态的可读性用代码常量或字典表解决。pay_time用 DATETIME NULL。支付动作不是所有订单都有所以允许 NULL选 DATETIME 而不是 TIMESTAMP避免时区干扰。如果业务明确跨时区TIMESTAMP 也说得通但单库单应用我首选 DATETIME。remark用 VARCHAR(500) 而不是 TEXT。500 字够一般备注用TEXT 没有默认值、还有行外溢出风险。需要更长再上 MEDIUMTEXT。created_at和updated_at用 DATETIME 加自动填充。DEFAULT CURRENT_TIMESTAMP 和 ON UPDATE CURRENT_TIMESTAMP 是 5.6.5 之后的主流写法应用层完全不用维护这两个字段。这张表的关键设计思路是能确定的宽度不给多的能用整型表达的状态不给字符串能精确定义的金额不用浮点。3.2 与 Java、Python 等语言的类型映射后端同学查问题最多的是数据库类型和 Java、Python 类型对不上。给一张常用映射表MySQL 类型Java 类型Python 类型注意点TINYINT / SMALLINTIntegerintPython 的 int 不区分长度INTIntegerintJDBC 返回 Integer注意 NPEBIGINTLongint超 2^31 时 Java 必须用 LongDECIMALBigDecimalDecimalPython 用 Decimal 保证精度FLOAT / DOUBLEFloat / Doublefloat仅非精确计算场景使用CHAR / VARCHAR / TEXTStringstrJDBC 里 TEXT 也是 StringDATELocalDatedatetime.date8.0 驱动推荐 LocalDateDATETIME / TIMESTAMPLocalDateTimedatetime.datetime注意时区参数TIMELocalTimedatetime.time—JSONString Jackson/Gsondict / json驱动通常转成字符串BOOLEANTINYINT(1)Booleanbool注意 JDBC 可能返回 0/1用 pandas 读 MySQL 时也要注意默认会把 INT 解析成 int64把 DECIMAL 解析成 object 或 Decimal写回时不做类型管理容易漂移。稳妥做法是在 SQL 层先把 DECIMAL 转成字符串或者在 pandas 里用 astype 指定类型。Python 没有 short、int、long 的区分但 MySQL 的 INT 和 BIGINT 边界不同写入时超过 INT 范围会直接报 Out of range value所以读到的 BIGINT 如果要从 Python 写回另一个表目标列也必须是 BIGINT。前端联调更要想清楚JS 的 Number 是双精度浮点超过 2^53 的整数会丢精度。所以 BIGINT 主键返回给前端时最好转成字符串否则订单 id 这种很大的数字前端拿过去直接变了样。这也是很多接口文档里主键字段直接用 string 的原因。3.3 旧表类型不合理这样低风险改造拿到一张历史表发现字段类型选崩了不能直接 ALTER 一把梭。我自己常用的低风险改造路径是新增新类型列比如ALTER TABLE t_user ADD COLUMN mobile_str VARCHAR(20) NULL COMMENT 手机号字符串 AFTER mobile;。分批回填。使用UPDATE t_user SET mobile_str CAST(mobile AS CHAR) WHERE mobile_str IS NULL AND id BETWEEN ? AND ?;一次别 update 全表分批限流避免长事务锁表。应用程序切换读写新列灰度验证一段周期。确认无误后再物理删除旧列。删除列会重建表务必安排在低峰期执行并提前评估磁盘空间和主从延迟。如果是只读报表库可以先用 SELECT CAST 临时转换。MySQL 里两个常用的显式类型转换函数SELECT CAST(123 AS UNSIGNED); SELECT CONVERT(2023-01-01 00:00:00, DATETIME);CAST 和 CONVERT 功能类似CAST 更标准CONVERT 支持指定字符集比如CONVERT(name USING utf8mb4)。处理乱码和类型转换都是这两个函数的日常用法。但要注意显式转换能改变类型不能解决数据本身的脏值。比如一个 VARCHAR 列里既有 123 又有 abcCAST 到整数时非数字开头的内容会变成 0WHERE 条件里这么写反而选出错误行。4. 常见类型相关的坑与排查实录这一章整理的是我在线上实际踩过、帮别人处理过的坑。每个坑都尽量给到快速验证和修复的方法。4.1 隐式类型转换索引失效的元凶数据类型的坑有一多半出在隐式转换上。MySQL 里当字符串和数字比较时字符串会转成数字当两个类型不同的列做 JOIN 时也会有一方被隐式转换。最简单的例子EXPLAIN SELECT * FROM t_user WHERE mobile 13800138000;如果 mobile 是 VARCHAR这个查询会把 mobile 列隐式转成数字再比较无法直接走 mobile 上的普通索引结果就是全表扫描。正确写法是mobile 13800138000让字符串和字符串比较。开发环境数据量小看不出问题线上几百万行时立刻原形毕露。类似的现象还有date_col 是 DATETIME 时WHERE date_col 2023-01-01字符串被转成日期一般没问题但如果列本身是 VARCHAR别指望索引。两个表 JOIN 时一边 utf8mb4、一边 latin1连接列字符集不同MySQL 会做隐式转换导致索引失效。MyBatis 或手拼 SQL 时参数类型和列类型不一致也容易触发。排查方法很简单遇到慢查询先 EXPLAIN看 type 列是不是 ALLExtra 里有没有 Using where再看 possible_keys 和 key 是否为空。如果是隐式转换把查询条件里的常量或参数类型改成和列一致即可。提示字符集不同导致的隐式转换比较隐蔽需要 SHOW CREATE TABLE 两张要 JOIN 的表看 JOIN 字段的 CHARSET 是否一致。不一致就统一字符集或对其中一边CONVERT(col USING utf8mb4)但转换之后通常也无法走索引。4.2 排序规则带来的大小写与排序差异字符串的比较和排序由 collation 决定。utf8mb4_general_ci 和 utf8mb4_unicode_ci 不区分大小写utf8mb4_bin 区分大小写且按二进制排序。8.0 默认的 utf8mb4_0900_ai_ci 不区分重音和大小写。如果业务需要区分大小写比如用户名校验要么建表时指定 utf8mb4_bin要么查询时加 BINARY。最坑的场景是同一个字段在不同表里用了不同 collationJOIN 时直接报 Illegal mix of collations 错误。遇到这种错误检查两边表的排序规则即可。另一个容易被忽略的点用 VARCHAR 存数字时排序走的是字典序。10 会排在 9 前面100 排在 99 前面。业务如果要对这类字段排序要么查出来在代码层转成数字要么建表时就规划成 INT。表结构里出现“年度”“批次号”这种前缀数字要特别留意排序语义。4.3 时区和 TIMESTAMP 的连环坑TIMESTAMP 的坑大多在时区。MySQL 会话时区可以用SET time_zone 08:00修改全局用SET GLOBAL time_zone 08:00。如果连接串指定了 serverTimezone驱动会按指定时区解析产生偏差的现场我见过太多次。最典型的症状是程序里 new Date() 传入的时间存到库里变成昨天 16:00。原因就是应用时区是 UTCMySQL 会话时区也是 UTC但业务希望看到北京时间。解法就是统一时区尽量在 JDBC URL 里写死serverTimezoneAsia/Shanghai或者在 MySQL 全局配置里把 default-time-zone 设为 08:00。用 DATETIME 的团队受这个困扰少因为 DATETIME 不做时区换算存什么就是什么。4.4 存储过程、函数里的类型声明也容易翻车如果用到存储过程或函数DECLARE 的变量类型要与表列匹配。典型问题DECLARE v_count INT;然后SELECT COUNT(*) INTO v_count ...当 COUNT(*) 结果超过 INT 上限时会报 OUT OF RANGE 错误又比如DECLARE v_price DECIMAL(10,2)去接 DECIMAL(12,2) 列四舍五入后精度就没了。另外游标遍历时fetch 变量类型和 SELECT 列不一致也可能隐式转换出脏数据。我在排查存量系统的慢存储过程时习惯先把每个 SELECT 的列类型和 DECLARE 变量类型对一遍能解决相当一部分“数据变了但没变对”的问题。4.5 常见问题速查表现象可能原因快速解法中文或 emoji 插入报错表不是 utf8mb4ALTER TABLE ... CONVERT TO CHARACTER SET utf8mb4金额对不上FLOAT/DOUBLE 存储改为 DECIMAL必要时重建列修复数据按数字排序乱VARCHAR 存数字排序时 CAST(列 AS UNSIGNED)后续改 INT时间差 8 小时时区未统一serverTimezoneAsia/ShanghaiJOIN 报 Illegal mix of collations两表排序规则不同统一 COLLATEBIGINT 给到前端数字变了JS Number 精度不够接口返回字符串状态枚举新增很痛苦ENUM 类型改成 TINYINT/SMALLINT慢查询全表扫隐式转换或字符集不同修条件类型或统一字符集5. 面试题视角与类型相关性能调优清单类型这个话题在面试里出现频率很高但很多人背了答案却不知道为什么。我把高频问题按自己的理解重新梳理一遍。5.1 几个高频 MySQL 类型面试题INT(11) 中的 11 是什么意思 不是存储长度是显示宽度配合 ZEROFILL 使用MySQL 8.0.17 开始已废弃整数显示宽度。DECIMAL 和 FLOAT 的区别 DECIMAL 是定点十进制精确FLOAT 是浮点二进制近似有误差。金额、精度敏感数据用 DECIMAL。为什么手机号不建议用 BIGINT 手机号是“编号”而不是“数值”可能带前导 0不参与算术用 VARCHAR(20) 最合适。用 BIGINT 会丢前导零语义也不对。DATETIME 和 TIMESTAMP 怎么选 TIMESTAMP 4 字节、受 2038 年限制、自动按会话时区转换DATETIME 8 字节、直观无时区干扰。单时区业务用 DATETIME跨时区业务用 TIMESTAMP 更省心。VARCHAR 和 TEXT 怎么选 VARCHAR 可以有默认值、长度可控、参与索引方便TEXT 默认值受限、有行外溢出能不用就不用。状态位为什么用 TINYINT 而不用 ENUM ENUM 修改要 ALTER排序按内部序号统计和扩展都不方便TINYINT 语义由代码层维护扩展无 DDL 成本。5.2 类型相关的性能调优清单最后给一个务实清单照做基本能减少一半由类型引发的性能事故。主键和二级索引列尽量小。主键本身会被每个二级索引复制一份BIGINT 作主键时所有二级索引都要背着这份额外空间。能用 NOT NULL 的字段不要留 NULL。虽然 8.0 对 NULL 的优化已经大幅进步但从语义和维护成本看明确表示“肯定有值”的字段就写 NOT NULL。索引字段长度要克制。长字符串索引可以用前缀索引比如INDEX idx_mobile (mobile(11))对纯前缀场景依然实用。避免在 WHERE、JOIN、ORDER BY 条件上做函数或隐式转换。WHERE CAST(col AS SIGNED) 123这种写法基本让索引报废。大字段不要塞进索引。JSON、TEXT、BLOB 类型不适合直接建索引用生成列提取标量再加索引。插入更新频繁的扩展字段不要用 JSON 一把梭。JSON 整字段重写带来的代价比多建一张扩展表要高得多。最后分享一个我查线上表的习惯拿到任何一张不熟悉的表第一件事永远是 SHOW CREATE TABLE把每一列的类型、字符集、索引结构过一遍。类型说明白了这张表怎么存、怎么写、怎么查基本就清楚了一大半。数据类型的功夫花在建表那一刻回报却在之后的每一次查询和维护里。下次建表时多想三秒钟后面能少熬很多个夜晚。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →