私のアプリケーションは、特定のテーブルの「列の表示」の実行に依存しています。実行には約60ミリ秒かかりますが、他のすべてのクエリには1ミリ秒未満かかります。クエリinformation_schema
は直接、さらに遅くなります。
データベースには約250のデータベースがあり、データベースごとに100から200のテーブル(合計で約2万テーブル)があります。
(アプリケーションはページの読み込みごとにこのようなクエリを約14回実行します。このレガシーコードをクリーンアップする必要があることは承知していますが、長期的な修正に取り組んでいる間、可能なオプションを探しています。)
MySQLは、INFORMATION_SCHEMA
テーブルにアクセスする特定の操作のテーブル統計を再計算します(SHOW COLUMNS
は、INFORMATION_SCHEMA.COLUMNS
のクエリに便利なエイリアスです)。 innodb_stats_on_metadata をfalseに設定すると、テーブルからメタデータを要求したときにこの再計算が行われなくなります。
SET GLOBAL innodb_stats_on_metadata=0;
以下をmy.cnf
に追加します
[mysqld]
innodb_stats_on_metadata = 0
INFORMATION_SCHEMA
テーブル(または必要なテーブルのみ)をレプリケートとして持つデータベースを作成することをお勧めします。それらを適切にインデックス付けすると、パフォーマンスが向上します。
ただし、このデータベースとINFORMATION_SCHEMA
間の同期の問題は注意が必要です。
これらのテーブルを1時間ごとまたは5分ごとに同期するプロシージャを使用できます(テーブルの構造はどのくらいの頻度で変更されますか?)。
別のアイデアは、MySQLプロキシを使用してALTER TABLE
ステートメント(およびCREATE
とDROP
とCREATE INDEX
、および必要な情報を変更するその他のステートメント)をキャッチして同期することです。これらのステートメントが成功した後の複製情報スキーマ。
列名だけが必要で、データ型、長さ、使用可能なインデックスなどの他の情報は必要ない場合は、SHOW COLUMNS
の使用を、1行のみを返す(高速)クエリでLIMIT 1
に置き換えることができます。またはなし、LIMIT 0
または:
SELECT * FROM TableName WHERE FALSE ;
SELECT *
の使用に対する一般的なアドバイスにもかかわらず、これは、他に何も役に立たない正当なケースである可能性があります。 (*
以外はすべてエラーになる可能性があります!)
この特定のケースでは、INFORMATION_SCHEMA
は赤いニシンだと思います。私自身のSHOW COLUMNS
パフォーマンスのテストから、innodb_stats_on_metadata
変数はMyISAMまたはInnoDBテーブルのいずれにも影響を与えないようです。
ただし、 MySQL 5.0マニュアル ...
一部の条件では、メモリ内の一時テーブルを使用できません。その場合、サーバーは代わりにディスク上のテーブルを使用します。
[...]
SHOW COLUMNS
およびDESCRIBE
ステートメントは一部の列のタイプとしてBLOB
を使用するため、結果に使用される一時テーブルはディスク上のテーブルです。
これはMySQL 5.5の時点ではマニュアルから削除されているようですが、そのバージョンでもまだ適用されているようです...
mysql> SHOW VARIABLES LIKE 'version';
+---------------+-------------------------+
| Variable_name | Value |
+---------------+-------------------------+
| version | 5.5.41-0ubuntu0.14.04.1 |
+---------------+-------------------------+
1 row in set (0.00 sec)
mysql> SHOW STATUS LIKE 'Created_tmp_disk_tables';
+-------------------------+-------+
| Variable_name | Value |
+-------------------------+-------+
| Created_tmp_disk_tables | 0 |
+-------------------------+-------+
1 row in set (0.00 sec)
mysql> SHOW COLUMNS FROM mysql.user;
[...snip...]
42 rows in set (0.00 sec)
mysql> SHOW STATUS LIKE 'Created_tmp_disk_tables';
+-------------------------+-------+
| Variable_name | Value |
+-------------------------+-------+
| Created_tmp_disk_tables | 1 |
+-------------------------+-------+
1 row in set (0.00 sec)
クエリ結果セットで返されるフィールド情報には、SHOW COLUMNS
によって返されるものと同じ情報が含まれているため、SELECT * FROM my_table LIMIT 0
はクエリごとにディスク上の一時テーブルを作成せずに同じことを実現する必要があります。
PHPでフィールド名を取得する簡単な例...
$mysql = new mysqli('localhost', 'root', '', 'my_database');
$field_names = array();
$result = $mysql->query("SELECT * FROM my_table LIMIT 0");
$fields = $result->fetch_fields();
foreach ($fields as $fields)
{
$field_names[] = $field->name;
}
var_dump($field_names);
この方法でフィールド情報を取得すると、デコードが少し厄介になります。データ型とフラグを引き出すには、基になる MYSQL_FIELD
構造体の説明を参照する必要がありますが、私のシステムでは約7倍高速に実行されます。
@ yerpcubeの回答の最初の提案 (+1)が好きですが、何か提案したいです
--no-data
--routines
--triggers
--all-databases
または--databases
に続けて、必要なデータベースのリストしたがって、mysqldumpは次のようになります。
mysqldump --no-data --routines --triggers --all-databases > ImportFile.sql
それでおしまい。今後は、このポート3307データベースインスタンスに接続して、スキーマ関連のクエリを実行するだけで済みます。変更された本番データベースのテーブルを知っている場合は、mysqldumpを本番環境からmysqldumpして、ポート3307インスタンスに再度リロードします。
警告:mysqlインスタンスを本番環境と同じマシンにインストールする場合は、必ずそのインスタンスに接続してください
mysql -u... -p... -h127.0.0.1 -P3307 < ImportFile.sql
実行すると
mysql -u... -p... -P3307 < ImportFile.sql
ホース生産になります。ので注意してください !!!!
別の方法は、別個のDBサーバーを使用することです。