上篇文章:MySQL库操作硬核解析:字符集、校验规则、大小写比较、备份恢复与连接排查
目录
[查看表结构:DESC 与 SHOW CREATE TABLE](#查看表结构:DESC 与 SHOW CREATE TABLE)
[修改表结构:ALTER TABLE 是 DDL 的核心](#修改表结构:ALTER TABLE 是 DDL 的核心)
[添加字段:ADD COLUMN](#添加字段:ADD COLUMN)
[删除字段:DROP COLUMN 的不可逆风险](#删除字段:DROP COLUMN 的不可逆风险)
[修改表名:RENAME TO](#修改表名:RENAME TO)
[删除表:DROP TABLE](#删除表:DROP TABLE)
[MyISAM 与 InnoDB 的表文件差异](#MyISAM 与 InnoDB 的表文件差异)
[小结:表操作不是背 SQL,而是在修改数据结构和磁盘布局](#小结:表操作不是背 SQL,而是在修改数据结构和磁盘布局)
前言
表是业务实体的结构化表达。
在 MySQL 中,表是保存业务实体数据的核心结构。用户、订单、商品、文章、评论,都可以被抽象成表。表由字段组成,每个字段都有自己的名字、类型、默认值、字符集、校验规则、注释等属性。
如果说数据库是业务模块的数据边界,那么表就是业务实体的数据模型。设计表,本质上是在设计程序如何理解和组织数据。
创建表的基本语法
创建表语法如下:
sql
CREATE TABLE table_name (
field1 datatype,
field2 datatype,
field3 datatype
) CHARACTER SET 字符集 COLLATE 校验规则 ENGINE 存储引擎;
其中 field 表示列名,datatype 表示列类型。CHARACTER SET 指定表的字符集,如果没有指定,则默认使用所在数据库的字符集。COLLATE 指定表的校验规则,如果没有指定,则默认使用所在数据库的校验规则。ENGINE 指定存储引擎,如果没有指定,则使用 MySQL 默认存储引擎,现代版本通常是 InnoDB。
示例:
mysql> create table if not exists user1(
-> id int,
-> name varchar(20) comment '用户名',
-> password char(32) comment '用户的密码',
-> birthday date comment '用户的生日'
-> )character set utf8 collate utf8_general_ci engine MyIsam;
Query OK, 0 rows affected, 3 warnings (0.01 sec)
mysql> create table if not exists user2(
-> id int,
-> name varchar(20) comment '用户名',
-> password char(32) comment '用户的密码',
-> birthday date comment '用户的生日'
-> )charset=utf8 collate=utf8_general_ci engine=InnoDB;
Query OK, 0 rows affected, 2 warnings (0.05 sec)
这个表包含四个字段:id、name、password、birthday。其中 name 使用 VARCHAR(20),表示可变长度字符串;password 使用 CHAR(32),适合保存固定长度的 MD5 字符串;birthday 使用 DATE,适合保存日期。
字段注释通过 COMMENT 添加,便于后续查看表结构时理解字段含义。
表的字符集、校验规则和继承关系
表可以指定自己的字符集和校验规则。如果没有指定,会继承所在数据库的默认设置。字段也可以单独指定字符集和校验规则,如果字段指定了,则字段优先级最高。
可以理解为:
字段级设置 > 表级设置 > 数据库级设置 > 服务器默认设置
这套继承关系非常重要。很多项目中出现"同一个库里不同表排序规则不一致""同一张表里字段字符集不同"的问题,根源就是建库、建表、建字段时没有统一规范。
工程建议是:建库时明确使用 utf8mb4,建表时除非特殊需求,否则保持继承,避免同一个系统内字符集混乱。
不同存储引擎对应不同磁盘文件
MySQL 表不是凭空存在的。创建表后,MySQL 会在数据目录下生成对应的文件。不同存储引擎生成的文件不同。
以 MyISAM 为例,早期版本中一个表通常对应三个文件:
users.frm:表结构
users.MYD:表数据
users.MYI:表索引
MySQL 8 环境里,创建 MyISAM 表后可以看到类似文件:
# ll
total 128
drwxr-x--- 2 mysql mysql 4096 Jun 6 21:19 ./
drwx------ 10 mysql mysql 4096 Jun 6 20:52 ../
-rw-r----- 1 mysql mysql 3991 Jun 6 21:11 user1_368.sdi
-rw-r----- 1 mysql mysql 0 Jun 6 21:11 user1.MYD
-rw-r----- 1 mysql mysql 1024 Jun 6 21:11 user1.MYI
-rw-r----- 1 mysql mysql 114688 Jun 6 21:19 user2.ibd
其中 .MYD 保存 MyISAM 数据,.MYI 保存 MyISAM 索引,.sdi 是 MySQL 8 中序列化的数据字典信息。InnoDB 表则常见:user2.ibd
.ibd 是 InnoDB 独立表空间文件,里面保存表的数据和索引页。笔记中同一个数据库下创建 MyISAM 和 InnoDB 表后,目录里出现:
user1_368.sdi
user1.MYD
user1.MYI
user2.ibd
这说明同样是 MySQL 表,不同存储引擎的物理组织方式不同。可以这样理解:
SQL 层看到的都是表
存储引擎层决定表如何落盘
文件系统层负责最终保存这些文件
这正是 MySQL 插件式存储引擎架构的体现。
查看表结构:DESC 与 SHOW CREATE TABLE
查看表结构:
DESC users;
DESC 适合快速查看字段结构,但它不是完整建表语句。想查看完整表定义,应使用:
SHOW CREATE TABLE users\G (\G 会把结果纵向展示,适合查看较长的建表语句。)
sql
mysql> show create table user1 \G
*************************** 1. row ***************************
Table: user1
Create Table: CREATE TABLE `user1` (
`id` int DEFAULT NULL,
`name` varchar(20) DEFAULT NULL COMMENT '用户名',
`password` char(32) DEFAULT NULL COMMENT '用户的密码',
`birthday` date DEFAULT NULL COMMENT '用户的生日'
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb3
1 row in set (0.00 sec)
mysql> show tables;
+-------------------+
| Tables_in_user_db |
+-------------------+
| user1 |
| user2 |
+-------------------+
2 rows in set (0.00 sec)
mysql> alter table user1 rename to user;
Query OK, 0 rows affected (0.02 sec)
mysql> show tables;
+-------------------+
| Tables_in_user_db |
+-------------------+
| user |
| user2 |
+-------------------+
2 rows in set (0.00 sec)
mysql> desc user;
+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| id | int | YES | | NULL | |
| name | varchar(20) | YES | | NULL | |
| password | char(32) | YES | | NULL | |
| birthday | date | YES | | NULL | |
+----------+-------------+------+-----+---------+-------+
4 rows in set (0.00 sec)
SHOW CREATE TABLE 能看到字段类型、默认值、注释、存储引擎、字符集等完整信息。排查线上表结构时,它比 DESC 更可靠。
插入数据:结构定义后的内容写入
创建表后可以插入数据:
sql
mysql> desc user;
+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| id | int | YES | | NULL | |
| name | varchar(20) | YES | | NULL | |
| password | char(32) | YES | | NULL | |
| birthday | date | YES | | NULL | |
+----------+-------------+------+-----+---------+-------+
4 rows in set (0.00 sec)
mysql> insert into user values (1, '张三', '12344556', '2020-9-10');
Query OK, 1 row affected (0.00 sec)
mysql> insert into user values (2, '李四', '12386566', '2020-3-10');
Query OK, 1 row affected (0.01 sec)
mysql> select * from user;
+------+--------+----------+------------+
| id | name | password | birthday |
+------+--------+----------+------------+
| 1 | 张三 | 12344556 | 2020-09-10 |
| 2 | 李四 | 12386566 | 2020-03-10 |
+------+--------+----------+------------+
2 rows in set (0.00 sec)
修改表结构:ALTER TABLE 是 DDL 的核心
实际项目中,表结构经常需要变化。例如增加字段、删除字段、修改字段类型、修改字段名、调整字符集、修改存储引擎、重命名表。这些操作都属于 DDL,常用 ALTER TABLE 完成。
常见语法包括:
sql
ALTER TABLE tablename ADD column datatype [DEFAULT expr];
ALTER TABLE tablename MODIFY column datatype [DEFAULT expr];
ALTER TABLE tablename DROP column;
ALTER TABLE tablename CHANGE old_column new_column datatype;
ALTER TABLE tablename RENAME TO new_table_name;
DDL 操作往往比普通数据操作风险更高,因为它修改的是数据结构。尤其是大表执行 DDL,可能涉及重建表、复制数据、加元数据锁、阻塞读写等问题。学习阶段可以直接执行,生产环境必须谨慎。
添加字段:ADD COLUMN
给表添加字段,例如添加用户头像路径:
sql
mysql> alter table user add image_path varchar(128) comment '用户头像路径' after birthday;
Query OK, 2 rows affected (0.04 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> select * from user;
+------+--------+----------+------------+------------+
| id | name | password | birthday | image_path |
+------+--------+----------+------------+------------+
| 1 | 张三 | 12344556 | 2020-09-10 | NULL |
| 2 | 李四 | 12386566 | 2020-03-10 | NULL |
+------+--------+----------+------------+------------+
2 rows in set (0.00 sec)
mysql> desc user;
+------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+-------+
| id | int | YES | | NULL | |
| name | varchar(20) | YES | | NULL | |
| password | char(32) | YES | | NULL | |
| birthday | date | YES | | NULL | |
| image_path | varchar(128) | YES | | NULL | |
+------------+--------------+------+-----+---------+-------+
5 rows in set (0.00 sec)
mysql> show create table user \G
*************************** 1. row ***************************
Table: user
Create Table: CREATE TABLE `user` (
`id` int DEFAULT NULL,
`name` varchar(20) DEFAULT NULL COMMENT '用户名',
`password` char(32) DEFAULT NULL COMMENT '用户的密码',
`birthday` date DEFAULT NULL COMMENT '用户的生日',
`image_path` varchar(128) DEFAULT NULL COMMENT '用户头像路径'
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb3
1 row in set (0.00 sec)
可以看到,插入新字段后,原来的数据仍然存在。新字段对已有行默认填充 NULL,除非指定默认值。
AFTER birthday 表示把新字段放在 birthday 字段之后。如果不指定位置,通常默认添加到最后。
修改字段类型:MODIFY
修改字段类型,例如把 name 长度从 20 改成 60:
需要注意:MODIFY 修改字段定义时,如果原字段有注释、默认值、非空约束等,而新定义没有写,可能会导致这些属性丢失。因此修改字段时要完整考虑字段定义,而不是只写类型。
sql
mysql> alter table user modify name varchar(60);
Query OK, 2 rows affected (0.03 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> show create table user \G
*************************** 1. row ***************************
Table: user
Create Table: CREATE TABLE `user` (
`id` int DEFAULT NULL,
`name` varchar(60) DEFAULT NULL,
`password` char(32) DEFAULT NULL COMMENT '用户的密码',
`birthday` date DEFAULT NULL COMMENT '用户的生日',
`image_path` varchar(128) DEFAULT NULL COMMENT '用户头像路径'
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb3
1 row in set (0.00 sec)
删除字段:DROP COLUMN 的不可逆风险
删除字段:
sql
mysql> alter table user drop password;
Query OK, 2 rows affected (0.02 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> desc user;
+------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+-------+
| id | int | YES | | NULL | |
| name | varchar(60) | YES | | NULL | |
| birthday | date | YES | | NULL | |
| image_path | varchar(128) | YES | | NULL | |
+------------+--------------+------+-----+---------+-------+
4 rows in set (0.00 sec)
mysql> select * from user;
+------+--------+------------+------------+
| id | name | birthday | image_path |
+------+--------+------------+------------+
| 1 | 张三 | 2020-09-10 | NULL |
| 2 | 李四 | 2020-03-10 | NULL |
+------+--------+------------+------------+
2 rows in set (0.00 sec)
mysql> show create table user\G
*************************** 1. row ***************************
Table: user
Create Table: CREATE TABLE `user` (
`id` int DEFAULT NULL,
`name` varchar(60) DEFAULT NULL,
`birthday` date DEFAULT NULL COMMENT '用户的生日',
`image_path` varchar(128) DEFAULT NULL COMMENT '用户头像路径'
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb3
1 row in set (0.00 sec)
结果中不再有 password 字段。
更重要的是,删除字段不仅删除表结构中的列,还会删除该列对应的所有数据。原来的密码数据会消失。课件中特别提醒:删除字段一定要小心,删除字段及其对应列数据都没了。
生产环境中删除字段通常要经过几个阶段:先确认业务不再使用,代码停止读写该字段,保留一段观察期,再执行删除,并且删除前必须备份。
修改表名:RENAME TO
修改表名:
sql
ALTER TABLE users RENAME TO employee;
其中 TO 可以省略:
sql
ALTER TABLE users RENAME employee;
重命名表后,原表名不再可用。业务代码、SQL、权限、备份脚本中引用旧表名的地方都需要同步修改。
修改字段名:CHANGE
将 name 字段修改为 xingming:
sql
mysql> desc user;
+------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+-------+
| id | int | YES | | NULL | |
| name | varchar(60) | YES | | NULL | |
| birthday | date | YES | | NULL | |
| image_path | varchar(128) | YES | | NULL | |
+------------+--------------+------+-----+---------+-------+
4 rows in set (0.00 sec)
mysql> alter table user change name xingming varchar(60) DEFAULT NULL;
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> desc user;
+------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+-------+
| id | int | YES | | NULL | |
| xingming | varchar(60) | YES | | NULL | |
| birthday | date | YES | | NULL | |
| image_path | varchar(128) | YES | | NULL | |
+------------+--------------+------+-----+---------+-------+
4 rows in set (0.00 sec)
删除表:DROP TABLE
删除表语法:
sql
DROP [TEMPORARY] TABLE [IF EXISTS] tbl_name [, tbl_name] ...
删除表会删除表结构和表数据。对于不同存储引擎,底层对应的数据文件也会被删除。例如 MyISAM 的 .MYD、.MYI,InnoDB 的 .ibd 等文件都会受到影响。
DROP TABLE 和 DROP DATABASE 一样,都是高危 DDL。执行前必须确认对象、环境和备份情况。尤其是生产环境中,误删表往往是严重事故。
表操作背后的磁盘变化
创建数据库和表后,可以在 Linux 数据目录中看到对应变化。创建数据库,本质上会创建目录:
bash
/var/lib/mysql/user_db
创建 MyISAM 表,可能出现:
bash
user1_368.sdi
user1.MYD
user1.MYI
创建 InnoDB 表,可能出现:
bash
user2.ibd
执行 ALTER TABLE user1 RENAME TO user; 后,MySQL 内部表名改变,底层相关文件也会随之调整。执行 ALTER TABLE user ADD image_path ... 后,表结构改变,已有记录新字段默认变为 NULL。执行 ALTER TABLE user DROP password; 后,表结构和对应列数据都会消失。
从这个角度看,DDL 并不是"改一下元信息"那么简单。很多 DDL 可能需要重建表结构、迁移数据、更新数据字典、修改底层文件。表越大,DDL 成本越高,风险越大。
MyISAM 与 InnoDB 的表文件差异
MyISAM 表结构和数据、索引通常分开存放:
.MYD:数据
.MYI:索引
.sdi / .frm:表结构或数据字典信息
InnoDB 表常见 .ibd 文件,数据和索引都以页的形式组织在表空间文件中。InnoDB 的聚簇索引结构决定了数据和主键索引关系非常紧密。虽然这部分超出了当前 PDF 范围,但这里至少要建立一个概念:InnoDB 不是简单把每行数据顺序写入文件,而是通过页、B+Tree、redo log、undo log、buffer pool 等机制管理数据。
因此,选择存储引擎会直接影响表的物理组织方式、事务能力、锁粒度、崩溃恢复和性能表现。
表结构设计中的工程建议
表结构设计不是随便写几个字段。即使是基础阶段,也应该形成一些习惯。
字段名要表达业务含义,例如 name、birthday、image_path。字段类型要匹配数据特点,固定长度字符串可以用 CHAR,可变字符串用 VARCHAR,日期用 DATE。字段注释要写清楚,方便维护。字符集和校验规则要统一,避免同库不同表、同表不同字段混乱。删除字段和删除表前必须备份。修改字段时要注意完整定义,避免丢失注释、默认值等属性。
更重要的是,大表上的 DDL 要谨慎。开发环境里 ALTER TABLE 可能瞬间完成,生产环境里可能因为数据量大导致长时间锁表、阻塞业务、复制延迟甚至引发故障。
小结:表操作不是背 SQL,而是在修改数据结构和磁盘布局
表是 MySQL 管理数据的核心单位。CREATE TABLE 定义业务实体结构,DESC 和 SHOW CREATE TABLE 用于查看表结构,ALTER TABLE 用于修改字段、类型、表名和字段名,DROP TABLE 用于删除整张表。不同存储引擎会让表在磁盘上呈现不同文件形态,MyISAM 和 InnoDB 的物理组织方式明显不同。
真正掌握表操作,不能只会写命令,还要知道这些命令背后发生了什么:创建表可能生成数据文件,添加字段会影响已有记录的默认值,修改字段可能改变数据定义,删除字段会删除整列数据,删除表会清除结构和内容。MySQL 表操作的本质,是在 SQL 抽象层、存储引擎层和文件系统层之间建立映射。只有理解这个映射,才能写出安全、可维护、可恢复的数据库结构设计。