Answers:
简短的答案:
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的表上应用优化表会花费很多时间,并且会阻塞表几分钟,这在生产中是不可行的。
因此,如果您考虑在性能方面取胜,而在优化表上浪费时间,则我不建议“优化”。
如果您认为存储更好,请查看比率并查看优化时可以节省多少空间。通常不会太多,所以我更喜欢“不优化”。
并且,如果您进行了优化,则下一个更新将通过将页面分成两个或更多来创建空白。但是更新碎片表要比不碎片表快,因为如果表被碎片化,则对一行的更新不一定会拆分页面。
我希望这可以帮助你。
只需添加到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%?),那么您可以不理会它。
更大的东西,您将需要根据数据库的使用情况,锁定表等进行评估,以了解对表进行碎片整理的重要性。
优化表确实可以解决您遇到的问题。
如果只有几个数据库,则可以使用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();
mysqlcheck --optimize -A
与SQL相同OPTIMIZE TABLE <tablename>;
我浏览了此页面,发现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;
使用MySQL的InnoDB Engine的表基本上不需要OPTIMIZEd
。
Data_free
from或information_schema.tables
or 的值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
。但是,当您添加行时,您将从操作系统中将其取回。Data_free
。历史记录。当我主要使用MyISAM表帮助DBA时,我发现1000个表中有2个受到了每月的 帮助OPTIMIZE
。从那时起,我就使用了数千个InnoDB表,但尚未发现可能由解决的性能问题OPTIMIZE
。(当然,存在磁盘空间问题OPTIMIZE
可能会有所帮助,但这会变得棘手-通常DBA没有足够的磁盘空间来运行OPTIMIZE
!)