Format CSV, JSON, XML, SQL,
Lequel est le plus rapide?

Quel format de fichier est le plus rapide pour modifier une valeur, par exemple l'email d'un client?


Un fichier clients.csv 42.9Ko comporte 311 lignes (il a été généré avec des valeurs aléatoires, mais prénoms et noms, noms de rue de Saint-Nazaire, villes réels,
j'utlise ces données pouer générer des clients et des commandes afin de tester les performances d'une boutique en ligne dans 10 ans quand les tables seront bien pleines)

Chaque ligne du CSV est du type
1a0b92051ae9|yJp|3pNLbRu.nE2sY|BOZORRGIGAZIANI|Alperen|5 QUAI DEMANGE|APPY|09250|06 89 35 19 17|bozorrgigaziani.alperen@njjgsvm.fr|1713437891
Spec: identifiant unique|identifiant de connexion|mot de passe crypté|nom de famille|prénom|rue|ville|code postal|tél|email|date de création du compte


Ce CSV a permis de générer le même fichier clients au format JSON clients.json 74.5Ko (écrit sur le disque en une seule ligne):
{
 "clients":[
   {
	"id":"0092051fbacc",
	"identifiant":"yJp",
	"mdp":"3pNLbRu.2nEsY",
	"nom":"BOZORRGIGAZIANI",
	"prenom":"Alperen",
	"rue":"5 QUAI DEMANGE",
	"ville":"APPY",
	"cp":"09250",
	"tel":"06 89 35 19 17",
	"email":"bozorrgigaziani.alperen@njjgsvm.fr",
	"date":1713437891
  },
  ...
 ]


Le fichier XML clients.xml 98.6Ko:
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<clients>
  <client>
    <id>aea6ea2ed27a</id>
    <identifiant>kk</identifiant>
    <mdp>3pPOuXDXNGRMU</mdp>
    <nom>Lancaster</nom>
    <prenom>Kurt</prenom>
    <rue>1 rue Paradis</rue>
    <ville>MARSEILLE</ville>
    <cp>13001</cp>
    <tel>0605040302</tel>
    <email>joe@orange.fr</email>
    <date>1713027624</date>
  </client>
  ...
</clients>

La structure de la table innoDB de la base de données MySQL:
CREATE TABLE `clients_innodb` (
  `id` varchar(12) NOT NULL,
  `identifiant` varchar(16) NOT NULL,
  `mdp` varchar(13) NOT NULL,
  `nom` varchar(32) NOT NULL,
  `prenom` varchar(32) NOT NULL,
  `rue` varchar(32) NOT NULL,
  `ville` varchar(32) NOT NULL,
  `cp` varchar(5) NOT NULL,
  `tel` varchar(11) NOT NULL,
  `email` varchar(64) NOT NULL,
  `creation` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


Maintenant les scripts dont le temps d'exécution est mesuré, modifient le client index 280 (id 56823b1fa645) en remplaçant la valeur de son email.


Mesure du temps d'exécution du CSV avec fgetcsv()

<?php
$id = '56823b1fa645';
$newemail = 'truc@machin.fr';
$start = microtime(true);

if (($fp = fopen("clients.csv","r"))!== FALSE){
    $s='';
    while (($t = fgetcsv($fp,1000,"|"))!== FALSE){
        if($t[0]==$id) $t[9] = $newemail;
        $s .= $t[0].'|'.$t[1].'|'.$t[2].'|'.$t[3].'|'.$t[4].'|'.$t[5].'|'.$t[6].'|'.$t[7].'|'.$t[8].'|'.$t[9].'|'.$t[10]."\n";
    }
    fclose($fp);
    file_put_contents('clients.csv',$s);
}

$end = microtime(true);
$duree = number_format(($end - $start)*1000,3);
echo "duree:$duree ms<br>";
?>
Durée moyenne de plusieurs tests: 20.659 ms


Mesure du temps d'exécution CSV avec file_get_contents() et explode()

<?php
$id = '56823b1fa645';
$newemail = 'truc@machin.fr';
$start = microtime(true);

$tab = explode("\n", file_get_contents("clients.csv"));
$nb=count($tab);
$s='';
for($n=0; $n<$nb-1; $n++){
    $t = explode('|',$tab[$n]);
    if($t[0]==$id) $t[9] = $newemail;
    $s .= $t[0].'|'.$t[1].'|'.$t[2].'|'.$t[3].'|'.$t[4].'|'.$t[5].'|'.$t[6].'|'.$t[7].'|'.$t[8].'|'.$t[9].'|'.$t[10]."\n";
}
file_put_contents('clients.csv',$s);

$end = microtime(true);
$duree = number_format(($end - $start)*1000,3);
echo "duree:$duree ms<br>";
?>
Durée moyenne de plusieurs tests: 2.213 ms
Comme quoi fgetcsv() est une fonction créée pour le format CSV, mais minable pour les performances.


Mesure du temps d'exécution CSV avec explode() et implode()

<?php
$id = '56823b1fa645';
$newemail = 'truc@machin.fr';
$start = microtime(true);

$lignes = explode("\n", file_get_contents("clients.csv"));
$nb = count($lignes);
for($n=0; $n<$nb-1; $n++){
    $t = explode('|',$lignes[$n]);
    if($t[0]==$id){
        $t[9] = $newemail;
        $lignes[$n] = implode('|', $t);
        break;
    }
}
file_put_contents('clients.csv',implode("\n", $tab));

$end = microtime(true);
$duree = number_format(($end - $start)*1000,3);
echo "duree:$duree ms<br>";
?>
Durée moyenne de plusieurs tests: 1.488 ms



Mesure du temps d'exécution JSON (sans tableau associatif)

<?php
$id = '56823b1fa645';
$newemail = 'truc@machin.fr';
$start = microtime(true);

$json = json_decode(file_get_contents('clients.json'));
foreach($json->clients as $client){
    if($client->id==$id){
        $client->email = $newemail;
        break;
    }
}
file_put_contents('clients.json', json_encode($json));

$end = microtime(true);
$duree = number_format(($end - $start)*1000,3);
echo "duree:$duree ms<br>";
?>
Durée moyenne de plusieurs tests: 5.608 ms


Mesure du temps d'exécution JSON avec tableau associatif

<?php
$id = '56823b1fa645';
$newemail = 'truc@machin.fr';
$start = microtime(true);

$json = json_decode(file_get_contents('clients.json'), true);
foreach($json["clients"] as $client){
    if($client["id"]==$id){
        $client["email"] = $newemail;
        break;
    }
}
file_put_contents('clients.json', json_encode($json));

$end = microtime(true);
$duree = number_format(($end - $start)*1000,3);
echo "duree:$duree ms<br>";
?>
Durée moyenne de plusieurs tests: 5.352 ms



Mesure du temps d'exécution XML avec SimpleXMLElement

<?php
$id = '56823b1fa645';
$newemail = 'truc@machin.fr';
$start = microtime(true);

$xmlstr = file_get_contents("clients.xml");
$clients = new SimpleXMLElement($xmlstr);
foreach ($clients as $client) {
    if($client->id==$id){
        $client->email = $newemail;
        break;
    }
}
//file_put_contents('clients.xml',$clients->asXML());
$clients->asXML('clients.xml');

$end = microtime(true);
$duree = number_format(($end - $start)*1000,3);
echo "duree:$duree ms<br>";
?>
Durée moyenne de plusieurs tests: 15.395 ms


Mesure du temps d'exécution XML avec XMLWriter

<?php
$id = '56823b1fa645';
$newemail = 'truc@machin.fr';
$start = microtime(true);

$xmlstr = file_get_contents("clients.xml");
$clients = new SimpleXMLElement($xmlstr);

$xml = new XMLWriter();
$xml->openURI('clients.xml');
$xml->setIndent(true);
$xml->setIndentString("\t");
$xml->startDocument('1.0', 'UTF-8');
$xml->startElement('xml');

foreach ($clients as $client) {
    if($client->id==$id) $client->email = $newemail;

    $xml->startElement('client');
    $xml->writeElement('id', $client->id);
    $xml->writeElement('identifiant', $client->identifiant);
    $xml->writeElement('mdp', $client->mdp);
    $xml->writeElement('nom', $client->nom);
    $xml->writeElement('prenom', $client->prenom);
    $xml->writeElement('rue', $client->rue);
    $xml->writeElement('ville', $client->ville);
    $xml->writeElement('cp', $client->cp);
    $xml->writeElement('tel', $client->tel);
    $xml->writeElement('email', $client->email);
    $xml->writeElement('date', $client->date);
    $xml->endElement();
}
$xml->endElement();
$xml->endDocument();
$xml->flush();
unset($xml);

$end = microtime(true);
$duree = number_format(($end - $start)*1000,3);
echo "duree:$duree ms<br>";
?>
Durée moyenne de plusieurs tests: 19.317 ms



Mesure du temps d'exécution MySQL table innoDB, mysqli procédural

<?php
$id = '56823b1fa645';
$newemail = 'truc@machin.fr';
$start = microtime(true);

$servername = "";
$username = "";
$database = "";
$password = "";

$sql = "UPDATE clients_innodb SET email='$newemail' WHERE id='$id';";

$mysqli = mysqli_connect($servername, $username, $password, $database) or die('Error connecting to MySQL server.');
mysqli_query($mysqli, $sql) or die("<b>$sql</b><br/>".mysqli_error($mysqli));
mysqli_close($mysqli);

$end = microtime(true);
$duree = number_format(($end - $start)*1000,3);
echo "duree:$duree ms<br>";
?>
Durée moyenne de plusieurs tests: 3.633 ms



Mesure du temps d'exécution MySQL table innoDB, mysqli objet

<?php
$id = '56823b1fa645';
$newemail = 'truc@machin.fr';
$start = microtime(true);

$servername = "";
$username = "";
$database = "";
$password = "";

$sql = "UPDATE clients_innodb SET email='$newemail' WHERE id='$id';";

$mysqli = new mysqli($servername, $username, $database, $password);
if ($mysqli->connect_errno) {
    echo "Failed to connect to MySQL: ".$mysqli->connect_error;
    die();
}
$mysqli->query($sql);
if ($mysqli->errno) {
    printf("Could not update table: %s<br />", $mysqli->error);
}
$mysqli->close();

$end = microtime(true);
$duree = number_format(($end - $start)*1000,3);
echo "duree:$duree ms<br>";
?>
Durée moyenne de plusieurs tests: 3.727 ms



Mesure du temps d'exécution MySQL table innoDB, Objets PDO

<?php
$id = '56823b1fa645';
$newemail = 'truc@machin.fr';
$start = microtime(true);

$servername = "";
$username = "";
$database = "";
$password = "";

$sql = "UPDATE clients_innodb SET email='$newemail' WHERE id='$id';";

try {
    $pdo = new PDO("mysql:host=$servername;dbname=$database", $username, $password);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $stmt= $pdo->prepare($sql);
    $stmt->execute();
    $pdo = null;
}
catch(PDOException $e) {
  echo $e->getMessage();
}

$end = microtime(true);
$duree = number_format(($end - $start)*1000,3);
echo "duree:$duree ms<br>";
?>
Durée moyenne de plusieurs tests: 3.728 ms



Récapitulatif des mesures

Code Durée (ms) Taille du fichier (Ko)
CSV fgetcsv() 20.659 42.9
CSV explode() 2.213 42.9
CSV implode() 1.488 42.9
JSON 5.608 74.5
JSON associatif 5.352 74.5
XML 15.395 98.6
XML XmlWriter 19.317 98.6
MySQL innodb, mysqli 3.633 54.6
MySQL innodb, mysqli obj 3.727 54.6
MySQL innodb, PDO 3.728 54.6





"Conclusion, en perf c'est le plus petit qui gagne.

CSV implode() est le plus rapide! Sur ordinateur de développement local ET sur le serveur. Et le fichier CSV le plus léger.
Noter que dès qu'il trouve l'enregistrement concerné il fait un break pour sortir de la boucle,
et que l'enregistrement à mettre à jour est quasiment à la fin du fichier, donc au début il serait encore plus rapide.

Cette méthode me permet de sortir en 4ms des données stockées pendant 10 ans pour générer des graphiques: c'est instantané,
il faut plus de temps pour modifier le DOM que pour faire le tri des données et les calculs.

Voilà, le CSV est presque 3X plus rapide que le SQL... Et je ne vous dis pas le temps passé à installer MySQL et PHPMyAdmin, créer la base de données, l'utilisateur, lui assigner des droits, créer la structure de la table, la remplir.

Si vous voulez tester voici le zip (107.9Ko) contenant les données CSV, JSON, XML, MySQL.
À vous de voir quelle méthode vous préférez."
- B.M.

COMMENTAIRES