- Porque
avg_row_length
édata_length / rows
.
data_length
é basicamente o tamanho total da tabela no disco . Uma tabela InnoDB é mais do que apenas uma lista de linhas. Portanto, há essa sobrecarga extra. - Porque uma linha do InnoDB é mais do que os dados.
Semelhante ao acima, cada linha vem com alguma sobrecarga. Então isso vai aumentar o tamanho de uma linha. Uma tabela InnoDB também não é apenas uma lista de dados amontoados. Ele precisa de um pouco de espaço vazio extra para funcionar com eficiência.
- Porque as coisas são armazenadas em discos em blocos e esses blocos nem sempre estão cheios.
Os discos armazenam coisas geralmente em 4K, 8K ou 16K blocos . Às vezes, as coisas não se encaixam perfeitamente nesses blocos, então você pode obter algumas vazias espaço .
Como veremos abaixo, o MySQL vai alocar a tabela em blocos. E vai alocar muito mais do que precisa para evitar ter que aumentar a tabela (o que pode ser lento e levar a fragmentação de disco o que torna as coisas ainda mais lentas).
Para ilustrar isso, vamos começar com uma tabela vazia.
mysql> create table foo ( id smallint(5) unsigned NOT NULL );
mysql> select data_length, table_rows, avg_row_length from information_schema.tables where table_name = 'foo';
+-------------+------------+----------------+
| data_length | table_rows | avg_row_length |
+-------------+------------+----------------+
| 16384 | 0 | 0 |
+-------------+------------+----------------+
Ele usa 16K, ou quatro blocos de 4K, para não armazenar nada. A tabela vazia não precisa desse espaço, mas o MySQL o alocou assumindo que você colocaria um monte de dados nela. Isso evita ter que fazer uma dispendiosa realocação em cada inserto.
Agora vamos adicionar uma linha.
mysql> insert into foo (id) VALUES (1);
mysql> select data_length, table_rows, avg_row_length from information_schema.tables where table_name = 'foo';
+-------------+------------+----------------+
| data_length | table_rows | avg_row_length |
+-------------+------------+----------------+
| 16384 | 1 | 16384 |
+-------------+------------+----------------+
A mesa não ficou maior, há todo aquele espaço não utilizado dentro desses 4 blocos que ela tem. Há uma linha que significa um avg_row_length de 16K. Claramente absurdo. Vamos adicionar outra linha.
mysql> insert into foo (id) VALUES (1);
mysql> select data_length, table_rows, avg_row_length from information_schema.tables where table_name = 'foo';
+-------------+------------+----------------+
| data_length | table_rows | avg_row_length |
+-------------+------------+----------------+
| 16384 | 2 | 8192 |
+-------------+------------+----------------+
Mesma coisa. 16 K são alocados para a tabela, 2 linhas usando esse espaço. Um resultado absurdo de 8K por linha.
À medida que insiro mais e mais linhas, o tamanho da tabela permanece o mesmo, está usando cada vez mais espaço alocado e o
avg_row_length
se aproxima da realidade. mysql> select data_length, table_rows, avg_row_length from information_schema.tables where table_name = 'foo';
+-------------+------------+----------------+
| data_length | table_rows | avg_row_length |
+-------------+------------+----------------+
| 16384 | 2047 | 8 |
+-------------+------------+----------------+
Aqui também começamos a ver
table_rows
tornar impreciso. Eu definitivamente inseri 2048 linhas. Agora, quando eu insiro um pouco mais...
mysql> select data_length, table_rows, avg_row_length from information_schema.tables where table_name = 'foo';
+-------------+------------+----------------+
| data_length | table_rows | avg_row_length |
+-------------+------------+----------------+
| 98304 | 2560 | 38 |
+-------------+------------+----------------+
(Eu inseri 512 linhas e
table_rows
voltou à realidade por algum motivo) O MySQL decidiu que a tabela precisa de mais espaço, então foi redimensionada e pegou muito mais espaço em disco.
avg_row_length
apenas pulou novamente. Ele pegou muito mais espaço do que o necessário para essas 512 linhas, agora são 96K ou 24 blocos de 4K, supondo que será necessário mais tarde. Isso minimiza quantas realocações potencialmente lentas ele precisa fazer e minimiza a fragmentação do disco.
Isso não significa que todo o espaço foi preenchido . Significa apenas que o MySQL pensou que estava cheio o suficiente para precisar de mais espaço para ser executado com eficiência. Se você quiser ter uma ideia do motivo, veja como uma tabela de hash opera. Não sei se o InnoDB usa uma tabela de hash, mas o princípio se aplica:algumas estruturas de dados funcionam melhor quando há algum espaço vazio.
O disco usado por uma tabela está diretamente relacionado ao número de linhas e tipos de colunas na tabela, mas a fórmula exata é difícil de descobrir e mudará de versão para versão do MySQL. Sua melhor aposta é fazer alguns testes empíricos e se resignar que você nunca obterá um número exato.