在MySQL中,我们经常会使用VARCHAR、TEXT、BLOB等可变长度的文本数据类型。不过,当我们使用这些数据类型之后,我们就不得不做一些额外的工作——MySQL数据表碎片整理。
那么,为什么在使用这些数据类型之后,我们就要对MySQL定期进行碎片整理呢?
现在,我们先来看一个具体的例子。在这里,我们使用如下SQL语句在MySQL自带的TEST数据库中创建名为DEMO的数据表并插入5条测试数据。
然后我们以这5条测试数据为基础,使用如下INSERT INTO语句重复执行多次进行复制性插入。
INSERT INTO DEMO SELECT id, body FROM DEMO;
使用INSERT INTO语句多次插入产生总共约262万条数据
众所周知,MySQL中MyISAM表的数据是以文件形式存储的,我们可以在MySQL存储数据的文件夹中找到数据库test目录下的demo.MYD文件。此时,我们可以看到demo.MYD文件的大小约为50MB。
此时,假如我们需要删除DEMO表中所有ID列小于3的数据(即1和2),于是我们执行如下SQL语句:
DELETE FROM DEMO WHERE id < 3
此时,我们可以看到DEMO表中的数据量只有原来的3/5:
DEMO表中的现有数据量只有原来的3/5,按理说,这个时候demo.MYD文件的大小也应该只有原来的3/5左右。不过,我们再次查看demo.MYD文件时,却惊奇地发现该文件的大小一点都没有变!
那么就究竟是怎么一回事呢?原来,在MySQL中,如果我们删除了表中的大量数据,或者我们对含有可变长度文本数据类型(VARCHAR,TEXT或BLOB)的表进行了很多更改,不过被删除的数据记录仍然被保持在MySQL的链接清单中,因此数据存储文件的大小并不会随着数据的删除而减小。
当我们确定数据需要被清除掉时,那么这些数据就已经成了无用的数据,但是按照MySQL的处理方式,这些数据仍然会占用我们的磁盘空间,从而造成了极大的资源浪费。不仅如此,过大的数据文件还会导致MySQL执行相关数据操作时需要耗费更多的性能和时间。因此,对MySQL的某些数据表进行碎片整理是非常有必要的。
对MySQL进行碎片整理的方法非常简单,因为MySQL已经给我们提供了对应的SQL指令,这个SQL指令就是OPTIMIZE TABLE,其完整语法如下:
OPTIMIZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE table_name1 [, table_name2] ...
从上面的语法描述中,我们可以得知,OPTIMIZE TABLE可以一次性对多个表进行碎片整理,只需要在OPTIMIZE TABLE后面接多个表名,并以英文逗号隔开即可。
此外,OPTIMIZE TABLE语句有两个可选的关键字:LOCAL和NO_WRITE_TO_BINLOG。在默认情况下,OPTIMIZE TABLE语句将会被记录到二进制日志中,如果我们指定了LOCAL或NO_WRITE_TO_BINLOG关键字,则不会记录。当然,一般情况下,我们也无需关注这两个关键字。
现在,我们就使用OPTIMIZE TABLE语句对刚才的DEMO表进行碎片整理。
然后,我们再来查看demo.MYD文件,此时我们就会发现demo.MYD文件的大小已经减小到约为原来的3/5了。
备注:
1.MySQL官方建议不要经常(每小时或每天)进行碎片整理,一般根据实际情况,只需要每周或者每月整理一次即可。
2.OPTIMIZE TABLE只对MyISAM,BDB和InnoDB表起作用,尤其是MyISAM表的作用最为明显。此外,并不是所有表都需要进行碎片整理,一般只需要对包含上述可变长度的文本数据类型的表进行整理即可。
3.在OPTIMIZE TABLE运行过程中,MySQL会锁定表。
4.默认情况下,直接对InnoDB引擎的数据表使用OPTIMIZE TABLE,可能会显示「 Table does not support optimize, doing recreate + analyze instead」的提示信息。这个时候,我们可以用mysqld --skip-new或者mysqld --safe-mode命令来重启MySQL,以便于让其他引擎支持OPTIMIZE TABLE。
在MySQL中,慢查询的界定时间是由MySQL内置参数变量long_query_time来指定的,其默认值为10(单位:秒),我们可以通过show variables like 'long_query_time';指令来查看该参数变量的信息:
不过,在程序开发过程中,我们认为慢速查询的界定时间并没有10秒这么长,依据不同项目的不同需求,我们一般将慢查询的界定时间设定为1~5秒之间。我们可以使用指令set long_query_time = 秒数来设定long_query_time变量的值。
修改了long_query_time参数后,我们还需要让MySQL能够记录下慢查询的日志信息。因为,在默认情况下,MySQL并不会记录慢查询的日志信息。如果要记录慢查询日志,我们需要以命令行方式进入MySQL安装目录\bin目录(也可将该目录添加到PATH环境变量中),使用如下命令重新启动MySQL:
在上述命令中,如果没有指定日志文件名,则日志文件名称默认为主机名-slow.log;如果没有指定文件路径或者指定的文件路径不是绝对路径,则日志文件将默认存放在MySQL配置文件my.ini中参数datadir所指定的目录下。
此外,你也可以将上述命令行启动命令配置到my.ini中的[mysqld]节点下,这样无需每次手动键入上述命令来启动
注意:虽然慢查询的名字中只包含了"查询",实际上并不仅仅表示SELECT查询操作,诸如INSERT、UPDATE、DELETE、CALL等其他DML操作,只要是超过指定时间的,都可以称之为「慢查询」,并且会记录在慢查询日志中。
在LAMP架构的网站开发过程中,有些时候我们需要了解MySQL的服务器状态信息,譬如当前MySQL启动后的运行时间,当前MySQL的客户端会话连接数,当前MySQL服务器执行的慢查询数,当前MySQL执行了多少SELECT语句、执行了多少UPDATE/DELETE/INSERT语句等统计信息,从而便于我们根据当前MySQL服务器的运行状态进行对应的调整或优化工作。
在MySQL中,我们可以使用SHOW STATUS指令语句来查看MySQL服务器的状态信息。下面,我们以DOS命令窗口的形式连接MySQL,并执行show status;指令,我们将看到如下显示信息:
当我们执行show status语句时,MySQL将会列出多达300多条的状态信息记录,其中包括了供我们查看了解的各种信息。不过,如果直接使用show status指令得到300多条记录,会让我们看得眼花缭乱,因此我们希望能够「按需查看」一部分状态信息。这个时候,我们可以在show status语句后加上对应的like子句。例如,我们想要查看当前MySQL启动后的运行时间,我们可以执行如下语句:
--查询当前MySQL本次启动后的运行统计时间 show status like 'uptime';
此时,我们就可以看到如下结果:
+---------------+-------+ | Variable_name | Value | +---------------+-------+ | Uptime | 5667 | +---------------+-------+ 1 row in set (0.00 sec)
同样的,如果我们要本次MySQL启动后执行的SELECT语句的次数,我们可以执行如下语句:
show status like 'com_select';
对应输出结果如下:
+---------------+-------+ | Variable_name | Value | +---------------+-------+ | Com_select | 1 | +---------------+-------+ 1 row in set (0.00 sec)
此外,与WHERE子句中的LIKE关键字类似,show status后的LIKE关键字也可以使用'_' 或'%'等通配符来进行模糊匹配。例如我们可以执行如下语句来查看MySQL服务器的线程信息:
show status like 'Thread_%';
对应输出结果如下:
+-------------------+-------+ | Variable_name | Value | +-------------------+-------+ | Threads_cached | 0 | | Threads_connected | 1 | | Threads_created | 1 | | Threads_running | 1 | +-------------------+-------+ 4 rows in set (0.00 sec)
值得注意的是,在上述show status like 'com_select'指令的执行示例中,显示的SELECT语句统计信息仅仅表示当前会话连接执行的SELECT语句数量。因为,show status指令的完整语法如下:
SHOW [统计范围] STATUS [LIKE '状态项名称'] --统计范围关键字分为GLOBAL和SESSION(或LOCAL)两种。
在show status的完整语法中,"[]"中的部分是可选的,如果我们的show status语句中不包含统计范围关键字,则默认统计范围为SESSION,也就是只统计当前连接的状态信息。如果我们需要查询自当前MySQL启动后所有连接执行的SELECT语句总数,我们可以执行如下语句:
show global status like 'com_select';
以上即是show status的详细用法。由于show status的状态统计项较多,我们就不再一一解释每个统计项的具体含义,在这里,我们仅列出部分常用的状态信息查看语句: