如何查找和修复碎片化的MySQL表


27

我使用MySQLTuner指出了一些表是零散的。我用了

mysqlcheck-优化-A

优化所有表格。它修复了一些表,但是MySQLTuner仍然找到19个表碎片。如何查看需要对哪些表进行碎片整理?也许优化表可以在mysqlcheck没有的地方工作?还是我应该尝试什么?


1
我有一个类似的问题。我正在使用MySQL 5.5建立新的数据库,并且某些InnoDB表永不碎片化。我想知道InnoDB表的Data_free检查(显示在KayakJim的答案中)是否不正确。
docwhat 2011年

Answers:


38

简短的答案:

select  ENGINE, TABLE_NAME,Round( DATA_LENGTH/1024/1024) as data_length , round(INDEX_LENGTH/1024/1024) as index_length, round(DATA_FREE/ 1024/1024) as data_free from information_schema.tables  where  DATA_FREE > 0;

“您必须知道”的答案

首先,您必须了解Mysql表在更新行时会碎片化,所以这是正常情况。假设创建了一个表(例如,使用带有数据的转储导入),所有行将无碎片地存储在许多固定大小的页面中。更新可变长度行时,包含该行的页面将分为两个或多个页面以存储更改,并且这两个新的(或多个)页面包含填充未使用空间的空白。

这不会影响性能,除非碎片会增长太多。太多的碎片,让我们看看您要查询的查询:

  select  ENGINE, TABLE_NAME,Round( DATA_LENGTH/1024/1024) as data_length , round(INDEX_LENGTH/1024/1024) as index_length, round(DATA_FREE/ 1024/1024) as data_free from information_schema.tables  where  DATA_FREE > 0;

DATA_LENGTH和INDEX_LENGTH是数据和索引使用的空间,而DATA_FREE是所有表页(碎片)中未使用的字节总数。

这是真实生产表的示例

| ENGINE | TABLE_NAME               | data_length | index_length | data_free |
| InnoDB | comments                 |         896 |          316 |         5 |

在这种情况下,我们有一个使用(896 + 316)= 1212 MB的表,并且数据的可用空间为5 MB。这意味着以下情况的“碎片比率”:

5/1212 = 0.0041

...这是一个非常低的“碎片率”。

我一直在使用比率接近0.2(意味着20%的空格)的表,并且从来没有注意到查询速度变慢,即使我优化了表,性能也一样。但是在800MB的表上应用优化表会花费很多时间,并且会阻塞表几分钟,这在生产中是不可行的。

因此,如果您考虑在性能方面取胜,而在优化表上浪费时间,则我不建议“优化”。

如果您认为存储更好,请查看比率并查看优化时可以节省多少空间。通常不会太多,所以我更喜欢“不优化”。

并且,如果您进行了优化,则下一个更新将通过将页面分成两个或更多来创建空白。但是更新碎片表要比不碎片表快,因为如果表被碎片化,则对一行的更新不一定会拆分页面。

我希望这可以帮助你。


1
尽管这是几年前的答案,但我想指出的是data_free是整个表空间的统计信息,而不是各个表的统计信息。如果将多个表存储在一个表空间中,则data_free可能会误导您认为该表需要进行碎片整理,而这仅意味着表空间中存在可用扩展区。运行优化表不会减少可用范围。对表进行碎片整理甚至可能会增加可用范围。
比尔·卡温

14

只需添加到Felipe-Rojas的答案中,您就可以计算片段比率作为查询的一部分:

select ENGINE,
  concat(TABLE_SCHEMA, '.', TABLE_NAME) as table_name,
  round(DATA_LENGTH/1024/1024, 2) as data_length,
  round(INDEX_LENGTH/1024/1024, 2) as index_length,
  round(DATA_FREE/1024/1024, 2) as data_free,
  (data_free/(index_length+data_length)) as frag_ratio
FROM information_schema.tables
WHERE DATA_FREE > 0
ORDER BY frag_ratio DESC;

如果一个表的碎片很小(小于5%?),那么您可以不理会它。

更大的东西,您将需要根据数据库的使用情况,锁定表等进行评估,以了解对表进行碎片整理的重要性。


2

优化表确实可以解决您遇到的问题。

