今天要说的mv/rm这两个命令的“趣事”是再使用mysql时遇到的。我先将问题贴出来,你也先思考一下,看看你的答案是什么。
环境:mysql5.5,innodb_file_per_table=1
问题:一个mysql实例在跑,数据库里面新创建一张表t1(因为是独立表空间所以有一个数据文件叫做t1.ibd),然后我rm或者mv t1.ibd文件,然后继续对这个表进行操作,DML/DDL,请问这个操作可以成功吗?为什么?
....(5分钟已过)
好了,下面我将解释现象及原因。
现象:物理mv/rm 对于DML/DDL都可以操作。
原因:1.对于mv,可能你的直觉是我这个数据文件都不存在了,那么当对这个表进行操作时(比如insert)数据存放到哪呢?或者delete时从哪个数据文件删除呢?有些朋友或许会认为这些操作都是在内存中进行的,但是我还要告诉你一个现象:当进行insert操作时,数据文件(我这里是t1.ibd)在不断的增大,所以从这点就可以说明这些操作最终还是反映到了物理磁盘上。那么为什么会出现这种情况呢?如果比较懂linux的文件操作或许很快就会明白原因--文件描述符(file descriptor,Linux操作文件的一种方式)在起作用。关于文件描述符这里不多说,网上资料很丰富。在之前我明确的是说是实例起来后新建一张表,那么在建完表后系统中就有一个针对这个表的数据文件的文件描述符被打开,而恰恰在mv的时候,linux会实时的更新这个文件描述符是打开的具体那个文件,比如我之前刚创建表时文件描述符16是打开的t1.ibd这个文件,现在我对t1.ibd这个文件mv操作改成其他文件名(比如t1.ibd.bak),那么你再去查看这个文件描述符你会发现16这个文件描述符已经指向到t1.ibd.bak这个文件。而mysql操作文件的时候是根据文件描述符进行,也就是说它利用文件描述符16打开了一个数据文件data1,那么在它没有关闭这个文件描述符之前,对data1的操作都是通过16来操作,而mv会实时更新文件描述符的信息,因为我们能正常进行DML/DDL也就理所当然了。又有朋友或许会问,那是不是将这个文件描述符关闭后再对那个表操作就会出问题?答案是肯定会出问题的,因为当我们关闭文件描述符后,在此打开就会发现这个表的数据文件已经不存在,因此会报错。
2.那对于rm掉数据文件以后为什么也可以继续对表进行操作呢?答案是因为我对数据文件进行rm时,还有其他的进程在使用它(mysqld),因为此时并不是立马删除,不过如果此时你去查看这个数据文件的文件描述符你会发现是deleted 状态,类似于下面这种:
12 -> /mnt/ddb/zbs/mysql/node-2/ibq01gM7 (deleted)
当你这个使用这个数据文件的进程退出以后(比如说mysqld重启),那么这个文件也就真正被删除掉,因此你此时再次对这个表进行操作也会报错。我有一个地方有疑问:此时文件虽然没真正被立马删除,但是我也完全查不到这个文件的信息,不知道有什么方法可以查看到?另外,当我对一个进程正在使用的文件进行rm的时候,为什么这里不提示或报错说这个文件正在被使用或你不能删除?
最后还说明一下前面一个遗漏的问题:为什么我在一个表的数据文件被删除的情况下还可以正常启动mysqld?这是因为我是正常关闭mysqld的,关闭之前什么刷脏页,merge操作都进行完了,再次启动mysqld的时候没有那个crash recovery的步骤,因此也就不会去检验数据文件是否处于一致性。另外我也尝试过kill -9关闭mysqld(也是像前面一样删除了一个表的数据文件),能正常的再次启动mysqld,虽然说此时非正常关闭要进行crash recovery,但是因为kill -9时脏页也会正常的刷到了物理磁盘,crash时不会检测到这个表的相关数据页,所以也不会报错。
注:有时候误删除了进程正在使用的文件,不要着急,或许可以尝试这个方法进行恢复