mysqlslap et supersmack, deux outils de benchmark pour MySQL
7 July 2008 par arnaudIl est parfois reprochĂ© au serveur MySQL de ne pas fournir suffisamment d’outils de benchmark / profiling concernant les requĂȘtes ou le fonctionnement du serveur lui-mĂȘme. Des commandes telles que SHOW STATUS (affiche l’Ă©tat du serveur Ă un instant t), ou bien encore EXPLAIN (plan d’exĂ©cution de la requĂȘte SQL) permettent nĂ©anmoins d’obtenir de prĂ©cieuses informations.
Ceci Ă©tant dit, comment s’assurer que son serveur MySQL tiendra la charge ? 1000 requĂȘtes /s en insertion sont prĂ©vues le jour de la sortie de votre prochain service internet : votre serveur sera t-il capable d’y faire face ?
Les deux outils prĂ©sentĂ©s aujourd’hui permettent de simuler la charge reçue par le serveur MySQL en fonction de diffĂ©rentes paramĂštres dont le nombre de connexions simultanĂ©es et le nombre de requĂȘtes par utilisateurs. Avec de tels outils, vous pouvez par exemple tester mysqlslap sur une de vos requĂȘtes clĂ©, visualiser comment celle-ci rĂ©agit sous diffĂ©rentes configurations, et visualiser un “score” Ă base de temps d’exĂ©cution. Une fois ce “score” rĂ©cupĂ©rĂ©, comparez-le avec celui que vous obtiendriez avec la mĂȘme requĂȘte modifiĂ©e par vos soins, avez-vous progressĂ© ?
mysqlslap
mysqlslap est un “client” du serveur MySQL au mĂȘme titre que “mysql” ou “mysqladmin” (autre client). Officiellement disponible depuis la version 5.1.4 de MySQL, il est nĂ©anmoins possible de faire fonctionner mysqlslap avec des versions moins avancĂ©es. Plusieurs astuces pour cela, en voici une, suivie d’une autre. Il y’a peut-ĂȘtre encore plus simple : sur ma version d’ubuntu la version 5.0.51 de MySQL est installĂ©e, le binaire de mysqlslap est absent mais il m’a suffit d’installer les binaires de la 5.1.25 dans un autre rĂ©pertoire pour exĂ©cuter mysqlslap via le serveur MySQL 5.0.51 en fonctionnement.
mysqlslap est trĂšs simple d’utilisation, parmi ses options les plus notables on trouve :
–auto-generate-sql : mysqlslap peut gĂ©nĂ©rer pour vous des requĂȘtes SQL alĂ©atoires. Pratique pour des tests rapides, il vaut mieux nĂ©anmoins soumettre vos propres requĂȘtes Ă mysqlslap.
–query : permet d’utiliser vos propres requĂȘtes.
–concurrency : simule un nombre de clients qui se connectent simultanĂ©ment.
–iterations : … pour rĂ©pĂ©ter votre test n fois.
--number-of-queries : permet d’attribuer Ă chaque client un nombre de requĂȘtes approximatifs.
–engine : spĂ©cifie le moteur de stockage Ă utiliser lorsque vous laissez mysqlslap gĂ©nĂ©rer ses propres requĂȘtes.
–csv : pour gĂ©nĂ©rer les rĂ©sultats de vos tests au format csv.
Notes :
- Il est possible de spĂ©cifier plusieurs tests Ă la suite en sĂ©parant par une virgule vos valeurs pour les paramĂštres “concurrency”, “iterations”, “engines”.
- mysqlslap recherche par dĂ©faut une base du nom de “mysqlslap”, crĂ©ez lĂ . Ma base mysqlslap Ă©tant vide, j’ai utilisĂ© l’instruction “USE world” afin de ne pas avoir Ă deplacer mes tables de test existant dans la base “world”. Pour des benchmarks plus prĂ©cis, Ă©vitez d’utiliser cette commande et dĂ©placez vos tables dans la base mysqlslap.
mysqlslap en pratiqueundercat@u200:/usr/local/mysql5125$ ./bin/mysqlslap –user=mysql –concurrency=200 –number-of-queries=1000 –iterations=10 –query=”USE world;SELECT SQL_NO_CACHE ci.name, co.name from City ci, Country co where ci.countrycode = co.code and ci.name like ‘c%’”
On obtient :
Benchmark
Average number of seconds to run all queries: 1.121 seconds
Minimum number of seconds to run all queries: 1.015 seconds
Maximum number of seconds to run all queries: 1.265 seconds
Number of clients running queries: 200
Average number of queries per client: 5
Nous avons ici 200 clients concurrents dont on limite le nombre de requĂȘtes Ă 1000 au total, ce qui nous donne environ 5 requĂȘtes par client.
En rajoutant la clause –csv et une concurrence variable :
undercat@u200:/usr/local/mysql5125$ ./bin/mysqlslap --user=mysql –csv=/tmp/slap.csv –concurrency=1,10,50,100,200 –iterations=10 --query="USE world;SELECT SQL_NO_CACHE ci.name, co.name from City ci, Country co where ci.countrycode = co.code and ci.name like 'c%'"
J’utilise ici SQL_NO_CACHE afin de ne pas placer la requĂȘte dans le query cache, ce qui fausserait les tests.
L’option –csv couplĂ© Ă openoffice / impress par exemple, permet de crĂ©er rapidement un graphique issu de nos tests. Ci-dessous le csv gĂ©nĂ©rĂ© et le graphique :undercat@u200:/tmp$ cat slap.csv
,mixed,0.002,0.000,0.004,1,1
,mixed,0.015,0.011,0.027,10,1
,mixed,0.063,0.061,0.068,50,1
,mixed,0.133,0.122,0.205,100,1
,mixed,0.245,0.105,0.298,200,1
supersmack
Disponible sur http://vegan.net/tony/supersmack/, cet outil n’Ă©volue plus depuis trois ans mais il reste intĂ©ressant ! Initialement dĂ©veloppĂ© par Sasha Pachev (un ancien de chez MySQL), puis retouchĂ© par plusieurs autre contributeurs dont JĂ©remy Zawodny, supersmack est moins intuitif que mysqlslap mais il se rattrape par ailleurs puisqu’il est plus largement disponible (pas seulement sur MySQL 5) et offre des rĂ©glages plus fins. Il est notamment possible de construire dynamiquement une requĂȘte Ă partir d’un “dictionnaire”… supersmack peut en effet “piocher” dans un fichier texte une liste d’identifiants par exemple, nous verrons comment.
Note : en plus des avantages suscitĂ©s, supersmack peut Ă©galement ĂȘtre interfacĂ© avec PostgreSQL et Oracle.
Installation de supersmack
Le binaire n’Ă©tant pas distribuĂ© avec mysql, nos tests sous supersmack vont nĂ©cessiter quelques Ă©tapes supplĂ©mentaires.
PremiÚre étape, récupérer les sources :
wget http://vegan.net/tony/supersmack/super-smack-1.3.tar.gz
puis ./configure --with-mysql=/usr/local/mysql5125
A ce stade vous devriez voir s’afficher ceci :
Building with the following options:
MySQL Support………………… yes
PostgreSQL Support……………. no
Oracle Support……………….. no
Thanks for using super-smack!
Faites suivre d’un make, et make install : supersmack devait ĂȘtre opĂ©rationnel.
Si ça n’est pas le cas, veillez Ă avoir installĂ© les paquets suivants (ubuntu) :
apt-get install libmysqlclient15-dev
apt-get install build-essential
supersmack : fonctionnement
Le répertoire /smacks contient deux exemples : select-key.smack et update-select.smack.
Prenons comme base le premier fichier et voyons comment le configurer.
EvoquĂ©e en introduction, supersmack dispose d’une fonctionnalitĂ© trĂšs pratique dans certains cas, il s’agit du “dictionnaire”. Ce dernier permet de construire des requĂȘtes dynamiques, comme si votre requĂȘte Ă©tait directement issue de votre script. Exemple :
SELECT Capital FROM Country WHERE Code = “$code_pays”
Difficile d’exĂ©cuter une telle requĂȘte avec mysqlslap… Il faudrait s’arranger pour rĂ©cupĂ©rer le code pays via une jointure mais dans certains cas cette information n’est tout simplement pas disponible. Il se peut que vous souhaitiez exĂ©cuter une sĂ©rie de requĂȘte construites dynamiquement Ă partir d’informations contenues dans un fichier.
Construisons Ă titre d’exemple un tel dictionnaire Ă partir de l’une de nos tables (la fameuse base “world” est encore une fois mise Ă contribution).mysql> select distinct(countrycode) into outfile '/tmp/code_pays_pop_under_2M.txt' from City where population < 2000000;
On a ainsi exportĂ© dans /tmp/code_pays_under_2M.txt la liste des codes pays qui possĂšdent des villes infĂ©rieures Ă 2 000 000 d’habitants. Le fichier contient des lignes stockĂ©s sous la forme suivante :
AFG
NLD
ANT
…
Notre “dictionnaire” dĂ©sormais construit, supersmack pourra y piocher les informations requises et construire dynamiquement nos requĂȘtes.
Voyons maintenant à quoi ressemble un fichier .smack. Nous partons ici du fichier select-key.smack pour construire un dbnewz.smack allégé pour notre exemple :
//define a dictionary
dictionary “code_pays”
{
type “rand”; // autres valeurs possibles : “seq” ou “unique”
source_type “file”; // nous utiliserons le fichier contenant nos codes pays
source “/tmp/code_pays_pop_under_2M.txt”; // emplacement du fichier “dictionnaire”
delim “,”; // take the part of the line before ,
file_size_equiv “45000″; // if the file is greater than this
//divide the real file size by this value obtaining N and take every Nth
//line skipping others. This is needed to be able to target a wide key
// range without using up too much memory with test keys
}Â
//define a query
query “find_capital”
{
query “SELECT SQL_NO_CACHE ci.Name, ci.Population
FROM City ci
INNER JOIN Country co ON ci.CountryCode = co.Code
WHERE co.Code = ‘$code_pays’”;
type “req_code_pays”; // libellĂ© de votre requete, permet de s’y retrouver dans les resultats
has_result_set “y”; // On attend des resultats (a la difference d’un UPDATE par ex).
parsed “y”; // signale que supersmack doit construire dynamiquement la requete a partir du dictionnaire
}
// define database client type
client “dbnewz”
{
user “root”; // connect as this user
pass “XXXXXXX”; // use this password
host “localhost”; // connect to this host
db “world”; // switch to this database
#socket ” /var/run/mysqld/mysqld.sock”;Â // j’utilise ici la valeur de SHOW VARIABLES LIKE ’socket’;
query_barrel “1 find_capital”; // Execute 1 fois la requete “find_capital” a chaque iteration.;
}
main
{
dbnewz.init(); // initialize the client
dbnewz.set_num_rounds($2); // le 2eme argument recu par supersmack est le nombre d’iterations.
dbnewz.create_threads($1); // permet de definir combien de clients simultanĂ©s l’on souhaite.
dbnewz.connect();
dbnewz.unload_query_barrel(); // for each client fire the query barrel
dbnewz.collect_threads();
dbnewz.disconnect()
}
Il est Ă©galement possible pour supersmack de crĂ©er automatiquement les tables dans lesquelles se dĂ©rouleront les tests, ouvrez le fichier select-key.smack pour davantage d’informations, il fournit la syntaxe relative Ă cette fonctionnalitĂ©.
Nous pouvons désormais lancer le test, ici 50 clients concurrents et 1000 itérations :
root@u200:/opt/super-smack-1.3/smacks# super-smack dbnewz.smack 50 1000
Query Barrel Report for client dbnewz
connect: max=888ms min=7ms avg= 213ms from 50 clients
Query_type   num_queries   max_time   min_time   q_per_s
req_code_pays   50000   41   1   1327.08
Nous avons ici obtenu en moyenne 1327 requĂȘtes / seconde pour cette requĂȘtes et 50 clients concurrents entre le dĂ©but et la fin de nos tests.
ConnectĂ© sur le serveur MySQL en question, on observe via un SHOW FULL PROCESSLIST des requĂȘtes du type :
SELECT SQL_NO_CACHE ci.Name, ci.Population
FROM City ci
INNER JOIN Country co ON ci.CountryCode = co.Code
WHERE co.Code = ‘LKA’
Le code ‘LKA’ a Ă©tĂ© remplacĂ© par supersmack lui-mĂȘme, le dictionnaire a donc bien fonctionnĂ©.
Vous venez de faire connaissance avec ces deux outils de benchmark (si ça n’Ă©tait pas dĂ©jĂ fait), pensez Ă les utiliser lors de vos prochaines Ă©valuations de performance (serveur / requĂȘtes). Il est en effet toujours apprĂ©ciable de pouvoir qualifier la situation de dĂ©part afin de mesurer l’Ă©tendue des progrĂšs effectuĂ©s par la suite.
Autres billets susceptibles de vous intéresser :