如果只有几个数据库,则可以使用PHPMyAdmin遍历所有数据库。选择开销较大的表,然后选择进行优化。

如果您有很多数据库,则最好使用另一种方法。

我在cron中使用以下PHP脚本设置来每小时运行一次。

$DB = new mysqli ('localhost', 'DbUser', 'DbPassword');
$results = $DB->query('show databases');
$allDbs = array();
while ($row = $results->fetch_array(MYSQLI_NUM))
{
    $allDbs[] = $row[0];
}
$results->close();
foreach ($allDbs as $dbName)
{
    if ($dbName != 'information_schema' && $dbName != 'mysql')
    {
        $DB->select_db($dbName);
        $results = $DB->query('SHOW TABLE STATUS WHERE Data_free > 0');
        if ($results->num_rows > 0)
        {
            while ($row = $results->fetch_assoc())
            {
                $DB->query('optimize table ' . $row['Name']);
            }
        }
        $results->close();
    }
}
$DB->close();

3
我很确定这mysqlcheck --optimize -A与SQL相同OPTIMIZE TABLE <tablename>;
docwhat 2011年

2

我浏览了此页面,发现Felipe-Rojas和sysadmiral的查询非常有帮助。但就我而言,我在WHM的phpMyAdmin中运行查询,由于未列出数据库,并且只有几个数据库具有相同的表名,因此仅获得TABLE_NAME的帮助不大。因此,只需添加TABLE_SCHEMA也将提供该列。

select  ENGINE, TABLE_SCHEMA, TABLE_NAME, Round( DATA_LENGTH/1024/1024) as data_length , round(INDEX_LENGTH/1024/1024) as index_length, round(DATA_FREE/ 1024/1024) as data_free, (data_free/(index_length+data_length)) as frag_ratio from information_schema.tables  where  DATA_FREE > 0 order by frag_ratio desc

显示数据库

ENGINE  | TABLE_SCHEMA  | TABLE_NAME    | data_length   | index_length  | data_free | frag_ratio

InnoDB  | db_name       | db_table      | 0             | 0             | 8         | 170.6667

为了“修复”,我对每个表使用了phpMyAdmin中的Defragment table链接,从而导致对phpMyAdmin执行较高的“ frag_ratio”:

ALTER TABLE `table_name` ENGINE = InnoDB;

0

使用MySQL的InnoDB Engine的表基本上不需要OPTIMIZEd

Data_freefrom或information_schema.tablesor 的值SHOW TABLE STATUS通常不是零,即使您认为自己已经做了所有事情,也可以对表进行碎片整理。此外,该指标只是可能发生的和确实发生的几个碎片之一。(此外,浪费了块,还原列表,索引BTree与数据BTree等的空间,等等。

并且innodb_file_per_table使的使用复杂化Data_free。如果表位于中ibdata1,则Data_free引用整个表空间;一个相当无用的数字。如果表在其自己的.ibd文件中,则可能是表大小的几MB或百分之几,以较大者为准。

只有当你已经删除大量的行不打算重新填充表,可能它是值得的运行OPTIMIZE TABLE

PARTITIONs还会显示令人不安的Data_free,因为每个分区通常显示4-7MB的“可用空间”。而且这不会消失。

为什么要进行碎片整理?

  • 要将空间还给操作系统?好吧,如果您有的话,您可能会短暂地实现这一目标innodb_file_per_table=1。但是,当您添加行时,您将从操作系统中将其取回。
  • 要加快访问速度?算了吧。磁盘上块的布局是相对随机的,并且已经存在了几十年。半个世纪前,重新排列块有些重要。
  • 要重新平衡B树?所以?他们将立即再次变得不平衡。随机插入的BTree的稳态为69%。甚至没有考虑到这一点Data_free
  • MySQLTuner说要?该产品需要冷却。

历史记录。当我主要使用MyISAM表帮助DBA时,我发现1000个表中有2个受到了每月的 帮助OPTIMIZE。从那时起,我就使用了数千个InnoDB表,但尚未发现可能由解决的性能问题OPTIMIZE。(当然,存在磁盘空间问题OPTIMIZE可能会有所帮助,但这会变得棘手-通常DBA没有足够的磁盘空间来运行OPTIMIZE!)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.