MySQL是怎样存储数据的?

本文详细介绍了MySQL中InnoDB存储引擎的数据存储方式,包括表空间、段、区、页和记录的组织,以及B+树索引结构的应用。重点讲解了聚簇索引和二级索引的区别,以及数据查找的高效策略。

MySQL是怎样存储数据的?

在现代数据库系统中,MySQL的InnoDB存储引擎通过精巧的数据结构设计和高效的索引算法,为海量数据提供了稳定、快速且持久化的存储服务。

本文将自顶向下详细解读MySQL如何组织和管理数据,从宏观的表空间概念出发,层层剥茧至微观的记录存储,并阐述InnoDB所采用的B+树索引结构以及基于此结构查找数据的流程。

(文末附视频链接)

表空间的组成

在MySQL中记录是如何进行存储的呢?

MySQL存储数据的方式大体上取决于所使用的存储引擎(这里主要以最常用的InnoDB存储引擎为例来说明)

MySQL会将数据存储在data目录中 show variables like 'datadir'

其中包含日志与数据文件,日志包括:redo log、bin log、慢SQL日志、错误日志等,而数据文件包括系统的和我们创建的

在data目录中以库为单位生成目录,库的目录中存储表相关的文件

在Innodb中,表相关的文件包括表结构文件和表空间文件

表结构文件:声明表结构信息 表名.frm

表空间文件:存储数据(记录)表名.idb

如果使用的是myisam存储引擎,存储数据的文件还会分为数据文件和索引文件

(Innodb中数据即索引,索引即数据,因此只有一个文件)

表空间文件又分为独立表空间和共享表空间,独立表空间用于存储用户数据,共享表空间则是服务于元数据(管理用户数据)ibdata1

(这里的用户指的是使用MySQL的用户)

自顶向下查看MySQL的存储情况:表空间->段(逻辑)->区->页->记录 非/叶子节点段构建索引B+树

为了方便管理,表空间逻辑上使用段进行管理,段由区、零散页组成

独立表空间中的段用于存储索引数据(用户数据),索引数据时分为叶子节点段和非叶子节点段

系统表空间的段用于存储元数据如:回滚段(存储undo log)

Innodb存储数据使用改进的B+树,叶子节点中的记录存储用户数据,非叶子节点中的记录存储下层节点的信息

在物理上表空间由多个区组成,区为在物理上连续的64个页,而页是内存、磁盘交互的基本单位 默认为16KB

使用区的好处是页连续,这样在进行范围扫描时IO是顺序的,如果用零散页范围扫描时可能出现随机IO

但是一个区占用的空间太大,连续的64个页,如果存储小数据量的表会造成空间浪费

因此申请空间时会先使用零散页,当数据量逐步上升时申请空间以区为单位

页内存储着记录,记录由额外信息与数据组成,额外信息可能记录一些数据如:事务ID、回滚指针、字段额外长度等

聚簇索引的存储

在Innodb中索引即数据,在创建表时会默认生成聚簇(主键)索引,如果创建表时未设置主键,则会使用记录的隐藏列作为主键

聚簇索引的特点是以主键排序并拥有完整的记录

在叶子节点中记录以主键升序维护成单向链表,非叶子节点中记录则是以下层目录页中最小主键升序维护成单向链表

为了方便范围查找同级节点之间会维护成双向链表

当查询时会从根节点(非叶子节点)一步一步查询到叶子节点

页中的记录维护成单向链表,在一个页中搜索记录的时间复杂度为O(n),当数据量较大时只能进行遍历

由于页内记录是有序的,为了加快查找速度将页内的记录分为多个组,将每个组中的最大记录维护成一个升序列表

图中不同颜色的记录为不同的组,每个组的最大值维护成升序列表(infimum,2,4,6,supermum)

页内默认有最小的记录infimum和最大的记录supermum,其中infimum记录单独为一组,supermum可以和其他记录为一组

(它们的加入是为了方便加间隙锁,防止幻读)

这样在进行页内查找时可以使用二分法进行查找,将时间复杂度降低为O(log n)

比如查询条件为 id >= 7

  1. 在根节点上使用二分法找到第一个小于等于目标值的记录(假设这里升序列表为1、17、33,7在1、17之间,则会去查找1对应的页)
  2. 在第二层上使用二分法找到第一个小于等于目标值的记录(假设这里升序列表为1、5、9、13,7在5、9之间,则会去查找5对应的页)
  3. 在第三层(叶子节点层)上使用二分法找到第一个小于等于目标值的记录(假设这里升序列表为5、6、7、8,则就定位到7的记录),然后通过记录中维护的单向链表,页与页维护的双向链表进行范围扫描

二级索引的存储

为表中某个列建立索引时,可以称这个索引为二级索引

聚簇索引与二级索引较大的区别为:聚簇索引存储完整的记录,而二级索引上的记录只存储索引列、主键

比如为表中列包含:id 主键、age、student_name、info

聚簇索引中的记录则会以id升序并存储所有列的信息

建立age、student_name的联合索引(二级索引)

