Générer un jeu de données : shell, mysqlslap, et procédure stockée

19 août 2008 par arnaud

Le prochain article de notre série consacrée aux index MySQL approche et j’ai besoin pour ce prochain épisode de générer une table de test de la forme suivante :

CREATE TABLE `t` (
`id` mediumint(8) unsigned NOT NULL auto_increment,
`date` timestamp NOT NULL,
`flag` tinyint(4) NOT NULL default '0',
PRIMARY KEY  (`id`),
KEY `flag` (`flag`)
) ENGINE=MyISAM;


La structure est définie, reste à alimenter la table, disons 1 million d’enregistrements.

La valeur du champ « flag » est importante pour nos tests ultérieurs, sa valeur doit pour le moment être comprise entre 0 et 1, le tout à peu près uniformément distribué.

La requête sera la suivante :

INSERT INTO test.t (flag) VALUES (ROUND(RAND()));

Il faut maintenant exécuter celle-ci 1 million de fois.
Voyons ce que cela donne en utilisant le shell, mysqlslap ou bien encore une procédure stockée.

Le shell

L’idée : générer un fichier texte des données à insérer puis exécuter le fichier de sortie comme entrée du client mysql.

Cette méthode n’est pas la plus efficace du lot, surtout pour 1 million de lignes, mais on peut néanmoins arriver à ses fins :

debian:/tmp# (for i in `seq 1 1000000`; do echo "insert into test.t(flag) values(round(rand()));"; done;) > /tmp/insertions.txt

Puis :

debian:/tmp# mysql test < /tmp/insertions.txt

Notre million de lignes est inséré.

mysqlslap

mysqlslap est un outil de benchmark que nous avons étudié récemment. Nous détournons ici son utilisation première pour finalement exécuter notre million de requêtes :

debian:/usr/local/mysql51# ./bin/mysqlslap --user=root --socket=/tmp/mysql.sock --concurrency=1 --iterations=1000000 --query="insert into test.t (flag) values (round(rand()))"

Benchmark
Average number of seconds to run all queries: 0.000 seconds
Minimum number of seconds to run all queries: 0.000 seconds
Maximum number of seconds to run all queries: 0.609 seconds
Number of clients running queries: 1
Average number of queries per client: 1

Cette solution est plus rapide que la précédente, aussi si vous disposez de mysqlslap, n’hésitez pas.

La procédure stockée : REPEAT… UNTIL

Dès lors qu’on envisage d’insérer 1 million de lignes dans une table, la notion de « boucle » n’est jamais très loin, et finalement la procédure stockée non plus. Celle-ci est en effet un moyen simple et rapide d’implémenter un traitement répétitif.

Basique et à peine paramétrable (seul le nombre d’enregistrements à insérer est dynamique), voici à quoi ressemble la procédure stockée permettant d’insérer notre million de lignes :

delimiter //
CREATE PROCEDURE fill_table(nb_rows INT)
BEGIN
DECLARE i INT DEFAULT 0;
REPEAT
SET i = i + 1;
INSERT INTO t (flag) VALUES(round(rand()));
UNTIL i >= nb_rows
END REPEAT;
END;
//
delimiter ;


Pas vraiment besoin de commenter le code, on note simplement les « delimiter » qui permettent de définir une autre terminaison que « ; » le temps d’écrire la procédure dans le client mysql.

Pour appeler celle-ci :

mysql> CALL fill_table(1000000);
Query OK, 1 row affected (1 min 37.98 sec)

Cette solution est la plus rapide des trois présentées ici.

Mission accomplie ? Presque…

Avant de lancer la procédure stockée, la commande SHOW INDEX affichait une cardinalité de 0 pour la clé primaire et de NULL pour notre index situé sur « flag ».

Une fois la procédure terminée, un SHOW INDEX indique cette fois une cardinalité mise à jour pour la clé primaire (1 000 000) mais celle-ci est toujours à NULL pour l’index « flag ».

La commande ANALYZE permet de laver cet affront :

mysql> ANALYZE TABLE t;
+---------+---------+----------+----------+
| Table   | Op      | Msg_type | Msg_text |
+---------+---------+----------+----------+
| world.t | analyze | status   | OK       |
+---------+---------+----------+----------+

Les statistiques de notre index sont désormais à jour et la commande SHOW INDEX indique désormais une cardinalité de 2 pour l’index « flag », le champ indexé ne comporte effectivement que des 0 ou des 1.

Nous voilà face à un index de cardinalité 2… Peu sélectif ? Inutile ? Mieux que rien ? Les paris sont ouverts.

Réponse au prochain numéro, stay tuned.

Mots-clefs : ,

6 commentaires sur “Générer un jeu de données : shell, mysqlslap, et procédure stockée”

  1. [...] dbnewz le blog français sur les SGBD – MySQL, Oracle et plus… « Générer un jeu de données : shell, mysqlslap, et procédure stockée [...]

  2. Bruno dit :

    Article intéressant.

    J’ai essayé de le reproduire, mais je bute sur la commande ANALYSE TABLE. Elle ne semble plus supportée par mes versions de MySQL (Redhat -> 5.0.22) et Ubuntu (5.0.67). On m’annonce une erreur de syntaxe ! La doc en ligne ne la référence plus non plus…

  3. pébé dit :

    « analyZe table »
    ;)

  4. Bruno dit :

    Merci. C’en est humiliant !!!

    Commentaire général : je trouve les articles très riches d’enseignements (les réactions aux commentaires également…). Longue vie à ce site.

  5. [...] repris la structure de la table d’un billet précédent (générer un jeu de données), ainsi que la procédure stockée nécessaire pour la renseigner, souvent 1M de lignes pour mes [...]

  6. [...] jeu d’essai similaire à ma table de production, j’alimente ma table de test grâce à une procédure stockée déjà évoquée sur [...]

Laisser une réponse