表空间管理:独立表空间 vs 共享表空间

表空间管理:独立表空间 vs 共享表空间

什么是表空间

InnoDB 的表空间(Tablespace)是存储数据的逻辑结构,可以理解为”数据的文件容器”。InnoDB 提供两种表空间管理模式:

  • 共享表空间(System Tablespace):所有表共享一个或多个数据文件(ibdata1)
  • 独立表空间(File-per-table Tablespace):每个表拥有独立的表空间文件(.ibd)

共享表空间(System Tablespace)

工作原理

通过 innodb_data_file_path 参数控制:

# 配置一个可自动扩展的共享表空间
innodb_data_file_path = ibdata1:12M:autoextend

# 配置多个文件
innodb_data_file_path = ibdata1:12M;ibdata2:50M:autoextend

所有表的数据都存储在同一个文件中,不可单独管理。

共享表空间存储的内容

除了表数据,共享表空间还存储:

  1. 数据字典:表结构元数据(MySQL 8.0 移到了数据字典表空间)
  2. 双写缓冲区(Doublewrite Buffer)
  3. 变更缓冲区(Change Buffer)
  4. Undo 日志(MySQL 5.6 之前)
  5. 用户表的索引和数据(当 innodb_file_per_table=0 时)

独立表空间(File-per-table Tablespace)

工作原理

innodb_file_per_table = 1  -- MySQL 5.6+ 默认开启

每个表对应一个 .ibd 文件:

/var/lib/mysql/mydb/
├── users.ibd         -- users 表的数据
├── orders.ibd        -- orders 表的数据
├── users.frm         -- 表结构定义(8.0 前)
└── orders.frm

MySQL 8.0 中,表结构定义移到了数据字典中,不再有 .frm 文件:

/var/lib/mysql/mydb/
├── users.ibd
└── orders.ibd

两种模式的对比

对比项 共享表空间 独立表空间
回收空间 ❌ 困难(ibdata1 不会缩小) ✅ 简单(DROP/TRUNCATE 即回收)
维护粒度 全局维护一个文件 按表独立维护
备份恢复 必须备份整个文件 支持单表恢复
磁盘碎片 ❌ 碎片在其中 表级别碎片
文件管理 单个或多个固定文件 每个表一个文件
性能 大量并发写入可能有瓶颈 没有共享文件锁竞争
fsync 多表共享 fsync 独立的 fsync

关键差异详解

1. 空间回收

这是选择独立表空间最常被提到的理由:

共享表空间的问题

-- 即使删除了大量数据,ibdata1 文件大小不会减少
INSERT INTO huge_table VALUES (...);  -- 写入大量数据,ibdata1 变大
DELETE FROM huge_table WHERE ...;     -- 删除后 ibdata1 仍然那么大
DROP TABLE huge_table;                -- 表删了,ibdata1 空间也不释放!

解决方案:需要重建 ibdata1——导出所有数据,删除 ibdata1,重启 MySQL,再导入。

独立表空间DROP TABLETRUNCATE TABLE 直接删除 .ibd 文件,空间立刻回到操作系统。

2. 碎片管理

独立表空间允许对单个表做碎片整理:

-- 重建表,消除碎片
ALTER TABLE users ENGINE=InnoDB;

-- 或者使用 OPTIMIZE TABLE
OPTIMIZE TABLE users;

共享表空间无法单独整理某个表。

3. 单表恢复

独立表空间可以恢复单张表:

# 1. 在目标库建空表
mysql > CREATE TABLE users (...) ENGINE=InnoDB;

# 2. 丢弃新表的表空间
mysql > ALTER TABLE users DISCARD TABLESPACE;

# 3. 将备份的 users.ibd 复制到数据目录
cp /backup/users.ibd /var/lib/mysql/mydb/

# 4. 导入表空间
mysql > ALTER TABLE users IMPORT TABLESPACE;

共享表空间无法做单表恢复。

独立表空间的缺点

  1. 文件句柄增多:大量小表时消耗更多文件描述符
  2. fsync 开销增加:每个表独立 fsync,写压力大时 fsync 次数更多
  3. 磁盘空间浪费:每个表至少占用 4KB(一个页),大量小表浪费空间
  4. 操作系统限制:某些文件系统对大目录下大量文件性能不好

特殊表空间类型

通用表空间(General Tablespace)

MySQL 5.7+ 支持通用表空间,可存放多个表:

-- 创建通用表空间
CREATE TABLESPACE my_ts ADD DATAFILE 'my_ts.ibd' ENGINE=InnoDB;

-- 在通用表空间上建表
CREATE TABLE t1 (id INT) TABLESPACE my_ts;
CREATE TABLE t2 (id INT) TABLESPACE my_ts;

优势:平衡了共享和独立的优点。

临时表空间

MySQL 5.7+ 使用独立的临时表空间:

innodb_temp_data_file_path = ibtmp1:12M:autoextend

实践建议

MySQL 5.6+

innodb_file_per_table = 1   # 默认开启,推荐保持开启
innodb_data_file_path = ibdata1:12M:autoextend  # ibdata1 缩小点即可

共享表现状

即使开启了独立表空间,ibdata1 仍然需要。但只存:
– 数据字典(MySQL 8.0 分离到数据字典表空间)
– 双写缓冲区
– 变更缓冲区
– Undo 日志(MySQL 5.6+ 可选分离)

ibdata1 大小管理

如果 ibdata1 已经很大,可以通过以下步骤缩小:

-- 1. 确认开启了独立表空间
SHOW VARIABLES LIKE 'innodb_file_per_table';

-- 2. 导出所有数据
mysqldump --all-databases > all.sql

-- 3. 关闭 MySQL,删除 ibdata1,重启
mysqladmin shutdown
rm /var/lib/mysql/ibdata1
mysqld_safe &

-- 4. 导入数据
mysql < all.sql

面试常问题

Q:使用了独立表空间,DROP TABLE 后空间会回收吗?
A:会。独立表空间的 .ibd 文件会被删除,磁盘空间立即回收。但共享表空间的 ibdata1 不会缩小,即使使用了独立表空间,ibdata1 中的系统数据仍然不会减少。

Q:为什么 ibdata1 越来越大?
A:即使开启了独立表空间,ibdata1 中仍然有双写缓冲区(默认 2MB)、变更缓冲区、Undo 日志等。当大量写入时,Undo 日志可能膨胀。MySQL 8.0 分离了 Undo 日志到独立表空间,减少了 ibdata1 的膨胀。

Q:什么时候不建议用独立表空间?
A:极少场景:MySQL 5.5 及以前(性能不如共享)、存在大量小表(大量表共用文件名会浪费文件句柄)、使用内存表或临时场景。现代 MySQL 默认开启独立表空间,保持即可。

© 版权声明
THE END
喜欢就支持一下吧
点赞15 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容