二级索引中记录则只存储age、student_name、id的信息,并以age、student_name、id的顺序升序排序

当age相等时,根据student_name升序排序;当student_name相等时,再根据id升序排序

如果使用二级索引时要获取完整数据还需要回表查询聚簇索引,比如使用二级索引时还要获取info列则需要回表查询聚簇索引

总结

本篇文章自顶向下描述MySQL的Innodb如何进行存储数据

在MySQL的data目录中会存储日志、系统库、用户库等数据,其中库以目录为单位,表文件存储在对应库中

Innodb下表文件通常包括表结构文件(.frm存储表结构) 和表空间文件(.idb存储记录-用户数据)

表空间分为共享表空间和独立表空间,共享表空间服务元数据存储回滚段等,独立表空间服务用户数据存储非叶子节点段、叶子节点段等

段是逻辑上的概念方便于管理不同功能的空间,段由若干个区和零散页组成

区由连续64个页组成,连续页便于范围扫描顺序IO,页是内存与磁盘IO交互的基本单位,默认是16KB用于存储记录

非叶子节点中存储的记录通常用于“路由”,真正的数据存储在叶子节点中的记录

页内记录按照索引列升序排序维护成单向链表,同层级的页与页之间维护成双向链表方便范围查询

页中记录会分为多个组,记录每个组中最大记录维护成升序列表,当查找时在升序列表上使用二分法进行查找

聚簇索引以主键值升序排序并存储完整数据,如果未规定主键则在记录的隐藏列自动记录

二级索引则按照索引列进行排序,并且只存储索引列和主键的数据,如果使用二级索引时要获取完整数据还需要回表查询聚簇索引

最后(不要白嫖,一键三连求求拉~)

本篇文章被收入专栏 MySQL进阶之路,感兴趣的同学可以持续关注喔

本篇文章笔记以及案例被收入 gitee-StudyJavagithub-StudyJava 感兴趣的同学可以stat下持续关注喔~

有什么问题可以在评论区交流,如果觉得菜菜写的不错,可以点赞、关注、收藏支持一下~

关注菜菜,分享更多干货,公众号:菜菜的后端私房菜

bilibili视频链接

西瓜视频:

jvideo

本文由博客一文多发平台 OpenWrite 发布!

