Page 1 sur 1

Faut-il vraiment utiliser PDO?

Posté : 20 janv. 2010, 16:09
par Ripat
Bonjour à tous,

Comme un peu toute la communauté des développeurs PHP, je me suis laissé convaincre d'utiliser la classe PDO pour accéder à mes BDD. On connait tous les arguments habituellement avancés:
  1. Pseudo-couche d'abstraction des BDD
  2. Possibilité de personnalisation de la classe par extension
  3. gestion des exceptions
  4. Possibilité d'utiliser des "Prepared Statements"
    • Meilleure sécurité contre les injections SQL
    • Amélioration des performances
      • sur le parse des query répétitives
      • sur le protocole binaire client-serveur (au lieu de chaine de caractères).
  5. Ça en jette un max et ça fait "übergeek"
Pour ma part, je suis d'accord sur les points 1 à 3 mais suis plus circonspect au sujet des avantages annoncés des prepared statments. On lit (presque) partout que leur utilisation conduit à une amélioration sensible des perf pour des query répétitives comme des SELECT consécutifs dont seuls les paramètres changent. Ou des INSERT en série. J'ai donc testé mais ne suis pas arrivé à dégager d'amélioration sensible par rapport aux extensions mysql et mysqli de PHP. J'ai un peu gratté et il se fait que, par défaut, les Prepared Statement de PDO se font en mode client-side (PDO::ATTR_EMULATE_PREPARES=> true) une émulation de ce que fait le serveur MySQL en quelque sorte. De ce fait, on se retrouve avec quelques limitations:
  • PDO met des quote autour des INT dans les requêtes envoyées au serveur MySQL.
    SELECT * FROM matable WHERE id > 3 devient SELECT * FROM matable WHERE id > '3' dans les log de MySQL sauf si on utilise PDOStatement::bindValue en utilisant son paramètre optionnel data_type. Imaginez de faire un prepare sur SELECT * FROM matable LIMIT ? qui devient SELECT * FROM matable LIMIT '1' une fois arrivé sur le serveur MySQL. Va pas être content le serveur.
  • Contrairement à ce la doc PHP prétend, il n'y a pas de validation de la requête sur le serveur MySQL avant de la lui envoyer pour exécution. En mode émulation (client-side prepare), une requête comme SELECT ice FROM artica AND antarctica n'est pas une requête SQL valide mais ne donnera pas d'erreur dans la phase prepare.
    http://bugs.php.net/bug.php?id=44169
Pour pallier ces - disons - "bugs by design" on peut basculer en mode prepare natif (MySQL server-side) avec PDO::ATTR_EMULATE_PREPARES à false. On supprime les bugs du PDO en mode émulation et on peut arriver à un légère amélioration des performances sur des INSERT successifs (de l'ordre de 10% dans mes tests) MAIS:

Les requêtes preparées (server-side) ne sont pas cachées par MySQL! Du moins dans la version la plus courante (< 5.1.17). Et la pénalité de travailler hors cache est infiniment plus élevée que le malheureux gain de 10% puisque l'utilisation du cache de MySQL peut déboucher sur des gains de plusieurs centaines de pourcents.

Pour vous en convaincre, faites le test chez vous:
mysql> SHOW STATUS LIKE 'Qcache_queries_in_cache';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| Qcache_queries_in_cache | 0     | 
+-------------------------+-------+
1 row in set (0.00 sec)

mysql> SELECT * FROM maTable WHERE code = 1234567;
Empty set (0.05 sec)

mysql> SHOW STATUS LIKE 'Qcache_queries_in_cache';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| Qcache_queries_in_cache | 1     | 
+-------------------------+-------+
1 row in set (0.00 sec)

mysql> PREPARE myStmt FROM "SELECT * FROM maTable WHERE code = ?"; SET @code = 1234567; EXECUTE myStmt USING @code;
Query OK, 0 rows affected (0.00 sec)
Statement prepared

Query OK, 0 rows affected (0.00 sec)

Empty set (0.04 sec)

mysql> SHOW STATUS LIKE 'Qcache_queries_in_cache';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| Qcache_queries_in_cache | 1     | 
+-------------------------+-------+
1 row in set (0.00 sec)

Vous verrez bien que le compteur de queries cachées n'augmentera pas avec les prepared statements.

Alors pourquoi est-ce que je continue malgré tout à utiliser PDO? Et bien, d'une part parce que les autres avantages cités restent réels, aussi parce que je n'ai pas envie de revenir en arrière et de changer tout mon code et enfin parce que la communauté PHP mais aussi MySQL prepare de nouvelles versions pour mieux supporter les Prepare Statements: à partir de MySQL server > 5.1.17 les PS seront cachées. Et, du côté de PHP, ça bouge aussi: le pilote PHP MySQL Client Library devrait être remplacé par le nouveau PHP PDO Native Driver qui devrait être livré avec PHP 5.3 ou PHP 6.

__________________________________
Références:
http://blog.ulf-wendel.de/?p=187#pdo
http://dev.mysql.com/tech-resources/art ... ments.html

Re: Faut-il vraiment utiliser PDO?

Posté : 20 janv. 2010, 18:02
par Genova
Bon si j'ai bien compris ton sujet est donc une réflexion sur PDO. En ce qui me concerne il y a une erreur sur PDO qui m'a toujours parue mystique puisque dans certains cas elle apparaissait lorsque des requêtes étaient imbriquées, dans d'autres cas non (par requêtes imbriquées, j'entends par là l'exécution d'une requête dans une boucle PHP qui boucle sur les résultats d'une précédente), il s'agit de cette erreur :

Code : Tout sélectionner

Cannot execute queries while other unbuffered queries are active
Assez pénible, si quelqu'un a une explication de pourquoi PDO m'emmerde avec ça, et pourquoi parfois ça le dérange, je prends :)

