MySQL中使用OPTIMIZE TABLE优化表

摘要:MySQL中使用OPTIMIZE TABLE优化表

OPTIMIZE TABLE语句可以重新组织表、索引的物理存储,减少存储空间,提高访问的I/O效率。类似于碎片整理功能。

OPTIMIZE TABLE语法如下:

OPTIMIZE [NO_WRITE_TO_BINLOG | LOCAL]
TABLE tbl_name [, tbl_name] …

默认情况下,MySQL将OPTIMIZE TABLE语句写入二进制日志,以便它们复制到slave服务器。如果不想写二进制日志,使用命令时加上NO_WRITE_To_BINLOG或LOCAL关键字即可。

使用这个语句需要具有对目标表的SELECT、INSERT权限。

在MySQL 5.7里OPTIMIZE TABLE语句支持InnoDB、MyISAM、ARCHIVE引擎的表。它还支持in-memory NDB表的动态列,但不支持固定列,也不支持NDB磁盘表。

我们创建一张InnoDB引擎的表,并插入大量数据。

root@database-one 14:08:  [gftest]> create table testot(id int not null auto_increment primary key,context text) engine=innodb;
Query OK, 0 rows affected (0.02 sec)

root@database-one 14:09:  [gftest]> insert into testot(context) values(repeat('nanjing',100));
Query OK, 1 row affected (0.00 sec)

root@database-one 14:10:  [gftest]> insert into testot(context) values(repeat('shanghai',100));
Query OK, 1 row affected (0.00 sec)

root@database-one 14:10:  [gftest]> insert into testot(context) values(repeat('beijing',100));
Query OK, 1 row affected (0.00 sec)

root@database-one 14:10:  [gftest]> insert into testot(context) values(repeat('shenzhen',100));
Query OK, 1 row affected (0.00 sec)

root@database-one 14:11:  [gftest]> insert into testot(context) select context from testot;
Query OK, 4 rows affected (0.00 sec)
Records: 4  Duplicates: 0  Warnings: 0

root@database-one 14:11:  [gftest]> insert into testot(context) select context from testot;
Query OK, 8 rows affected (0.00 sec)
Records: 8  Duplicates: 0  Warnings: 0

root@database-one 14:11:  [gftest]> insert into testot(context) select context from testot;
Query OK, 16 rows affected (0.01 sec)
Records: 16  Duplicates: 0  Warnings: 0

root@database-one 14:11:  [gftest]> insert into testot(context) select context from testot;
Query OK, 32 rows affected (0.01 sec)
Records: 32  Duplicates: 0  Warnings: 0

root@database-one 14:11:  [gftest]> insert into testot(context) select context from testot;
Query OK, 64 rows affected (0.01 sec)
Records: 64  Duplicates: 0  Warnings: 0

root@database-one 14:12:  [gftest]> insert into testot(context) select context from testot;
Query OK, 128 rows affected (0.01 sec)
Records: 128  Duplicates: 0  Warnings: 0

root@database-one 14:12:  [gftest]> insert into testot(context) select context from testot;
Query OK, 256 rows affected (0.02 sec)
Records: 256  Duplicates: 0  Warnings: 0

root@database-one 14:12:  [gftest]> insert into testot(context) select context from testot;
Query OK, 512 rows affected (0.03 sec)
Records: 512  Duplicates: 0  Warnings: 0

root@database-one 14:12:  [gftest]> insert into testot(context) select context from testot;
Query OK, 1024 rows affected (0.03 sec)
Records: 1024  Duplicates: 0  Warnings: 0

root@database-one 14:12:  [gftest]> insert into testot(context) select context from testot;
Query OK, 2048 rows affected (0.04 sec)
Records: 2048  Duplicates: 0  Warnings: 0

root@database-one 14:12:  [gftest]> insert into testot(context) select context from testot;
Query OK, 4096 rows affected (0.10 sec)
Records: 4096  Duplicates: 0  Warnings: 0

root@database-one 14:12:  [gftest]> insert into testot(context) select context from testot;
Query OK, 8192 rows affected (0.28 sec)
Records: 8192  Duplicates: 0  Warnings: 0

