1 min read

Verhoog de performantie van je PHP MDB2 applicatie met 30%

Eén van de manieren om je webapplicatie te beveiligen tegen SQL-injectie, is gebruik te maken van Prepared Statements. Een voorbeeldje van zo'n prepared statement is:

$types = array("text");
$statement = $con->prepare("SELECT * FROM countries 
    WHERE tld = ?", $types, MDB2_PREPARE_RESULT);
$resultset = $statement->execute(array($landcode));
if(PEAR::isError($resultset)) {
    die('failed... ' . $resultset->getMessage());
}

Op deze manier ben je immers zeker dat de SQL query correct zal doorgegeven worden en dat er geen onverwachte dingen kunnen gebeuren. Als je niet via prepared statements werkt, kan het zijn dat iemand via de variabele $landcode foute data doorgeeft.

Lang leve prepared statements dus! Helaas hebben ze ook een groot nadeel als je MySQL gebruikt als database engine. MySQL kan immers geen resultaten van prepared statements cachen! Waarvoor dient die Query Cache en waarom is die zo belangrijk?

Stel dat je homepage bijvoorbeeld je 5 laatste artikels bevat. Telkens iemand je homepage opvraagt, vraagt je CMS/blog-platform/... de 5 laatste artikels aan de database. Je database slaat de resultaten van die zoekopdracht op in de "Query Cache", zodat de volgende bezoeker de resultaten sneller te zien krijgt. De database moet de zoekopdracht niet meer gaan uitvoeren, hij weet het resultaat immers al.

Op het eerste zicht moet je dus kiezen tussen veiligheid (via prepared statement) en snelheid (gebruik maken van de query cache). Een hartverscheurende keuze dus! Omdat MySQL niet de enige database engine is die de on-efficiënt omspringt met prepared statements, implementeren verschillende frameworks de prepared statements "client side". Dit wil zeggen dat ze de prepared statements zelf afhandelen en ze als "normale" queries doorstuurt naar de database. MySQL ziet met andere woorden enkel de normale queries, niet de prepared statements, en kan deze requests dus gewoon cachen. Best of both worlds dus, als je framework in staat is die afhandeling van de prepared statements zelf te maken.

MDB2 kan dit in recente versies ook! Gebruik je dus MDB2, pas dan je connect strings op volgende manier aan. Vervang

$this->handler =& MDB2::connect($dsn);

door

$options = array('emulate_prepared' => true);
$this->handler =& MDB2::connect($dsn,$options);

Ik heb dit recent getest op een applicatie van een klant, en de performantie-winst bedroeg meer dan 30%.