一次MySQL问题的解决

起因

实验室的 MySQL 数据库突然出现了问题,当我尝试更改一个大小为十多个G的表的表结构时,MySQL 报了错,怎么也无法更改。一开始我还不知道是什么原因,所以我就想着先重启一下 MySQL 服务再说吧。于是我用 ssh 登录 MySQL 所在的 Ubuntu 服务器,运行命令:

1
$ sudo service mysql restart

本以为重启一下就好了,结果没重启成功,提示:start: Job failed to start
我又尝试用另外的命令启动 MySQL:

1
$ sudo /etc/init.d/mysql start

结果也没启动成功,还报了这样一个错:

1
ERROR: The partition with /var/lib/mysql is too full! failed!

这下我算是有点明白是什么原因了,我查看一下服务器的磁盘空间使用情况,还真的占满了。

实验室的服务器做了虚拟化,所以 MySQL 所在的数据库是放在专门的一台虚拟机上的,由于当初没有考虑周到,数据库服务器的硬盘空间分配的不够大,所以导致现在都不够用了。所以现在只能重新给这台服务器分配一块大一点的硬盘,然后将数据迁移到上面了。

解决

硬盘添加、分区、格式化与挂载

1、添加

由于数据库服务器是用 VMware vSphere 虚拟出来的虚拟机,所以要给它添加一块硬盘非常简单,在 Vmware 管理界面操作一下就好了。但是虽然给它加上了硬盘,但 Ubuntu 系统暂时还无法使用这块硬盘。这就好比给一台物理机上插上了一块硬盘,直接用是无法使用的,要挂载上去才能使用。

2、分区

硬盘添加后,我们启动服务器,然后运行下面的命令查看一下当前机器上的硬盘以及所属分区情况:

1
$ sudo fdisk -l

这条命令只能在 sudo 下运行才能显示结果。一般来说,我们可以看到现有的硬盘分区。同时,我们还能看到刚刚新添加的硬盘,当然该硬盘现在还不能用,既没有分区,也没有挂载。
我们应该会看到类似这样的话:Disk /dev/sdb doesn/t contain a valid partition table,所以我们首先要对这块硬盘进行分区。

sdb代表是第二块硬盘,与之对应的还有已经存在的sda。现在我们就要对这块新添加进来的硬盘进行分区,运行下面的命令:

1
$ sudo fdisk /dev/sdb

运行之后会提示输入指令以继续下一步操作,我们可以先输入m来查看指令帮助:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Device contains neither a valid DOS partition table, nor Sun, SGI or OSF disklabel
Building a new DOS disklabel with disk identifier 0x0f6c9a4f.
Changes will remain in memory only, until you decide to write them.
After that, of course, the previous content won't be recoverable.

Warning: invalid flag 0x0000 of partition table 4 will be corrected by w(rite)

Command (m for help): m
Command action
a toggle a bootable flag
b edit bsd disklabel
c toggle the dos compatibility flag
d delete a partition
l list known partition types
m print this menu
n add a new partition
o create a new empty DOS partition table
p print the partition table
q quit without saving changes
s create a new empty Sun disklabel
t change a partition's system id
u change display/entry units
v verify the partition table
w write table to disk and exit
x extra functionality (experts only)

Command (m for help):

然后输入n来给硬盘添加一个新的分区。
之后输入p或者e来指定分区为主分区还是扩展分区。
然后输入数字1,表示只创建一个分区,当然也可以输入2/3/4,来创建对应数量的分区。
之后的两个选项可以使用默认,直接回车就可以。
当再次出现Command (m for help):的时候,输入p来显示刚刚操作之后的分区表。
确定无误之后,输入w来保存分区表。

然后再次运行

1
$ sudo fdisk -l

来查看硬盘分区情况,现在应该已经可以识别刚刚的新分区了。

3、格式化

分完区之后,对硬盘进行格式化,将其格式化为ext4文件系统。

1
$ sudo mkfs -t ext4 /dev/sdb

4、挂载

格式化之后,就可以对刚刚的硬盘进行挂载了。

首先,先看一下当前挂载的硬盘:

1
$ duso df -l

在显示的列表中,我们肯定找不到我们刚刚的硬盘分区,因为还没挂载上去嘛。假设我们准备将硬盘挂载到根目录下的 newdisk 目录下,那么我们要先建立该目录,然后执行挂载命令:

1
$ sudo mkdir /newdisk
1
$ sudo mount -t ext4 /dev/sdb /newdisk

