1

I have a site which uses PHP's PDO library to access a mysql database. The mysql database is highly optimised and has all the suitable indexes to make the queries fast and so on. I am encountering some strange behaviour though in relation to the first query to run for a particular web service.

This particular web-service runs a query against the database and returns a json response which is then fed to a jquery auto-complete.

The query upon first run in a client takes approx 2s to run, after which it drops to hundredths of a second, presumably due to innodb caching.

If I type in an entry in the auto-complete box during a new session then the first query response can take upwards of 5 seconds after which it becomes blisteringly fast to return responses. If I then leave the site for a good period i.e. perhaps an hour(not an exact measure but for the sake of argument, a relatively long period of time) and come back to it the same slow first query behaviour is observed again.

I am using a persistent connection out of necessity and owing to a finite number of connections on the server in connection.

I was wondering if any of you had any ideas which might allow me to mitigate the initial delay a bit more.

$DBH = null;

$host = "127.0.0.1";
$db_name = "my_db";
$user_name = "me";
$pass_word = "something";

try {
  # MySQL with PDO_MYSQL
  $DBH = new PDO("mysql:host=$host;dbname=$db_name;charset=utf8", $user_name, $pass_word, array(PDO::ATTR_PERSISTENT => true, PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'"));
  $DBH->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
}
catch(PDOException $e) {
    error_log( $e->getMessage(), 0 );
}

Updated with answer

Right guys after much testing and after thoroughly checking that it was not a dns issue, I went checking the innodb buffer pool route. Anyway I wrote a stored procedure which uses a query to generate a a query for each table in the database which would thus cause them to be cached in the innodb_buffer_pool. The query to generate the sql queries for each table is from the following SO question. I made only one edit to that query and put in the database() function so that it would work from whichever database it was called from.

I also set it up so that it can be called via PHP without waiting for the script to complete so your normal application continues on.

I hope this helps someone out. As an aside to be even more efficient you cold wrap the exec in a small function to only run it at certain times and so on.

MySQL stored procedure SQL

DELIMITER $$

USE `your_db_name`$$

DROP PROCEDURE IF EXISTS `innodb_buffer_pool_warm_up`$$

CREATE DEFINER=`user_name`@`localhost` PROCEDURE `innodb_buffer_pool_warm_up`()
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE sql_query VARCHAR(1000) DEFAULT NULL;
DECLARE sql_cursor CURSOR FOR
SELECT 
  CONCAT('SELECT `',MIN(c.COLUMN_NAME),'` FROM `',c.TABLE_NAME,'` WHERE `',MIN(c.COLUMN_NAME),'` IS NOT NULL')
FROM
  information_schema.COLUMNS AS c
LEFT JOIN (
  SELECT DISTINCT
    TABLE_SCHEMA,TABLE_NAME,COLUMN_NAME
  FROM
    information_schema.KEY_COLUMN_USAGE
) AS k
USING
  (TABLE_SCHEMA,TABLE_NAME,COLUMN_NAME)
WHERE
  c.TABLE_SCHEMA = DATABASE()
  AND k.COLUMN_NAME IS NULL
GROUP BY
  c.TABLE_NAME;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN sql_cursor;
read_loop: LOOP
    FETCH sql_cursor INTO sql_query;
    IF done THEN
      LEAVE read_loop;
    END IF;

SET @stmt_sql = sql_query;
PREPARE stmt FROM @stmt_sql;
EXECUTE stmt;

END LOOP;
CLOSE sql_cursor;
END$$
DELIMITER ;

PHP to call the stored procedure

innodb_warm_up_proc_call.php

<?php
$DBH = null;

$host = "localhost";
$db_name = "your_db_name";
$user_name = "user_name";
$pass_word = "password";

try {
  # MySQL with PDO_MYSQL
  $DBH = new PDO("mysql:host=$host;dbname=$db_name;charset=utf8", $user_name, $pass_word, array(PDO::ATTR_PERSISTENT => true, PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'"));
  $DBH->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

  $sql = "CALL innodb_buffer_pool_warm_up()";
  $STH = $DBH->prepare( $sql );
  $STH->execute();

    }catch( PDOException $e ) {
        error_log( $e->getMessage() . ' in ' .$e->getFile(). ' on line ' .$e->getLine(), 0 );
    }
?>

PHP to run the above script silently and without waiting for it to complete

innodb_warm_up.php

<?php

$file_to_execute = dirname(__FILE__) . "/innodb_warm_up_proc_call.php";

//Run the stored procedure but don't wait around for a chat
exec("php -f {$file_to_execute} >/dev/null 2>&1 &");

?>
Community
  • 1
  • 1
jiraiya
  • 977
  • 4
  • 13
  • 34
  • 1
    I remember it was something to do with changing "localhost" to "127.0.0.1" in the PDO connection string. I'll see if I can find the post... See [here](http://stackoverflow.com/questions/9800577/why-is-constructing-pdo-connection-slow) EDIT: Obviously this assumes you are running the db server on your local machine... – GarethL Apr 17 '13 at 14:41
  • @GarethL thanks I had actually made that change aright after reading that article but unfortunately in my case it did not cause much of a perceptible change. – jiraiya Apr 17 '13 at 14:44
  • How about adding some code? how are you instantiating PDO, how are you querying? what attributes have you set (like emulate prepares...) – Elias Van Ootegem Apr 17 '13 at 14:47
  • Please calrify: "when I type in the auto-complete for the first time on the site". Does it mean it happens only once after restarting your web server? or after starting a new session? Or does it just mean that when you type the first character, it takes long, but if you type more characters, then it's instant (regardless of whether you restarted the server/session)? – RandomSeed Apr 17 '13 at 14:56
  • @EliasVanOotegem Thanks I have added my connection code now. I would have added it initially but I thought it might not be needed as its just a pretty standard connection and since the queries are very fast after the first. In any case its there now. Thanks again. – jiraiya Apr 17 '13 at 14:59
  • @YaK Sorry I clarified that area now. thanks. – jiraiya Apr 17 '13 at 15:02

2 Answers2

0

When addressing a particular web service, change it's domain name to IP address.
Most likely it will eliminate such a delay (caused by DNS lookup)

Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
  • This is very interesting, I am definitely going to check this out and I will let you know if it works. Thanks for the help. – jiraiya Apr 17 '13 at 15:03
  • Thanks for your help on this it really helped me to eliminate things and track down the innodb issue. If there were a way to also tick this a a very helpful part of the answer I would. – jiraiya Apr 22 '13 at 10:01
0

Thanks for all the help and great suggestions on this one. I thoroughly checked the dns and other solutions but in the end it turned out to be the innodb page buffer pool. I have coded up a solution for myself and have added it in its entirety in my question above so hopefully it will be of use. Thanks again for the help.

jiraiya
  • 977
  • 4
  • 13
  • 34