2

ページの読み込み時にこのクエリを初めて実行するときは、約1ミリ秒かかるはずですが、常に約100ミリ秒かかります。MySQLコンソールとPhpMyAdminを介してまったく同じクエリを実行しましたが、常に高速です。

私は次のようにPHP内で時間を計りました:

$t = microtime(true);
mysql_query($sql, $this->id);
die((microtime(true)-$t)*1000);

まったく同じクエリを2回実行すると(たとえば、中央の行を複製して)、2回目はほぼ瞬時に実行されます。

クエリは次のようになります。

SELECT `user_id`, `login`, `first_name`, `last_name`, `name`, `email`, ... FROM `users` WHERE `user_id`='1000' LIMIT 1

実験の結果、選択するフィールドの数を減らすと、突然高速に実行されることがわかりました。26番目の列を追加すると、1ミリ秒から100ミリ秒にジャンプします。26列目は何でも構いませんが、「1」のような定数でも急に遅くなります。

これを引き起こしている可能性があり、どうすれば修正できますか?


スキーマ:

CREATE TABLE `user` (
    `f1` int(11) unsigned NOT NULL DEFAULT '0',
    `f2` varchar(50) NOT NULL DEFAULT '',
    `f3` varchar(32) NOT NULL DEFAULT '',
    `f4` varchar(50) DEFAULT NULL,
    `f5` varchar(50) DEFAULT NULL,
    `f6` varchar(100) NOT NULL DEFAULT 'Anonymous',
    `f7` varchar(100) DEFAULT NULL,
    `f8` varchar(100) NOT NULL DEFAULT 'Anonymous',
    `f9` tinyint(1) NOT NULL DEFAULT '0',
    `f10` tinyint(1) unsigned NOT NULL DEFAULT '0',
    `f11` varchar(250) DEFAULT NULL,
    `f12` int(10) unsigned NOT NULL DEFAULT '0',
    `f13` varchar(250) NOT NULL DEFAULT '',
    `f14` int(10) unsigned NOT NULL DEFAULT '0',
    `f15` varchar(100) DEFAULT NULL,
    `f16` varchar(25) DEFAULT NULL,
    `f17` varchar(25) DEFAULT NULL,
    `f18` varchar(25) DEFAULT NULL,
    `f19` varchar(200) DEFAULT NULL,
    `f20` varchar(50) DEFAULT NULL,
    `f21` varchar(50) DEFAULT 'BC',
    `f22` varchar(50) DEFAULT 'Canada',
    `f23` varchar(20) DEFAULT NULL,
    `f24` bigint(20) unsigned NOT NULL DEFAULT '0',
    `f25` bigint(20) unsigned NOT NULL DEFAULT '0',
    `f26` int(5) unsigned zerofill NOT NULL DEFAULT '00000',
    `f27` tinyint(1) NOT NULL DEFAULT '0',
    `f28` tinyint(1) unsigned NOT NULL DEFAULT '0',
    `f29` tinyint(1) NOT NULL DEFAULT '0',
    `f30` tinyint(1) unsigned NOT NULL DEFAULT '0',
    `f31` varchar(50) DEFAULT NULL,
    `f32` varchar(100) NOT NULL DEFAULT '',
    `f33` bigint(20) unsigned NOT NULL DEFAULT '0',
    `f34` bigint(20) unsigned NOT NULL DEFAULT '0',
    `f35` varchar(250) DEFAULT NULL,
    `f36` text NOT NULL,
    `f37` tinyint(1) unsigned NOT NULL DEFAULT '0',
    `f38` bigint(20) NOT NULL DEFAULT '0',
    `f39` bigint(20) NOT NULL DEFAULT '0',
    `f40` tinyint(1) unsigned NOT NULL DEFAULT '0',
    `f41` tinyint(1) unsigned NOT NULL DEFAULT '0',
    PRIMARY KEY (`f1`),
    UNIQUE KEY `f2` (`f2`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

罪のない人を保護するためにフィールド名を変更しました。


更新:魔法の列の制限について間違っていた可能性があります。結局のところ、これはバイト制限だと思います...実際に必要な少数のフィールドを除いて、すべての数値で試しました。

SELECT `f1`, `f2`, `f3`, `f4`, `f5`, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55 FROM `wx_user` WHERE `user_id`='1000' LIMIT 1

それは1msで実行されます。最後に「56」を追加すると、約100msかかります。


私はさらにいくつかのテストを行いました:

mysql_query("SELECT 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' FROM `user` WHERE `f1`='1000' LIMIT 1");

1200〜1300 aのどこかで、1msから100msにジャンプします。

より多くの列を使用して同じことを繰り返すことができますが、データ(int)は小さくなります。

これは私に2つのことを示唆しています:

  1. 送信されるバイト数と関係があります
  2. 約50intを選択すると、単一の1300 charフィールドを選択するのと同じ効果があるため、列を追加するオーバーヘッドは非常に大きくなります。

今考えてみると、この数はジョンが提案した数に非常に近いものです。1300文字+オーバーヘッドは、ジョンが言及した1500MTUの制限におそらく等しいでしょう。

それはMySQL設定ですか、それともOS設定ですか?私が実験できることはありますか?

4

3 に答える 3

2

MySQl はクエリをキャッシュするため、2 回目にクエリを実行すると、キャッシュから読み込まれ、はるかに高速になります。

また、テーブルに適切なインデックスがあることを確認してください。これにより、作業が大幅に高速化される可能性があります。

于 2012-08-15T22:00:02.517 に答える
2

確かに、MySQL にはキャッシュがあり、速度が向上します。

しかし、それだけではありません。問題はクエリにはありません。同じ問題に遭遇しましたが、デバッグできない理由はまさにキャッシュです。同じ問題を再度発生させることはできません。これは MySQL だけではなく、物事をキャッシュするファイルシステムでもあるため、最初の遅さを再現することは完全に不可能です。さらに、2 回目に発生する可能性があるかどうかはわかりません。また。MySQL を再起動しようとしましたが、キャッシュをフラッシュしようとしましたが、何も最初の実行を再現できません。

そしてさらに謎なのは!意味のない回避策があります: クエリの前に CHECK TABLE を実行すると (そして、それが最初だけであることを確認してください!)、クエリ自体は通常の速度で実行されます。いいえ、既にキャッシュされている場合と同じ速度ではありません。それは普通ですが、まだ初めての速度です。ご想像のとおり、もう一度呼び出すとすぐに結果が得られます。

今では誰もがうなずき、「もちろん、ファイルシステムはチェックを実行するときにテーブルファイルをキャッシュするので、次回はより速く実行されます」と言います。 しかし、そうではありません。 他のプログラムを使用して MYD ファイルをメモリに読み込もうとすると (単純な「コピー」を実行しました)、インデックスもコピーしても問題は解決しません。しかし、CHECK は本当に役立つことをしているようです。

結論: - これは単純な「遅いクエリ」の問題ではなく、闇の魔法です。そして明らかに、この回避策は実稼働環境向けではありません。2 つの重要なことが示されているので、ここで言及します。

  1. 違いはキャッシュだけではありません。それだけではありません。
  2. 開発時間中に、問題の簡単な修正を適用できます。

これが数人の人に役立つことを願っています-数日かかりました:)

于 2014-04-10T14:33:58.677 に答える
2

ジョンが述べたように、同じクエリを 2 回実行するとキャッシュから結果が取得されるため、ほぼ瞬時に実行されます。

そうは言っても、X番目の列が追加されているときに、クエリが本来よりも長く「実行」される場合があります。いくつかの可能性:

  • X 列が非常に大きく (CHAR(65000) など)、送信に時​​間がかかる場合、
  • すべての (X-1) 列は、既に MySQL メモリにあるインデックスの一部ですが、X 番目の列への参照は、MySQL がドライブから行を読み取ることを強制します。
  • 同時クエリは参照テーブルをロックするか、CPU/HDD/メモリに負荷をかけます。
  • X 番目の列は TEXT/BLOB であり、追加のディスク シークを実行して外部ファイルから列の値を取得する必要があります (BLOB および TEXT 値はテーブル データ ファイルの外部に格納されます)。
  • X 列は、MySQL サーバーからアプリケーションに送信する必要がある行データに十分なバイトを追加し、行全体を送信するために 2 つ以上のネットワーク パケットを使用する必要があります。本当に、本当に、** 本当に ** クエリからミリ秒ごとに絞り出さなければならない場合は、クエリ結果が 1 つの MTU フレーム (通常は 1500 バイト) に収まるようにします。スニファーを使用して確認します。
  • 他のケースは非常に低レベルであり、通常の人間は現実の世界でそれらを見ることはありません.

組み込みの MySQL クエリ プロファイリングを使用して、どのクエリ実行ステージに時間がかかるかを調べることもできます。走る:

SET profiling = 1;
[..run Your queries, each one is numbered..]
SHOW PROFILE FOR QUERY n; -- SHOW PROFILE FOR QUERY 1;
SET profiling = 0;

どこで追加の時間が費やされているかについて、かなり良い洞察が得られるはずです。クエリ プロファイリングの詳細については、「MySQL クエリプロファイリング」を参照してください。

于 2012-08-15T22:37:54.233 に答える