1. MySQL数据类型概述
在数据库设计和开发中,数据类型的选择直接影响着数据存储效率、查询性能和系统稳定性。MySQL作为最流行的关系型数据库之一,提供了丰富的数据类型系统,每种类型都有其特定的使用场景和限制条件。
我见过太多因为数据类型选择不当导致的性能问题:一个本该用TINYINT的字段被定义成INT,结果百万级数据表体积膨胀30%;用VARCHAR(255)存储固定长度的MD5值,白白浪费存储空间;DATETIME和TIMESTAMP混用造成时区转换问题...这些坑我都亲自踩过。
2. 数值类型详解
2.1 整数类型
MySQL提供了5种整数类型,它们的区别主要体现在存储空间和取值范围上:
| 类型 | 字节 | 有符号范围 | 无符号范围 |
|---|---|---|---|
| TINYINT | 1 | -128 ~ 127 | 0 ~ 255 |
| SMALLINT | 2 | -32768 ~ 32767 | 0 ~ 65535 |
| MEDIUMINT | 3 | -8388608 ~ 8388607 | 0 ~ 16777215 |
| INT | 4 | -2147483648 ~ 2147483647 | 0 ~ 4294967295 |
| BIGINT | 8 | -2^63 ~ 2^63-1 | 0 ~ 2^64-1 |
实际项目中我的选择原则是:
- 明确是否需要负数:不需要就加UNSIGNED
- 预估字段最大值:状态字段用TINYINT足够
- 考虑未来扩展性:用户ID建议直接BIGINT
2.2 浮点类型
FLOAT和DOUBLE用于存储近似数值,DECIMAL用于存储精确小数:
sql复制-- 价格等金融数据必须用DECIMAL
price DECIMAL(10,2) -- 共10位,小数占2位
-- 科学计算可用FLOAT/DOUBLE
temperature FLOAT(7,4) -- 范围大但可能丢失精度
重要提示:浮点比较必须设置误差范围,直接
=比较可能出错
3. 字符串类型
3.1 变长与定长字符串
- CHAR(10):固定分配10字节,适合存储固定长度数据(如MD5)
- VARCHAR(10):最多占用10字节,实际按内容长度分配
实测案例:存储100万个手机号(11位)
- CHAR(11)占用:11MB
- VARCHAR(11)占用:6.5MB
3.2 文本类型
| 类型 | 最大长度 | 特点 |
|---|---|---|
| TINYTEXT | 255B | 不需要指定长度 |
| TEXT | 64KB | 适合文章内容 |
| MEDIUMTEXT | 16MB | 适合大型文档 |
| LONGTEXT | 4GB | 极少使用,影响性能 |
经验:文本字段避免使用SELECT *,按需查询
4. 日期时间类型
4.1 各类型对比
| 类型 | 格式 | 范围 | 时区支持 | 存储空间 |
|---|---|---|---|---|
| DATE | 'YYYY-MM-DD' | 1000-01-01~9999-12-31 | 无 | 3字节 |
| TIME | 'HH:MM:SS' | -838:59:59~838:59:59 | 无 | 3字节 |
| DATETIME | 'YYYY-MM-DD HH:MM:SS' | 1000-01-01 00:00:00~9999-12-31 23:59:59 | 无 | 8字节 |
| TIMESTAMP | 'YYYY-MM-DD HH:MM:SS' | 1970-01-01 00:00:01~2038-01-19 03:14:07 | 自动转换 | 4字节 |
4.2 时区陷阱解决方案
TIMESTAMP会转换为UTC存储,DATETIME则原样存储。跨国系统推荐方案:
sql复制-- 存储用DATETIME
created_at DATETIME NOT NULL
-- 显示时前端转换时区
SELECT created_at FROM orders;
5. JSON类型
MySQL 5.7+原生支持JSON类型,提供了丰富的JSON函数:
sql复制-- 创建表
CREATE TABLE products (
id INT PRIMARY KEY,
specs JSON
);
-- 插入数据
INSERT INTO products VALUES (1, '{"color":"red", "size":42}');
-- 查询
SELECT specs->>"$.color" FROM products; -- 返回red
性能优化建议:
- 常用查询字段仍建议单独列存储
- JSON大小不超过1MB
- 使用虚拟列建立索引
6. 枚举与集合类型
6.1 ENUM类型
sql复制-- 性别字段最佳实践
gender ENUM('male','female','unknown') NOT NULL DEFAULT 'unknown'
注意事项:
- 实际存储为整数索引(1,2,3...)
- 不要超过50个选项
- 修改枚举值需要ALTER TABLE
6.2 SET类型
适合存储多选项:
sql复制user_perms SET('read','write','delete','admin') DEFAULT 'read'
查询示例:
sql复制-- 查找有write权限的用户
SELECT * FROM users WHERE FIND_IN_SET('write', user_perms);
7. 空间数据类型
MySQL支持GIS地理数据:
sql复制CREATE TABLE locations (
id INT PRIMARY KEY,
name VARCHAR(100),
position POINT SRID 4326 -- WGS84坐标系统
);
-- 插入坐标点
INSERT INTO locations VALUES (1, 'Eiffel Tower', ST_GeomFromText('POINT(48.8584 2.2945)'));
使用场景:
- 地图应用
- 距离计算
- 地理围栏
8. 数据类型选择最佳实践
- 优先选择最小满足需求的类型
- 字符串字段考虑字符集和排序规则
- 主键推荐使用无符号BIGINT
- 金额使用DECIMAL,避免浮点误差
- 大文本与主表分离存储
- 时间字段根据是否需要时区转换选择
常见错误案例:
- 用VARCHAR存储IP地址(应用INET_ATON转换)
- 用字符串存储数字(导致排序错误)
- BOOL类型用CHAR(1)(应直接用TINYINT(1))
9. 性能优化技巧
- 数据类型隐式转换问题:
sql复制-- 索引失效案例(user_id是字符串类型)
SELECT * FROM users WHERE user_id = 10086; -- 错误
SELECT * FROM users WHERE user_id = '10086'; -- 正确
- 使用PROCEDURE ANALYSE()获取优化建议:
sql复制SELECT * FROM orders PROCEDURE ANALYSE(16,256);
- 查看实际数据分布:
sql复制SELECT
column_name,
MIN(value),
MAX(value),
AVG(LENGTH(value))
FROM table GROUP BY column_name;
在实际项目中,我通常会先分析业务数据的实际特征,再结合上表选择最合适的数据类型。比如用户年龄字段,虽然SMALLINT足够,但考虑到未来可能扩展为出生日期,直接使用DATE类型可能更合理。