资讯动态

MySQL数据库入门:一文吃透数据类型选型与底层存储原理

发布时间:2026/10/11 3:16:39 来源:尧图企业网站定制
1. 为什么我建议你从MySQL开始学数据库如果你正打算进入后端开发、数据方向或者只是想把手上的项目做得更完整一点那数据库几乎是绕不开的一关。而在所有数据库里MySQL又是最适合作为第一门课来学的它免费、跨平台、资料多、社区活跃而且绝大多数中小型网站、后台系统、App服务端都在用它。不夸张地讲只要你会写CRUD基本就拿到了大多数后端岗位的入场券。这篇文章定位是“从零起步”系列的第一章主要面向完全没接触过数据库的读者所以我不会一上来就甩给你一堆DDL语法而是先带着你建立三个底层概念MySQL到底是什么、一条SQL是怎么跑起来的、以及最重要的——它的内部数据类型到底是怎么存储的。数据类型这个东西表面看只是“建表时填个字段类型”但实际影响的不只是存储空间还包括查询效率、索引命中、排序规则、甚至并发场景下的锁竞争。我见过太多人栽在类型选择不当上比如用VARCHAR存手机号、用DOUBLE存金额、用TEXT存超长内容导致临时表爆炸。这些坑我希望你在建表之前就避开而不是线上出了事故再回来改表。2. 说句实在话MySQL真的不难难的是你对它不够了解很多人第一次接触MySQL是在课程里跟着老师敲了两条SELECT语句然后就觉得自己“会数据库”了。等真正做项目才发现什么索引失效、慢查询、死锁、数据不一致全是没建立好底层认知挖的坑。2.1 MySQL到底是干什么的通俗地讲MySQL就是一个帮你有条理地存数据、取数据的管家。你没有它的时候数据要么堆在日志文件里要么散落在Excel表里查起来全靠肉眼找。有了MySQL你可以把一张张数据表当成Excel的Sheet来理解——有行、有列、有约束但它比Excel强的地方在于支持高并发读写、支持事务、支持SQL标准化查询、支持索引加速还有权限控制。这里有一个很重要的认知MySQL并不是把数据原原本本存成一个个独立的文件让你自己翻的。它有自己的一套存储引擎机制默认的InnoDB引擎会把数据按页结构组织起来每页默认16KB有页头、页尾、记录槽位、目录甚至还有预读和缓冲池。你写的每一条INSERT最终都会被转化成“在某个数据页的某个槽位里写入一条记录”的动作。理解了这一层你后面学习索引、学习执行计划时就会有一种豁然开朗的感觉。2.2 一条SQL在MySQL里到底经历了什么在深入数据类型之前我建议你先建立一条SQL的完整生命周期概念。当你敲下一行查询语句MySQL大致经历这么几个阶段连接器先校验你的用户名密码分配一个线程来处理你的请求。查询缓存8.0已移除老版本里如果命中了完全相同的SQL会直接返回缓存结果所以以前经常有人吐槽“改了数据但查询结果没变”就是缓存没失效。8.0之后直接砍掉了这个模块因为缓存命中率太低还影响并发。解析器做词法分析和语法分析。说白了就是把你的SQL拆成“关键字、表名、字段、条件”等零件再按语法规则检查顺序对不对。字段名写错了、关键字拼错了都是在这里报错。优化器决定以什么顺序、走哪个索引、用哪种连接方式去执行你的SQL。同一个SQL在这里能被优化出好几种执行方案选错一个性能天差地别。执行器真正去存储引擎里取数据、做过滤、回表、排序、返回结果。所以你后来学的索引优化、EXPLAIN分析本质上是干预第4步“优化器”的决策。现在你只要记住这个流程就够了。2.3 我们学习MySQL的路线应该怎么规划我建议你按照“认知建立 - 单表操作 - 多表关系 - 约束设计 - 索引原理 - 事务与锁 - 性能调优 - 运维基础”这条主线去走。第一章就老老实实把地基打好知道MySQL是什么、怎么装、有哪些存储引擎、数据类型怎么选。地基没打好的话后面学JOIN也好、学索引也好总会遇到“好像懂了但实际不会用”的尴尬。3. 安装MySQL前先想清楚这几个问题虽然安装MySQL的方法网上已经有很多教程但我还是想花一点篇幅讲讲几个容易被忽略的决策点。因为装错了版本、配错了字符集后面每一节课都会带着隐患走。3.1 版本选择5.7还是8.0如果你只是自己学习直接选MySQL 8.0别犹豫。8.0相比5.7有太多关键改进说几个跟数据类型直接相关的字符集默认从utf8变成了utf8mb4以前创建数据库不指定字符集的话5.7默认是latin1中文根本存不了必须自己手工指定。8.0就省了这一步。8.0移除了查询缓存很多初学者以前会遇到“为什么UPDATE了查询结果还是不变”的怪问题现在少了一个坑。整数类型的显示宽度在8.0.17之后已经废弃比如INT(11)这种写法虽然兼容但已经不再推荐这意味着网上的老教程很多写法已经过时了。窗口函数、公用表表达式CTE这些高级功能8.0支持得更完整后面学数据分析时你会感谢自己选了新版本。3.2 安装时顺手把这三个配置做好安装过程我不展开讲重点说三个新手很容易忽视的地方。第一个是 root 密码。本地学习环境尽量别设太复杂但也不能空密码因为很多图形客户端工具连接时对空密码处理不好容易让你误以为连接不上。第二个是端口。MySQL默认3306如果你机器上已经跑着别的服务占了端口或者你以后要同时开MySQL 5.7和8.0建议把端口区分开来比如3306、3307这样。Windows下装完如果服务启动不了最常见的坑就是端口被占用排查半天发现是别的东西在用。第三个是环境变量。很多人装完MySQL后在命令行敲mysql提示找不到命令这是因为没有把MySQL的bin目录加到PATH里面。在Windows里把安装路径下的bin目录追加到系统环境变量即可Linux下一般用包管理器装完会自动配好。3.3 终端进去了先执行这四条命令安装完成后我建议你进入MySQL命令行后先执行下面这组命令确认自己的库环境没问题-- 查看版本 SELECT VERSION(); -- 查看默认字符集和排序规则 SHOW VARIABLES LIKE character_set_database; SHOW VARIABLES LIKE collation_database; -- 查看支持的存储引擎 SHOW ENGINES;正常的情况下你的版本应该显示8.x数据库默认字符集是utf8mb4默认排序规则是utf8mb4_0900_ai_ci存储引擎列表里InnoDB那一行的Support列是DEFAULT。这四条命令是在确认你起跑的姿势对不对。如果你装完发现默认字符集还是latin1那么你后面建库时就要额外交代一句DEFAULT CHARACTER SET utf8mb4否则存中文就会变成一串问号。4. 深入理解数据类型之前先建立这几个底层直觉数据类型这个词听起来很基础但真正把它吃透的人并不多。我的理解是数据类型本质上是在回答三件事怎么存、存多大、怎么比。4.1 数据类型到底在管什么你可以把一个字段理解成一个小格子数据类型就是给这个小格子设定了边界。它规定了你只能放什么形状的东西、最多放多大体积、以及两个值之间应该按什么规则来比较大小。MySQL里的每种数据类型背后都对应着一套存储格式和比较规则。比如整数类型在存储时直接按二进制补码存字符串类型要按字符集编码成字节序列再存日期时间类型底层存的是某个基准时刻的差值。理解了这一点你就明白为什么数据类型不能随便乱选。比如你用整数类型存手机号手机号是11位数字看起来是数字对吗但一个整数在MySQL里做运算、比较、排序时都会走数值逻辑手机号你根本不需要做加减法而且如果将来要支持国际区号或者86前缀整数就彻底无能为力了。这时候用VARCHAR(20)才是正确姿势。4.2 存储层级从字节到页MySQL最底层的存储单位是字节多个字节组成一条记录行多个行记录组织在一个数据页里默认每页16KB。数据页之间通过双向链表连接页内部有页目录来加速查找。为什么要提这个因为一个字段类型的大小直接决定了一个数据页里能塞下多少行。比如你设计了一张用户表一个字段用VARCHAR(1024)另一个场景用VARCHAR(128)在同一逻辑数据量下前者的数据页数量会明显更多加载到内存缓冲池时占用的空间也更大扫描时的IO开销自然更高。这就是为什么很多建表规范会建议字段能小就不要大能用数值类型就不要用字符串能定长就不要用变长。4.3 类型的归类和选择顺序MySQL的类型大致分为五大家族数值型、字符串型、日期时间型、二进制型、特殊类型JSON、空间数据等。我在实际设计表结构时心里有一条选择优先级能用数值型解决的问题绝不用字符串。数值型里能用整数解决的绝不用小数。字符串里能定长的优先定长不能定长再用VARCHAR。日期时间统一用DATETIME除非明确需要记录时区。大字段一律隔离到扩展表避免撑爆主表数据页。这条优先级是多年踩坑后总结出来的下面我用具体的类型细节来证明它为什么成立。5. 数值类型的拆解不是只有INT和DOUBLE数值类型是MySQL里最简单的类型但正因为简单很多人反而不去深究。我见过的错误用法包括用DECIMAL存订单金额时设置不合理精度导致溢出、用BIGINT存时间戳导致后面没法直接做日期格式化、甚至有人用FLOAT当主键结果因为精度问题把数据搞出脏数据。5.1 整数类型全家族对比MySQL的整数类型一共有五个TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT。它们占用的存储字节数依次递增可表示的范围也递增。类型存储字节有符号范围无符号范围典型用途TINYINT1-128~1270~255状态值、开关量、枚举数字SMALLINT2-32768~327670~65535小计数、年龄、订单数量MEDIUMINT3-8388608~83886070~16777215中等范围的计数INT4-2147483648~21474836470~4294967295主键、外键、常规计数BIGINT8极大极大雪花ID、流水号、时间戳等5.1.1 这个选择为什么重要这里我想强调一个新手常常忽略的点类型的选择不仅是范围问题也是存储和索引性能问题。我举一个真实的例子。某开发者负责一个内部系统给用户表设计了BIGINT自增主键。当时想的是“反正8字节也就多几个G”结果每天写入几百万行跑了一个多月后发现定期数据清理的SQL非常慢。排查下来发现主键越大二级索引叶子节点存的主键值也越大一个16KB的数据页能容纳的索引项就越少获取同样数据需要读更多页。把主键改成INT之后写入和查询性能都有明显提升。所以在设计主键时想清楚未来十年的数据量不要一味求大。另外整数类型还有一个很重要的隐形属性显示宽度Display Width。这是MySQL 5.7及更早版本里的概念比如INT(11)里面的11是指不包含负号的显示位数它不影响存储大小只影响ZEROFILL时展示的位数。在8.0.17之后这个属性已经废弃新写的建表语句不需要再写它。5.1.2 有符号和无符号的取舍默认情况下整数都是有符号的。如果你确定某个字段只存非负数比如年龄、库存数量、订单件数可以考虑加UNSIGNED。这样做的好处是上限翻倍但要注意一点如果两个有符号和无符号的整数字段做JOIN或比较MySQL的优化器有时会因类型不一致导致无法使用索引出现隐式转换。建表时最好统一别一半字段加UNSIGNED一半不加。MySQL 8.0.17之后官方也在逐步弱化无符号整数的推荐度所以我的个人建议是主键和自增列不要用无符号普通业务字段如果确实不会为负可以用但必须全表统一。5.2 小数到底该用FLOAT、DOUBLE还是DECIMAL这是一个老生常谈的问题但我还是想再强调一次钱、金额、精确计量一律用DECIMAL不要用FLOAT或DOUBLE。原因是FLOAT和DOUBLE是浮点数底层采用IEEE 754标准用二进制科学计数法来逼近数值。0.1在十进制里看起来很简单但转成二进制浮点数后是个无限循环小数存储时只能截断所以运算会出现0.10.20.30000000000000004这种结果。DECIMAL则不同它是以字符串形式按位存储的十进制数每一位都精确不存在二进制逼近问题。它的存储结构比较特殊每9个十进制位占4个字节剩余部分按4字节折叠。所以DECIMAL(10,2)实际上整数部分10位其中9位占4字节剩下1位也按4字节算总计8字节小数部分2位占1字节加在一起是9字节。DECIMAL还有一个限制是最大精度65也就是最多65个有效数字。TOP,如果你有个字段精度超过65在建表时会直接报错。所以设计金额字段时通常DECIMAL(10,2)就够日常用了最大到多少亿你可以自己算一下。5.3 布尔类型其实是个整数MySQL里有BOOLEAN和BOOL关键字但本质上它们只是TINYINT(1)的别名。所以不要指望这个字段里只会出现0和1你完全可以往里塞9因为它本身就是一个整数。这一点跟其他数据库不一样很多从其他数据库转过来的程序员会踩到这个坑逻辑查询里WHERE is_deleted 1没问题但如果你展示时用CASE WHEN is_deleted TRUE在某些连接器里TRUE会被当成1可能没问题可如果程序员自己往里面写了2就会莫名其妙出现“已经删除的数据还能查到”的bug。所以我的建议是布尔字段统一约束为0和1写代码时不要依赖隐式转换查询时明确写 1或者 0这样才能避免不必要的坑。6. 字符串类型没那么简单字符集才是理解一切的钥匙字符串类型是MySQL初学者最容易用错的类别。很多人以为CHAR(10)和VARCHAR(10)的区别只是能不能存10个字符但实际上它们的底层存储逻辑完全不同。6.1 CHAR和VARCHAR的底层差异CHAR(N)是定长字符串N的范围0~255个字符。你定义了多少每行就固定占用多少字节。它是通过补齐空格来保证存储长度固定的。读取时会自动去掉尾部空格这也是一个隐藏的坑如果你存密码时末尾有空格用CHAR类型取出来时空格会被干掉密码逻辑直接出问题。VARCHAR(N)是变长字符串N的范围0~65535但这里注意这个65535指的是“最大行字节数”的一部分约束不是单纯的N的字符数。实际最大能存多少字符取决于你选的字符集。比如utf8mb4下一个字符最多占4字节所以一变长字段最多大概16383个字符如果整行多个VARCHAR字段加一起超过65535字节建表会直接报错。VARCHAR的存储结果分为两部分实际数据字节 1~2字节的长度前缀。当数据小于等于255字节时长度前缀占1字节超过255字节时占2字节。这个设计是MySQL为了快速定位一条记录中可变字段的长度而做的。6.2 定长和变长的适用场景实际业务里这两个怎么选我给一个简单的判断标准如果字段内容长度基本固定比如手机验证码、订单号、身份证号、ISBN号用CHAR。如果长度变化较大比如姓名、邮箱、地址、文章标题用VARCHAR。如果一个表里绝大多数字段是CHAR那么整条记录就是定长记录InnoDB扫描起来效率略高如果混着VARCHAR记录是变长的需要使用记录头信息里的变长字段长度列表来解析。我见过一个性能优化的案例把某个统计表里的城市编码字段从VARCHAR改成CHAR同样数据量下表体积缩小、索引页能容纳更多记录查询响应从几十毫秒降到了个位数毫秒。这是因为定长字段让每行记录的位置更可控减少了额外解析。6.3 字符集和排序规则90%的新手都没搞明白字符集决定了字符如何编码成字节排序规则则决定了字符串之间如何排序和比较。MySQL 8.0默认字符集是utf8mb4排序规则是utf8mb4_0900_ai_ci。这里有一个流传已久的坑很多人以为utf8和utf8mb4是一样的。其实在MySQL里utf8是utf8mb3的别名最多只能存储3字节的字符也就是说像emoji这种4字节字符根本存不进去。以前有段时间很多人用utf8结果页面存emoji直接报错或者变成一堆问号。现在直接用utf8mb4就够了。排序规则中的_ai表示accent insensitive不区分重音_ci表示case insensitive不区分大小写。也就是说默认规则下abc和ABC比较时是相等的排序也是相邻的。如果你需要强制区分大小写可以在查询时加BINARY关键字或者在建表时指定排序规则为utf8mb4_bin。6.4 TEXT和BLOB家族不到万不得已不要用TEXT家族有TINYTEXT、TEXT、MEDIUMTEXT、LONGTEXTBLOB家族对应有四种。TEXT按字符存储BLOB按二进制存储。它们的存在让MySQL可以存大块内容但它们也有几个显著缺点一个TEXT字段即使在执行SELECT *时也可能拖慢查询因为大的字段内容会占用更多的IO带宽。TEXT类型的字段不能直接设默认值在8.0之前的版本每次插入都必须显式给值。在排序和分组时TEXT类型不能像普通字符串那样直接参与必须指定前缀长度或使用函数处理。TEXT字段在有些场景下无法直接建立完整索引只能建前缀索引这会影响查询效率。所以我有一个习惯如果某个业务字段可能超过255字节比如文章正文、长评论、日志详情我会把它单独拆到一张扩展表用主表存基础信息扩展表用主键关联。查询列表时不碰扩展表只有在详情页才按需查出大字段。7. 日期时间类型存对格式别让自己将来加班改数据日期时间类型也是新手经常用错的重灾区。有人直接存字符串格式的时间然后用字符串比较看起来也能跑但索引失效、时区错乱、统计口径不一致的问题会在后面集中爆发。7.1 DATETIME和TIMESTAMP怎么选MySQL里最常用的日期时间类型是DATETIME和TIMESTAMP两者对比下来各有适用场景。对比项DATETIMETIMESTAMP存储字节8字节4字节取值范围1000-01-01 00:00:00 到 9999-12-31 23:59:591970-01-01 到 2038-01-19时区影响无按字面值存储有时区转换存的是UTC时间自动初始化支持支持DATETIME适合业务型时间比如订单创建时间、用户注册时间它不跟随数据库时区变化存的是什么就显示什么逻辑简单可靠。TIMESTAMP适合做自动更新时间和需要跨时区换算的场景因为它内部存储的是自1970年起的秒数取出来时再按会话时区换算。但它有个著名的2038年问题32位系统下会溢出虽然64位系统基本不受影响但很多严谨的架构师仍然会偏向DATETIME。7.2 DATE、TIME和YEAR除了DATETIME和TIMESTAMP还有三个更专门的类型DATE只存年月日占用3字节适合记录生日、交易日期这类不需要时分秒的场景。TIME只存时分秒范围是-838:59:59到838:59:59不仅可以表示一天内的时间还能表示时间间隔。YEAR只存年份占用1字节范围1901~2155。这三个类型用到的场景相对少但如果在统计表里误用了DATETIME存日期你会发现时间维度上多出来的“时分秒”根本没有任何意义还白白浪费了5个字节的存储。7.3 时间戳存储策略有些团队习惯用BIGINT存Unix时间戳理由是排序快、跨数据库兼容。这个方案在纯程序系统里确实没问题但如果你的数据要直接通过BI工具分析或者要让运营同学在报表平台里查询BIGINT的时间戳会让SQL可读性非常差每次都要FROM_UNIXTIME()转一次相当麻烦。我更倾向于用DATETIME作为主要时间字段在极少数需要跨系统比对且无语义歧义的接口场景里再用BIGINT时间戳。并且时间字段如果建了索引DATETIME的范围查询效率并不比BIGINT差太多但查询语句的可读性和维护性要好得多。7.4 自动初始化与ON UPDATE CURRENT_TIMESTAMP在建表时有两个很实用的配置CREATE TABLE order ( id INT PRIMARY KEY AUTO_INCREMENT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP );DEFAULT CURRENT_TIMESTAMP意思是插入时自动取当前时间。ON UPDATE CURRENT_TIMESTAMP意思是UPDATE时如果这一行有变动自动更新时间戳。这个特性特别适合记录数据变更时间避免每次UPDATE都要手工写时间。需要注意8.0之后还支持DEFAULT CURRENT_TIMESTAMP(3)这种带小数秒精度的写法精确到毫秒。但业务上如果没有特殊需求秒级精度就够用了别盲目追求毫秒而增加存储和索引负担。8. 其他类型与特殊应用场景JSON、ENUM、SET、空间数据除了三大主力类型外MySQL还提供了一些特殊类型。这些类型很吸引眼球但也不能乱用下面逐一说明我在实战中的取舍。8.1 JSON类型灵活但别当主存储MySQL从5.7开始支持JSON类型8.0还引入了JSON TABLE函数可以直接把JSON数组拆成多行查询。JSON字段的优势是灵活适合存储格式变化较多、不需要关联查询的扩展属性比如用户偏好设置、页面埋点参数、第三方接口返回的原始数据。它存储时会自动校验JSON合法性还会做二进制优化比存字符串再解析效率高一些。但JSON类型也有缺点它不能像普通字段一样直接建传统索引只能通过生成列的方式间接建索引查询时经常要做JSON_EXTRACT写起来繁琐优化器对它的优化能力也有限更关键的是JSON字段的存在会让排序、分组、WHERE过滤变得更复杂也不利于数据血缘分析和报表统计。所以我建议JSON只用来存“确实无法预先确定结构”的数据但凡是参与核心业务逻辑、要做筛选和排序的数据一定要拆成规范的独立字段。8.2 ENUM和SET看着方便实际很局限ENUM枚举和SET集合自由度很强能限制字段只能存预定义的值。比如一个人的性别、订单状态用ENUM可以把范围内定义成固定的几个状态数据库层面就挡住了脏数据。不过ENUM底层也是按整数存的它有一个隐藏的问题当你定义ENUM(a,b,c)它存储时实际上是按索引0、1、2来存的排序时也是按索引值排序而不是按a、b、c的字面顺序。如果你把索引顺序调整了所有旧数据的排序结果就会变化这是很多人没有预料到的。此外ENUM类型做变更很麻烦例如要新增一个枚举值ALTER TABLE可能涉及全表数据重写在千万级大表上成本很高。所以我更推荐用TINYINT配合业务代码里的常量映射表来管理枚举状态。这样既保留了数据语义又避免了ENUM的局限性。8.3 BIT、BINARY、VARBINARY二进制类型在数据量较大时用于存储图片、文件、加密串等。但实话实说绝大多数业务不需要把二进制内容直接存MySQL。把文件存对象存储数据库里存路径字段是更推荐的架构方案。合理的使用场景是存储加密后的摘要值、AES加密后的密文、或者一些位运算标记字段。BIT(1)可以高效地存储一个布尔标记BINARY(16)在存储固定长度的哈希值比如UUID不带横线时非常合适因为定长二分查找比VARCHAR存储更高效并且能直接建立索引。8.4 空间数据类型空间数据包括POINT、LINESTRING、POLYGON等类型适合做地理位置、地图、路径规划等地理信息系统应用。MySQL对空间数据的支持在过去几年逐步加强8.0中已经引入了空间索引R-Tree可以做“查找某个范围内的所有POINT”这种操作。如果你只是需要一个“附近的人”功能基于经纬度的Haversine公式加索引也能做但大量空间查询场景确实值得使用空间类型。空间类型的学习曲线比普通类型高不少我自己是到了项目里明确有地图需求时才深入研究不建议初学者入门阶段花太多时间。9. 建表时如何选择数据类型一份可以直接抄的速查法则想了很多实践内容后我把最常用的建表类型选择规则整理成下面这张速查表。你在设计表结构时对着这张表选绝大多数情况不会出错。场景推荐类型原因自增主键INT或BIGINT数值型索引效率高写入性能好用户状态、订单状态TINYINT省空间配合代码常量映射年龄TINYINT UNSIGNED范围足够且不浪费库存数量、销量INT或BIGINT数值计算高效避免字符串隐式转换金额、价格DECIMAL(10,2)精确小数避免浮点误差手机号VARCHAR(20)以后可能加区号、前缀邮箱VARCHAR(255)标准允许最长254个字符身份证号CHAR(18)定长且不应做数值运算订单号CHAR或VARCHAR定长更佳但需要考虑索引空间姓名VARCHAR(40)长度变化受字符集影响文章标题VARCHAR(255)常规标题长度足够文章正文MEDIUMTEXT或独立扩展表大字段隔离避免拖慢主表创建时间DATETIME DEFAULT CURRENT_TIMESTAMP业务时间可读性好更新时间DATETIME ON UPDATE CURRENT_TIMESTAMP自动维护逻辑删除标记TINYINT0正常1删除不要用字符判断JSON扩展信息JSON结构不固定才用二进制哈希BINARY(16)定长适合UUID和MD5摘要这里特别提醒一句不要为了“灵活”用VARCHAR存所有字段。字符串比较和排序都按字符集规则走数值按数值规则走索引优化时的成本大不相同。能用数字表示的就不该用字符串。10. 常见问题与排查技巧实录10.1 建表时明明定义了INT为什么插入很大的数字会报错如果你定义的是INT它最大范围是2147483647超过这个值就会报Out of range value错误。解决方式有两种一是改成BIGINT扩大范围二是如果确实是正数且需要更大范围加UNSIGNED可以让上限翻倍。但根本上还是要在建表前估好数据量级。10.2 为什么存中文变成了问号这是字符集配置的问题。要么是数据库/表/列的字符集不是utf8mb4要么是创建连接时没有指定字符集。你可以在命令行里执行-- 确认当前会话的字符集 SHOW VARIABLES LIKE character_set_client; SHOW VARIABLES LIKE character_set_connection; SHOW VARIABLES LIKE character_set_results;正常规则是客户端录入时用SET NAMES utf8mb4保证客户端、连接、服务器三方一致。如果你用图形工具连库连接设置里也要把编码指定成utf8mb4。10.3 VARCHAR(255)和TEXT存储大段文字时哪个更好如果单条内容不超过255个字符VARCHAR毫无疑问更好它支持建索引、默认值且存储更紧凑。超过255但小于几千字符时VARCHAR依然可以只是要注意整行的字节上限。真正上万的正文建议使用MEDIUMTEXT或TEXT但必须在表设计上做隔离避免和大表主表放在一起。10.4 为什么我在WHERE条件里写了name abc却同时匹配到了ABC因为默认排序规则utf8mb4_0900_ai_ci不区分大小写。解决办法是把查询条件改成BINARY name abc或者把那列的排序规则改为utf8mb4_bin再或者用COLLATE关键字临时指定排序规则处理。10.5 为什么DATETIME类型在查询时会带出毫秒如果你定义的是DATETIME(3)那你存的时间会带毫秒精度。普通DATETIME默认秒级精度不会带毫秒。如果你看到毫秒大概率是建表时写明了精度。另外CURRENT_TIMESTAMP(3)会生成带毫秒的当前时间如果不需要去掉小括号参数即可。10.6 主键用BIGINT还是自增INT自增INT在绝大多数业务系统中够用最大21亿如果你预估不了这么大量级或者需要分布式ID可以用BIGINT。但别动不动就BIGINT因为主键越大二级索引占用的空间也越大数据页能放下的索引记录就越少整体查询性能会受到影响。11. 写在最后这一章你只需要记住这几件事我见过很多人学数据库第一天就把几十张表的建表语句写完结果第二天就被数据不一致、索引失效、各种“为什么一样的数据结果不一样”折磨到怀疑人生。根本原因就是建表时对数据类型的理解不到位。第一章没有要求你去背语法而是要你把以下几个认知钉在脑子里第一数据类型决定了存储方式、存储大小和比较规则选错类型的代价不只是空间浪费而是后续查询和扩展的全面拉胯。第二整数类型范围内能用小就不用大能用数值就不用字符串。第三小数精确计算必须用DECIMAL。第四字符串类型要时刻想着字符集utf8mb4是默认友好的选择。第五时间类型优先DATETIME自动更新用ON UPDATE CURRENT_TIMESTAMP。第六大字段隔离设计、JSON慎用、ENUM不建议用在核心状态字段。把这些搞明白之后下一章我们可以安心地聊建表语句规范、字段约束、索引设计原理以及怎么把你建的表调整到更适合实际业务的形态。MySQL这条路很长但基础扎实了后面每一步都会很稳。我自己在数据类型的选型上也算踩过不少坑现在每次设计表结构时都会先对着字段清单想一遍“这个字段未来会怎么查询、怎么排序、会不会超过上限、需不需要做运算”想清楚这四件事再动手表结构基本就不会出大问题。希望这一章的内容能帮你少走一些弯路我们下一章见。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