mysql 运行OPTIMIZE TABLE对表进行碎片整理以获得更好的性能

x4shl7ld  于 2022-10-31  发布在  Mysql
关注(0)|答案(3)|浏览(188)

我阅读了与优化表相关的文章,需要进一步澄清。
我对MariaDBv10.6.7运行了一个mysqltuner.pl,得到了一些建议,其中一个建议是运行优化表。

Run OPTIMIZE TABLE to defragment tables for better performance
      OPTIMIZE TABLE `DB`.`TableA`; -- can free 426 MB
    Total freed space after theses OPTIMIZE TABLE : 426 Mb

问题:

1.是否可以在InnoDB表中运行OPTIMIZE TABLE 'TableA'以获得更好的性能(据我所知,它会清除磁盘中未使用的空间,但它会有助于提高性能)?
1.因为我使用的是InnoDB,所以它说“表不支持优化,而是进行重新创建+分析”。我需要运行Alter Table ... OPTIMIZE而不是OPTIMIZE TABLE吗(我猜两者都是链接的)?
1.即使我按照建议运行了优化表,我仍然看到表426MB没有被它完全释放(它被减少到384MB)。我们不能释放整个大小吗?

> select * from information_schema.TABLES where TABLE_NAME = "TableA"\G;

***************************1. row***************************

  TABLE_CATALOG: def
   TABLE_SCHEMA: DB
     TABLE_NAME: TableA
     TABLE_TYPE: BASE TABLE
         ENGINE: InnoDB
        VERSION: 10
     ROW_FORMAT: Dynamic
     TABLE_ROWS: 1600474
 AVG_ROW_LENGTH: 207
    DATA_LENGTH: 332136448
MAX_DATA_LENGTH: 0
   INDEX_LENGTH: 0
      DATA_FREE: 446693376 (426MB)
 AUTO_INCREMENT: NULL
    CREATE_TIME: 2022-08-09 16:01:05
    UPDATE_TIME: 2022-08-09 16:04:47
     CHECK_TIME: NULL
TABLE_COLLATION: utf8_general_ci
       CHECKSUM: NULL
 CREATE_OPTIONS: partitioned
  TABLE_COMMENT:
1 row in set (0.01 sec)

ERROR: No query specified

> optimize table TableA;
+-----------+----------+----------+--------------------------------------------------------------------+
| Table     | Op       | Msg_type | Msg_text                                                           |
+-----------+----------+----------+--------------------------------------------------------------------+
| DB.TableA | optimize | note     | Table does not support optimize, doing recreate + analyze instead  |
| DB.TableA | optimize | status   | OK                                                                 |
+-----------+----------+----------+--------------------------------------------------------------------+
2 rows in set (8.25 sec)

127.0.0.1:3307> select * from information_schema.TABLES where TABLE_NAME = "TableA"\G;

***************************1. row***************************

  TABLE_CATALOG: def
   TABLE_SCHEMA: DB
     TABLE_NAME: TableA
     TABLE_TYPE: BASE TABLE
         ENGINE: InnoDB
        VERSION: 10
     ROW_FORMAT: Dynamic
     TABLE_ROWS: 1600474
 AVG_ROW_LENGTH: 193
    DATA_LENGTH: 310116352
MAX_DATA_LENGTH: 0
   INDEX_LENGTH: 0
      DATA_FREE: 402653184 (384MB)
 AUTO_INCREMENT: NULL
    CREATE_TIME: 2022-08-09 16:47:00
    UPDATE_TIME: NULL
     CHECK_TIME: NULL
TABLE_COLLATION: utf8_general_ci
       CHECKSUM: NULL
 CREATE_OPTIONS: partitioned
  TABLE_COMMENT:
1 row in set (0.27 sec)

使用与www.example.com中相同的逻辑mysqltuner.pl来查找可用大小。不确定查询背后的逻辑。

SELECT CONCAT(CONCAT(TABLE_SCHEMA, '.'), TABLE_NAME),cast(DATA_FREE as signed) FROM information_schema.TABLES WHERE TABLE_SCHEMA NOT IN ('information_schema','performance_schema', 'mysql') AND DATA_LENGTH/1024/1024>100 AND cast(DATA_FREE as signed)*100/(DATA_LENGTH+INDEX_LENGTH+cast(DATA_FREE as signed)) > 10 AND NOT ENGINE='MEMORY' $not_innodb

更新1:
根据要求,添加了tableA的输出

> SHOW TABLE STATUS WHERE name LIKE "TableA"\G;

***************************1. row***************************

            Name: TableA
          Engine: InnoDB
         Version: 10
      Row_format: Dynamic
            Rows: 1875385
  Avg_row_length: 3
     Data_length: 5685248
 Max_data_length: 0
    Index_length: 0
       Data_free: 1991245824
  Auto_increment: NULL
     Create_time: 2022-10-25 10:53:40
     Update_time: 2022-10-25 11:34:32
      Check_time: NULL
       Collation: utf8mb3_general_ci
        Checksum: NULL
  Create_options: partitioned
         Comment:
Max_index_length: 0
       Temporary: N
1 row in set (0.002 sec)

> Show create table TableA;