root@database-one 14:12:  [gftest]> insert into testot(context) select context from testot;
Query OK, 16384 rows affected (0.81 sec)
Records: 16384  Duplicates: 0  Warnings: 0

root@database-one 14:12:  [gftest]> insert into testot(context) select context from testot;
Query OK, 32768 rows affected (1.27 sec)
Records: 32768  Duplicates: 0  Warnings: 0

root@database-one 14:12:  [gftest]> insert into testot(context) select context from testot;
Query OK, 65536 rows affected (1.94 sec)
Records: 65536  Duplicates: 0  Warnings: 0

root@database-one 14:12:  [gftest]> commit;
Query OK, 0 rows affected (0.00 sec)

root@database-one 14:12:  [gftest]> exit
Bye
[root@database-one ~]# cd /home/mysql/gftest/
[root@database-one gftest]# ls -lh testot*
-rw-r----- 1 mysql mysql 8.4K 3月  10 14:08 testot.frm
-rw-r----- 1 mysql mysql 120M 3月  10 14:12 testot.ibd

 

表中有超过13万条数据,占磁盘空间大约120M。

我们删除部分数据

root@database-one 14:14:  [gftest]> select count(*) from testot where context like 'beijing%';
+----------+
| count(*) |
+----------+
|    32768 |
+----------+
1 row in set (0.13 sec)

root@database-one 14:15:  [gftest]> delete from testot where context like 'shenzhen%' or context like 'shanghai%';
Query OK, 65536 rows affected (0.88 sec)

root@database-one 14:16:  [gftest]> select count(*) from testot where context like 'beijing%';
+----------+
| count(*) |
+----------+
|    32768 |
+----------+
1 row in set (0.06 sec)

root@database-one 14:16:  [gftest]> exit
Bye
[root@database-one gftest]# cd /home/mysql/gftest/
[root@database-one gftest]# ls -lh testot*
-rw-r----- 1 mysql mysql 8.4K 3月  10 14:08 testot.frm
-rw-r----- 1 mysql mysql 120M 3月  10 14:12 testot.ibd

 


可以看到,删除后查询速度明显提升,但是表占用的磁盘空间没有明显变化。使用OPTIMIZE TABLE语句进行优化。

root@database-one 14:17:  [(none)]> use gftest;
Database changed
root@database-one 14:18:  [gftest]> optimize table testot;
+---------------+----------+----------+-------------------------------------------------------------------+
| Table         | Op       | Msg_type | Msg_text                                                          |
+---------------+----------+----------+-------------------------------------------------------------------+
| gftest.testot | optimize | note     | Table does not support optimize, doing recreate + analyze instead |
| gftest.testot | optimize | status   | OK                                                                |
+---------------+----------+----------+-------------------------------------------------------------------+
2 rows in set (1.94 sec)

root@database-one 14:18:  [gftest]> select count(*) from testot where context like 'beijing%';
+----------+
| count(*) |
+----------+
|    32768 |
+----------+
1 row in set (0.06 sec)

root@database-one 14:22:  [gftest]> exit
Bye
[root@database-one gftest]# cd /home/mysql/gftest/
[root@database-one gftest]# ls -lh testot*
-rw-r----- 1 mysql mysql 8.4K 3月  10 14:18 testot.frm
-rw-r----- 1 mysql mysql  64M 3月  10 14:18 testot.ibd

 

可以看到,表占用磁盘空间减少了大约一半,跟我们删除的数据量相匹配。在这里要注意几点:

  • 对于InnoDB的表,MySQL实现原理其实是在线重建了表及其索引,并重新收集了统计信息。
  • OPTIMIZE TABLE前后查询速度变化不明显,其实是因为本例中表的数据量、索引情况、分散情况等影响,随着这些情况的变化,OPTIMIZE TABLE后的性能优化就能显现出来了。

OPTIMIZE TABLE语句也支持对分区表进行优化。

© 2020, morinson. 版权所有. 欢迎转载,但请保留作者及出处。