再次执行 df -l 命令,应该就能看到我们刚刚挂载的硬盘分区了。

当然这种方式挂载并不是永久的,等到下次机器重启了,又要重新挂载了,所以我们要想办法让它能够永久挂载。办法当然是有的,那就是修改分区文件 /etc/fstab

首先我们先得到分区 /dev/sdb 的 UUID,使用以下命令:

1
$ sudo blkid /dev/sdb

然后用 vim 打开 /etc/fstab 文件,在下面添加一行(下面的 UUID 替换为自己分区的 UUID):

1
UUID=09a58003-d72b-418a-9fbd-8a5b27893165  /newdisk ext4  defaults  0  2

保存,重启,分区应该就永久挂载上了。

数据迁移,配置文件修改

硬盘分区搞定了,接下来就要将数据文件迁移到新的硬盘上了。默认的 MySQL 分区文件应该放在 /var/lib/mysql 目录下,当然也可能不在这里,具体可以去 MySQL 的配置文件 /etc/mysql/my.cnf 下查看 datadir 配置项。

我直接将/var/lib/mysql目录都拷贝到了 /newdisk 下面:

1
$ cp -r /var/lib/mysql /newdisk/

拷贝完成后,/newdisk/mysql 目录以及下面文件的所属用户和权限可能与原先不一样,这可能会造成 MySQL 无法正常启动,所以也要修改。

1
2
$ sudo chown -R mysql:mysql /newdisk/mysql
$ sudo chmod -R 755 /newdisk/mysql

接下来修改 MySQL 的配置文件/etc/mysql/my.cnf,将其datadir的路径改为新的数据路径 /newdisk/mysql。因为 MySQL 的配置文件可能在多个地方都有,读取顺序也有不同,所以要保证修改的配置文件是有效的。

然后重启 MySQL。我以为这下总可以了吧,结果还是没起来。。

打开 MySQL 的错误日志文件 /var/log/mysql/error.log,发现了这样的错误。。。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
170222  6:54:05 [Note] Plugin 'FEDERATED' is disabled.
^G/usr/sbin/mysqld: Can't find file: './mysql/plugin.frm' (errno: 13)
170222 6:54:05 [ERROR] Can't open the mysql.plugin table. Please run mysql_upgrade to create it.
170222 6:54:05 InnoDB: The InnoDB memory heap is disabled
170222 6:54:05 InnoDB: Mutexes and rw_locks use GCC atomic builtins
170222 6:54:05 InnoDB: Compressed tables use zlib 1.2.8
170222 6:54:05 InnoDB: Using Linux native AIO
170222 6:54:06 InnoDB: Initializing buffer pool, size = 10.0G
170222 6:54:06 InnoDB: Completed initialization of buffer pool
170222 6:54:06 InnoDB: Operating system error number 13 in a file operation.
InnoDB: The error means mysqld does not have the access rights to
InnoDB: the directory.
InnoDB: File name ./ibdata1
InnoDB: File operation call: 'open'.
InnoDB: Cannot continue operation.

我在网上查了一番,才明白,这个锅要给 apparmor

权限修改

apparmor 是一个类似 SE-Linux 的东西,用来控制应用程序的权限,就像它名字那样,应用铠甲嘛。所以我们要修改它对于 MySQL 的权限控制。

用 vim 打开文件 /etc/apparmor.d/usr.sbin.mysqld,可以看到 MySQL 对于一些目录、文件的权限,我们可以找到 MySQL 对于原先数据存储路径 /var/lib/mysql/ 的权限。现在我们只要依样画葫芦,再下面添加新的数据存储路径的权限就可以了,简单来说就是添加下面的语句

1
2
3
4
/newdisk/mysql/ r,
/newdisk/mysql/** rwk,
/newdisk/mysql/plugin/ r,
/newdisk/mysql/plugin/*.so* mr,

保存之后,重启 apparmor :

1
$ /etc/init.d/apparmor restart

重新初始化一下 MySQL 数据库

1
$ mysql_install_db

最后再次启动 MySQL

1
$ sudo service mysql start

这次终于成功启动了!

后记

说起来我写的是解决 MySQL 的问题,但中间我花了很多篇幅写的却是如何在 Ubuntu 下添加、挂载硬盘分区。。其实我也是有意为之,通过对 MySQL 的问题处理,我又回顾了一遍挂载分区的操作,我发现这些操作,一段时间不用了,就老是会忘记,所以还不如趁机记录下来。
这次碰到的问题,其实都不是 MySQL 本身的问题,但总算简简单单的搞定了。