Re: Faut-il vraiment utiliser PDO?

Posté : 20 janv. 2010, 18:38
par Victor BRITO
Il y a aussi un autre avantage à utiliser PDO qui n'a pas été cité : la facilité de migration d'un SGBD à un autre, en ce sens où l'on n'a pas besoin de réécrire tout le code comme on le ferait lorsqu'on utilise les fonctions mysql_* (), mysqli_* (), pgsql_* ()… : il suffit juste de modifier les paramètres de connexion à la base de données au moment de l'instanciation de l'objet PDO et de vérifier les requêtes SQL.

J'en connais un qui pourrait poster un retour d'expérience illustrant mon propos. ;)

Re: Faut-il vraiment utiliser PDO?

Posté : 20 janv. 2010, 18:48
par Genova
C'était le premier point ça "Pseudo-couche d'abstraction des BDD". Ca marche dans la plupart des cas, sauf au niveau des grosses requêtes avec des jointures, parfois suivant le type de SGBD il y a des changements à faire (UNION, LEFT JOIN, etc..).

Re: Faut-il vraiment utiliser PDO?

Posté : 20 janv. 2010, 19:10
par Ripat
En ce qui me concerne il y a une erreur sur PDO qui m'a toujours parue mystique puisque dans certains cas elle apparaissait lorsque des requêtes étaient imbriquées, dans d'autres cas non (par requêtes imbriquées, j'entends par là l'exécution d'une requête dans une boucle PHP qui boucle sur les résultats d'une précédente), il s'agit de cette erreur :

Code : Tout sélectionner

Cannot execute queries while other unbuffered queries are active
Assez pénible, si quelqu'un a une explication de pourquoi PDO m'emmerde avec ça, et pourquoi parfois ça le dérange, je prends :)
Un problème de curseur?
http://be.php.net/manual/fr/pdostatemen ... cursor.php

Re: Faut-il vraiment utiliser PDO?

Posté : 05 juil. 2010, 08:01
par nuxwin
En ce qui me concerne il y a une erreur sur PDO qui m'a toujours parue mystique puisque dans certains cas elle apparaissait lorsque des requêtes étaient imbriquées, dans d'autres cas non (par requêtes imbriquées, j'entends par là l'exécution d'une requête dans une boucle PHP qui boucle sur les résultats d'une précédente), il s'agit de cette erreur :

Code : Tout sélectionner

Cannot execute queries while other unbuffered queries are active
Assez pénible, si quelqu'un a une explication de pourquoi PDO m'emmerde avec ça, et pourquoi parfois ça le dérange, je prends :)
Un problème de curseur?
http://be.php.net/manual/fr/pdostatemen ... cursor.php
Tu dois simplement lire tout ton resultset avant d'exécuter une autre requête. Tu peux aussi désactiver ce comportement. Voir la doc sur PDO.

Re: Faut-il vraiment utiliser PDO?

Posté : 25 janv. 2013, 19:01
par Ripat
Bonjour à tous,

Je poste tous les 2 ans, alors oui, bonjour!

Je me permets de déterrer ce post que j'ai retrouvé par hasard sur google en cherchant des méthodes d'optimisation de MySQL et il me semble opportun de le réactualiser.

