PHP の PDO ライブラリを使用して mysql データベースにアクセスするサイトがあります。mysql データベースは高度に最適化されており、クエリを高速化するための適切なインデックスがすべて含まれています。特定の Web サービスに対して実行する最初のクエリに関して、奇妙な動作が発生しています。
この特定の Web サービスは、データベースに対してクエリを実行し、json 応答を返します。これが jquery オートコンプリートに渡されます。
クライアントでの最初の実行時のクエリは、実行に約 2 秒かかり、その後、おそらく innodb キャッシングが原因で、100 分の 1 秒に低下します。
新しいセッション中にオートコンプリート ボックスにエントリを入力すると、最初のクエリ応答に 5 秒以上かかることがあり、その後応答を返すのが非常に速くなります。その後、サイトをかなりの時間、つまりおそらく 1 時間 (正確な測定ではありませんが、議論のために比較的長い時間) 離れてからサイトに戻ると、同じ遅い最初のクエリ動作が再び観察されます。
接続中のサーバーの接続数が有限であるため、必要に応じて永続的な接続を使用しています。
最初の遅延をもう少し軽減できるアイデアがあるかどうか疑問に思っていました.
$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 );
}
回答で更新
多くのテストの後、DNSの問題ではないことを徹底的に確認した後、innodbバッファープールルートを確認しました。とにかく、クエリを使用してデータベース内の各テーブルのクエリを生成するストアド プロシージャを作成しました。これにより、テーブルは innodb_buffer_pool にキャッシュされます。各テーブルの SQL クエリを生成するクエリは、次の SO questionからのものです。そのクエリを 1 つだけ編集し、database() 関数を挿入して、どのデータベースから呼び出されても機能するようにしました。
また、スクリプトが完了するのを待たずに PHP 経由で呼び出すことができるように設定して、通常のアプリケーションを続行します。
これが誰かを助けることを願っています。余談ですが、さらに効率的にするために、exec を小さな関数にコールドラップして、特定の時間にのみ実行するようにします。
MySQL ストアド プロシージャ 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
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 を実行する
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 &");
?>