Mysql
 sql >> Base de données >  >> RDS >> Mysql

Longueur moyenne des rangées plus élevée que possible

  • Parce que avg_row_length est data_length / rows .

data_length est essentiellement la taille totale de la table sur le disque . Une table InnoDB est plus qu'une simple liste de lignes. Il y a donc cette surcharge supplémentaire.

  • Parce qu'une ligne InnoDB est plus que les données.

Comme ci-dessus, chaque ligne est accompagnée de frais généraux. Cela va donc ajouter à la taille d'une ligne. Une table InnoDB n'est pas seulement une liste de données entassées. Il a besoin d'un peu d'espace vide supplémentaire pour fonctionner efficacement.

  • Parce que les éléments sont stockés sur des disques en blocs et que ces blocs ne sont pas toujours pleins.

Les disques stockent généralement des éléments en 4K, 8K ou 16K blocs . Parfois, les choses ne rentrent pas parfaitement dans ces blocs, vous pouvez donc obtenir des éléments vides espace .

Comme nous le verrons ci-dessous, MySQL va allouer la table en blocs. Et il va allouer beaucoup plus que nécessaire pour éviter d'avoir à développer la table (ce qui peut être lent et conduire à fragmentation du disque ce qui rend les choses encore plus lentes).

Pour illustrer cela, commençons par un tableau vide.

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 |
+-------------+------------+----------------+

Il utilise 16K, ou quatre blocs 4K, pour ne rien stocker. La table vide n'a pas besoin de cet espace, mais MySQL l'a alloué en supposant que vous allez y mettre un tas de données. Cela évite d'avoir à faire une réallocation coûteuse sur chaque encart.

Ajoutons maintenant une ligne.

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 |
+-------------+------------+----------------+

La table n'a pas grossi, il y a tout cet espace inutilisé dans ces 4 blocs dont elle dispose. Il y a une ligne qui signifie un avg_row_length de 16K. Clairement absurde. Ajoutons une autre ligne.

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 |
+-------------+------------+----------------+

Même chose. 16K sont alloués pour la table, 2 lignes utilisant cet espace. Un résultat absurde de 8K par ligne.

Au fur et à mesure que j'insère de plus en plus de lignes, la taille de la table reste la même, elle utilise de plus en plus de son espace alloué, et le avg_row_length se rapproche de la réalité.

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 |
+-------------+------------+----------------+

Ici aussi, nous commençons à voir table_rows devenir imprécis. J'ai définitivement inséré 2048 lignes.

Maintenant, quand j'en insère d'autres...

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 |
+-------------+------------+----------------+

(J'ai inséré 512 lignes et table_rows est revenu à la réalité pour une raison quelconque)

MySQL a décidé que la table avait besoin de plus d'espace, elle a donc été redimensionnée et a pris beaucoup plus d'espace disque. avg_row_length vient de sauter à nouveau.

Il a pris beaucoup plus d'espace qu'il n'en a besoin pour ces 512 lignes, maintenant c'est 96K ou 24 blocs 4K, en supposant qu'il en aura besoin plus tard. Cela minimise le nombre de réallocations potentiellement lentes qu'il doit effectuer et minimise la fragmentation du disque.

Cela ne signifie pas que tout cet espace a été rempli . Cela signifie simplement que MySQL pensait qu'il était suffisamment plein pour avoir besoin de plus d'espace pour fonctionner efficacement. Si vous voulez savoir pourquoi il en est ainsi, regardez comment une table de hachage fonctionne. Je ne sais pas si InnoDB utilise une table de hachage, mais le principe s'applique :certaines structures de données fonctionnent mieux lorsqu'il y a de l'espace vide.

Le disque utilisé par une table est directement lié au nombre de lignes et aux types de colonnes de la table, mais la formule exacte est difficile à comprendre et changera d'une version à l'autre de MySQL. Votre meilleur pari est de faire des tests empiriques et de vous résigner à ne jamais obtenir de chiffre exact.