<think>我们正在回答用户的问题:如何修改MySQL默认数据存储路径?根据引用内容,我们可以总结出在Linux系统下修改MySQL数据存储路径的通用步骤,包括CentOS和Ubuntu系统。注意,不同MySQL版本(如5.7和8.0)的配置文件路径可能略有不同,但基本思路一致。步骤概述:1.停止MySQL服务2.创建新的数据存储目录3.复制原有数据到新目录并设置权限4.备份并修改MySQL配置文件(my.cnf或mysqld.cnf)5.对于SELinux环境(如CentOS),可能需要更新安全上下文6.启动MySQL服务下面以CentOS7和Ubuntu18.04为例,详细说明操作步骤。假设新的数据存储路径为`/data/mysql`。###📍一、CentOS7修改MySQL数据存储路径(适用于MySQL5.7/8.0)####1.停止MySQL服务```bashsudosystemctlstopmysqld```####2.创建新的数据目录并复制数据```bashsudomkdir-p/data/mysqlsudochown-Rmysql:mysql/data/mysql#修改所有者#复制原数据文件(注意保留原有权限)sudocp-rp/var/lib/mysql/*/data/mysql/```####3.备份并修改配置文件-MySQL默认配置文件路径:`/etc/my.cnf`(或`/etc/mysql/my.cnf`)```bashsudocp/etc/my.cnf/etc/my.cnf.bak#备份sudovi/etc/my.cnf```找到`[mysqld]`段,修改`datadir`和`socket`路径:```ini[mysqld]datadir=/data/mysqlsocket=/data/mysql/mysql.sock#如果原配置有指定socket,则修改```同时修改`[client]`和`[mysql]`段(如果存在)的socket路径:```ini[client]socket=/data/mysql/mysql.sock[mysql]socket=/data/mysql/mysql.sock```####4.处理SELinux安全上下文(仅启用SELinux的系统需要)```bash#安装policycoreutils-python(如果未安装)sudoyuminstallpolicycoreutils-python-y#添加新目录的安全上下文semanagefcontext-a-tmysqld_db_t"/data/mysql(/.*)?"restorecon-Rv/data/mysql```####5.启动MySQL服务```bashsudosystemctlstartmysqld```###📍二、Ubuntu18.04修改MySQL数据存储路径####1.停止MySQL服务```bashsudosystemctlstopmysql```####2.创建新目录并复制数据```bashsudomkdir-p/data/mysqlsudochown-Rmysql:mysql/data/mysqlsudocp-rp/var/lib/mysql/*/data/mysql/```####3.修改配置文件-Ubuntu下MySQL配置文件通常位于:`/etc/mysql/mysql.conf.d/mysqld.cnf````bashsudocp/etc/mysql/mysql.conf.d/mysqld.cnf/etc/mysql/mysql.conf.d/mysqld.cnf.baksudovi/etc/mysql/mysql.conf.d/mysqld.cnf```修改`datadir`和`socket`:```ini[mysqld]datadir=/data/mysqlsocket=/data/mysql/mysql.sock```同时修改客户端配置(如果有独立配置文件,如`/etc/mysql/conf.d/mysql.cnf`):```ini[client]socket=/data/mysql/mysql.sock[mysql]socket=/data/mysql/mysql.sock```####4.修改AppArmor配置(Ubuntu需要)```bashsudovi/etc/apparmor.d/usr.sbin.mysqld```找到原数据目录路径(如`/var/lib/mysql/**`),替换为`/data/mysql/**`,然后重载配置:```bashsudo/etc/init.d/apparmorreload```####5.启动MySQL服务```bashsudosystemctlstartmysql```###📍三、验证数据路径启动服务后,连接MySQL并执行:```sqlSHOWVARIABLESLIKE'datadir';```预期输出为`/data/mysql/`。###⚠️注意事项1.**权限问题**:确保新目录的权限设置为`mysql:mysql`,否则MySQL无法启动。2.**磁盘空间**:新挂载的磁盘空间应足够。3.**配置文件差异**:不同版本或发行版的MySQL配置文件路径可能不同,请根据实际情况调整。4.**符号链接**:不推荐使用符号链接方式迁移数据目录,可能导致性能问题[^1]。§§相关问题§§1.迁移MySQL数据目录后出现权限错误怎么办?2.如何为MySQL数据目录挂载新磁盘?3.MySQL的配置文件有哪些关键参数?</think> 以下是修改 MySQL 默认数据存储路径的完整教程,基于您提供的引用信息和系统实践经验整理: ### 📍 修改 MySQL 默认数据存储路径教程(Linux 系统通用方法) #### ⚠️ 准备工作 1. **备份重要数据** ```bash sudo mysqldump -u root -p --all-databases > full_backup.sql ``` 2. **停止 MySQL 服务** ```bash sudo systemctl stop mysqld # CentOS/RHEL sudo systemctl stop mysql # Ubuntu/Debian ``` --- ### 🔧 详细操作步骤(以迁移到 `/data/mysql` 为例) #### 步骤 1:创建新数据目录 ```bash sudo mkdir -p /data/mysql ``` #### 步骤 2:复制原始数据文件 ```bash sudo cp -rp /var/lib/mysql/* /data/mysql/ ``` #### 步骤 3:修改文件权限(关键步骤) ```bash sudo chown -R mysql:mysql /data/mysql sudo chmod 750 /data/mysql ``` #### 步骤 4:修改配置文件 编辑 MySQL 主配置文件(不同系统路径稍有差异): ```bash # CentOS/RHEL sudo vi /etc/my.cnf # Ubuntu/Debian sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf ``` 修改以下参数: ```ini [mysqld] datadir=/data/mysql # 修改数据存储路径 socket=/data/mysql/mysql.sock # 同时修改socket路径 [client] socket=/data/mysql/mysql.sock # 客户端配置也需修改 ``` #### 步骤 5:安全上下文设置(仅 SELinux 系统需执行) ```bash sudo yum install policycoreutils-python -y sudo semanage fcontext -a -t mysqld_db_t "/data/mysql(/.*)?" sudo restorecon -Rv /data/mysql ``` #### 步骤 6:启动 MySQL 服务 ```bash sudo systemctl start mysqld ``` #### 步骤 7:验证新路径 ```sql SHOW GLOBAL VARIABLES LIKE '%datadir%'; ``` ✅ 应显示: ```ini +---------------+--------------+ | Variable_name | Value | +---------------+--------------+ | datadir | /data/mysql/ | +---------------+--------------+ ``` --- ### ⚠️ 常见问题解决 1. **启动失败报错** - 检查 `/var/log/mysqld.log` 错误日志 - 确认权限设置:`sudo chown -R mysql:mysql /data/mysql` 2. **SELinux 阻止访问** ```bash sudo grep mysqld /var/log/audit/audit.log | audit2why sudo setenforce 0 # 临时关闭SELinux调试 ``` 3. **应用连接失败** 确保所有应用配置文件中的 socket 路径已更新为: `/data/mysql/mysql.sock` --- ### 📊 数据迁移前后对比 | 项目 | 原路径(`/var/lib/mysql`) | 新路径(`/data/mysql`) | |------|--------------------------|-----------------------| | 适用场景 | 默认小规模数据库 | 大型数据库(30GB+) | | 空间限制 | 受系统分区限制 | 可挂载独立大容量硬盘 | | I/O性能 | 与系统共享IO资源 | 专用高性能存储 | | 维护便利性 | ★★★ | ★★★★ | --- ### 🔍 其他注意事项 1. 大数据库迁移建议使用 `rsync` 替代 `cp`: ```bash sudo rsync -av /var/lib/mysql/ /data/mysql/ ``` 2. 云服务器用户可直接挂载新云盘到 `/data/mysql` 3. 迁移后原数据保留 7 天再删除(`sudo rm -rf /var/lib/mysql_bak`)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值