Mon post plus haut ne concerne que les versions MySQL < 5.1.17. Pour les versions actuelles, les Prepare Statement mysql-server-side (PDO::ATTR_EMULATE_PREPARES, false) sont bien mises en cache. J'ai vérifié. On gagne donc sur les deux tableaux: pré-compilation des requêtes et mise en cache.

Le temps ayant passé, on peut supposer que votre version de MySQL est plus récente. Mon conseil est donc de désactiver l'émulation prepare (php-client-side) et de laisser le serveur MySQL faire tout le boulot (prepare et mise en cache).

Si j'ai le temps, je ferai quelques essais pour confirmation. A suivre donc...

Re: Faut-il vraiment utiliser PDO?

Posté : 30 janv. 2013, 13:39
par Perine
Pour moi, l'émulation désactivée a comme meilleur avantage que MySQL retournera le bon type de données en retour (integer pour int, string pour varchar, etc.), tandis que lorsque l'émulation est activée, ce sont toujours des strings.

Re: Faut-il vraiment utiliser PDO?

Posté : 01 févr. 2013, 10:05
par Ripat
Pour moi, l'émulation désactivée a comme meilleur avantage que MySQL retournera le bon type de données en retour (integer pour int, string pour varchar, etc.), tandis que lorsque l'émulation est activée, ce sont toujours des strings.
Ceci n'est vrai que pour les versions PHP > 5.3 et compilées avec le pilote MySQL natif (MySQL Native Driver: mysqlnd). Pour toutes les autres versions le pilote MySQL de PHP retourne toujours des STR quelque soit le data type de MySQL et quelque soit le mode d'émulation PDO choisi.

La version vanille de PHP 5.3.3-7 compilée par Debian, par exemple, n'utilise pas le pilote mysqlnd et retourne donc toujours des strings. Et c'est également le cas de beaucoup de sites hébérgés.

C'est une lacune connue des pilotes MySQL de PHP.

Plus d'info sur mysqlnd:
http://www.php.net/manual/fr/mysqlnd.install.php

Re: Faut-il vraiment utiliser PDO?

Posté : 01 févr. 2013, 11:19
par Perine
Le problème est qu'il n'est pas possible de rendre compatible des scripts aussi facilement - même sur des hébergeurs à jour. D'ailleurs, si PHP avait réellement voulu changer quelque chose à l'histoire, ils auraient pu le faire, car pendant assez longtemps, j'ai dû compiler PHP moi-même après avoir fait le transtypage en C (parce que ça va bien plus vite qu'en PHP).

Re: Faut-il vraiment utiliser PDO?

Posté : 02 févr. 2013, 22:11
par AB
Oui c'est l'utilisation de "mysqlnd" et MySQL >= 5.1.17. qui changent la donne. "mysqlnd" est d'ailleurs maintenant activé par défaut avec php5.4.

Il est effectivement très conseillé de désactiver l'émulateur de requêtes préparée dans PDO (il n'est plus maintenu) pour faire intervenir la librairie "mysqlnd" (ou si non disponible, la librairie "libmysql" ) car MySQL sait très bien gérer les requêtes préparées, beaucoup plus efficacement et surtout avec mysqlnd. Ainsi, les requêtes sont réellement préparées avec les fonctionnalités du driver natif et pas simplement émulées avec PDO qui est trop généraliste pour pouvoir être optimisé d'où par exemple l'échappement des entiers par défaut qui conduit à l'erreur classique dans la clause LIMIT.

Et puis pas de panique car même désactivé, l'émulateur se remet en route s'il s'aperçoit que le sgdb ne supporte pas la syntaxe de la requête préparée. C'est pour cette raison que - même avec l'option PDO::ATTR_EMULATE_PREPARES, false - on peut continuer à utiliser des paramètres nommés malgré le fait que Mysql ne supporte que des paramètres anonymes pour les requêtes préparées. Dans ce cas l'émulateur PDO se met en route pour transformer les paramètres nommés en paramètres anonymes.

A noter au passage que l'emploi de "mysqlnd" permet des gains de performances très significatifs pour la mise en tableau du résultat d'une requête bufferisée côté php. Le tableau créé est une référence à la ressource "mysqlnd", et non pas une duplication dans le cas de l'utilisation de "libmysql" car dans ce dernier cas la ressource n'est pas directement exploitable par php. L'occupation mémoire peut donc varier du simple au double ! (testé avec mysqli mais j'espère que cela vaut aussi pour pdo).