| TableA | CREATE TABLE `TableA` (
  `Col1` mediumint(8) unsigned NOT NULL,
  `Col2` tinyint(4) NOT NULL,
  `Col3` tinyint(4) NOT NULL,
  `Col4` tinyint(4) NOT NULL,
  `Col5` tinyint(4) NOT NULL,
  `Col6` smallint(4) NOT NULL,
  `timestamp` int(11) NOT NULL,
  `Col7` bigint(20) DEFAULT NULL,
  `Col8` bigint(20) DEFAULT NULL,
  `Col9` tinyint(4) DEFAULT NULL,
  :::
  `Col40` tinyint(4) DEFAULT NULL,
  PRIMARY KEY (`Col1` ,`Col2` ,`Col3` ,`Col4` ,`Col5` ,`Col6`,`timestamp`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3
 PARTITION BY RANGE (`timestamp`)
(PARTITION `p2022_10_11_02_00_00` VALUES LESS THAN (1665437400) ENGINE = InnoDB,
 PARTITION `p2022_10_11_03_00_00` VALUES LESS THAN (1665441000) ENGINE = InnoDB,
 PARTITION `p2022_10_11_04_00_00` VALUES LESS THAN (1665444600) ENGINE = InnoDB,
 ....
 PARTITION `p2022_10_25_12_00_00` VALUES LESS THAN (1666683000) ENGINE = InnoDB)
 //partitioned by timestamp. Partitioned more than 360
t3psigkw

t3psigkw1#

简短回答:“永远不要”在InnoDB表上使用OPTIMIZE TABLE。(这基本上是浪费时间。即使您看到了改进,它也会很快被吞噬掉。)
为什么在你的情况下没有节省呢?每个PARTITION预先分配4 MB-7 MB的“自由”空间。“建议”没有注意到这一点;所以你没有存款。
如果你需要对很多行进行DELETE,我们来谈谈。通常有更好的方法来完成这一任务,而且它们不会导致你去做OPTIMIZE
这个表有大约70个分区?而只有160万行?你从PARTITIONing中得到了什么好处吗?(我怀疑)给我们看看主要的查询;我将带您进入一些更好的索引和没有分区。

u1ehiz5o

u1ehiz5o2#

从你的问题中的信息来看,你的数据库一切都很好。
1.是的,在生产表上使用OPTIMIZE是可以的。它和它的InnoDB时代的替代品使用联机数据定义语言语句。
1.当您对InnoDB表使用OPTIMIZE TABLE时,服务器会执行正确的操作。
1.像www.example.com这样的调优工具sqltuner.pl提供了有关节省空间等方面的 * 估计 ,而不是硬性数字。
1.在许多情况下,很难衡量OPTIMIZE TABLE所带来的性能改进。具有FULLTEXT索引的忙碌表是个例外。
有些背景。
当应用程序更改表(使用INSERT、UPDATE和DELETE)时,有时会在表的数据结构中留下未使用的空间。UPDATE行以将较长得VARCHAR()值更改为较短得值.
FULLTEXT索引在变更时也会留下空白空间。
诊断程序.和information_schema.TABLES.DATA_FREE
估计 * 在您重组(优化)表后可用的空间。这些估计值对于传统的MyISAM存储引擎比对于像InnoDB这样的新存储引擎更准确。因此,您的表重组没有产生您的工具估计的那么多可用空间并不奇怪。
一些参考资料:

ctrmrzij

ctrmrzij3#

使用OPTIMIZE TABLE对InnoDB表进行碎片整理,这可能会减小大小,尤其是在发生大数据更改(如批量删除)之后。
但实际上,它并不能显著提高性能。性能不是使用OPTIMIZE TABLE的原因。
关于“Table does not support optimize”的错误消息是因为它并不像OPTIMIZE TABLE那样在旧的MyISAM存储引擎上执行相同的操作。对于InnoDB,它确实支持该语句,并且它确实做了一些有用的事情:将行复制到一个新的表空间,并在此过程中重建二级索引。
对于InnoDB,OPTIMIZE TABLE <name>的作用与ALTER TABLE <name> FORCEALTER TABLE <name> ENGINE=InnoDB相同。ALTER TABLE <name> OPTIMIZE没有语法(除了指定的分区)。
至于你的神秘的高“data_free”,我想知道你是不是把这个表存储在shared system tablespace中,也就是说,配置选项innodb_file_per_table被禁用了,这导致表一起存储在系统表空间中,按照惯例,系统表空间是在数据目录下的一个文件ibdata1中。
如果表位于系统表空间中,则OPTIMIZE TABLE不会将表复制到新的表空间中,它只是将数据移动到系统表空间的不同区域中,并且不会恢复任何空闲空间。此外,报告整个表空间的“data_free”,而不仅仅是各个表的“data_free”。在优化该表空间中的表后,“data_free”可能不会有太大变化。
您可以通过以下方式检查表占用了哪些表空间:

SELECT SPACE FROM INFORMATION_SCHEMA.INNODB_SYS_TABLES
WHERE NAME = '<schemaname>.<tablename>';

系统表空间的空间ID为0。如果不是0,则表位于自己单独的表空间中。
我希望人们不要使用mysqltuner。
众所周知,mysqltuner提供的建议不可靠。我见过一些案例,它的建议表明作者对MySQL的内部结构了解不够,无法提供建议。我认为mysqltuner是导致大量不必要工作的原因。
下面是一些过去的例子,我试图纠正那些被mysqltuner报告弄糊涂的人的误解:

  • InnoDB写日志效率是否过高超过100%(1953.15%)?
  • 小型数据库的MySQL tmp_table_size
  • Mysql每线程内存,变量导致线程内存占用?

相关问题