9 July 2008 at 6:26 pm
Bonjour,
merci pour l’article, il met efficacement le pied Ă l’Ă©trier. Toutefois je compare trois requĂȘtes SELECT * FROM matable WHERE id = valeur; sur trois tables, dont le champ id (clef primaire) est respectivement du type int, char(30), varchar(100).
Intuitivement je pensais avoir des diffĂ©rences de temps d’exĂ©cution, et en fait non. MĂȘme en rĂ©pĂ©tant les tests plusieurs fois, en jouant sur iterations et concurrency, j’obtiens des temps trĂšs proches Ă chaque fois.
Donc ma question : y’a t’il un piĂšge dans lequel il ne faut pas tomber avec ce genre d’outils? Quelles sont les prĂ©cautions?
Possible aussi que les trois requĂȘtes soient effectivement Ă©gales, mais je crains l’erreur de condition expĂ©rimentale.
Cordialement
Cédric
9 July 2008 at 6:39 pm
Salut Cédric,
plusieurs questions:
InnoDB ou MyISAM?
version de MySQL?
quel type de test fais tu?
Single Key lookup est quand mĂȘme ce qu il y a de plus rapide comme requĂȘte.
10 July 2008 at 9:58 am
Salut Cédric,
un article qui tombe Ă point pour notre discussion : http://www.bigdbahead.com/?p=58
matt constate 25% de perf en moins sur des requetes identiques dont la clé primaire est mise entre quotes.
19 August 2008 at 12:39 am
[...] 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 [...]