0

最近、memcache にキャッシュする前のクエリの処理に時間がかかっています。この例では、10 秒かかりました。この場合、私がやろうとしているのは、最新の 10 件のヒットを取得することだけです。

125,592 行すべてをロードして 10 行しか返さないような気がしますが、そうですか?

# User@Host: root[root] @ localhost []
# Query_time: 10 Lock_time: 0 Rows_sent: 10 Rows_examined: 125592
SELECT * FROM ヒット WHERE キャンペーン ID = 30 ORDER BY ID DESC LIMIT 10;

別の遅いクエリを次に示します。

# 時間: 090214 5:00:40
# User@Host: root[root] @ localhost []
# Query_time: 3 Lock_time: 0 Rows_sent: 1 Rows_examined: 128879
SELECT count(DISTINCT(ip_address)) AS count_distinct_ip_address FROM `hits` WHERE (campaign_id = 30);

phpMyAdmin でクエリを実行すると、1.3395 秒かかります。実行するSELECT * FROM hitsだけで0.0001秒しかかかりませんが。すべてのヒットを返すのに、それらを並べ替えるよりも時間がかからないのは非常に奇妙だと思いますか、それとも単に並べ替えているだけですか?

私のテーブルを見たい人のために:

CREATE TABLE `ヒット` (
  `id` int(11) unsigned NOT NULL auto_increment,
  `hostname` varchar(255) NOT NULL,
  `url` tinytext NOT NULL,
  `user_agent` tinytext NOT NULL,
  `created_at` タイムスタンプ NOT NULL デフォルト CURRENT_TIMESTAMP,
  `ip_address` varchar(15) NOT NULL,
  `campaign_id` int(11) NOT NULL,
  主キー (`id`)、
  KEY `campaign_id` (`campaign_id`),
  KEY `ip_address` (`ip_address`)
);
4

5 に答える 5

5

インデックスの選択性が低いようです。campaign_idつまり、この値を持つレコードがたくさんあります。

非常に多くのレコードを注文すると、多くの時間がかかります。

注文のために で使用INDEX SCANしてみてください:PRIMARY KEY

/* Edited, as MySQL does not use live feed from the derived source with ORDER BY */
SELECT *
FROM hits
WHERE IFNULL(campaign_id, campaing_id) = 30
ORDER BY id DESC
LIMIT 10;

campaign_id = 302 番目のクエリについては、とにかく全体を完全にスキャンする必要があるため、できることはあまりありませTABLE SCANINDEX SCAN

実際、 はTABLE SCANさらに高速になる可能性があります。

SELECT count(DISTINCT(ip_address)) AS count_distinct_ip_address
FROM `hits`
WHERE IFNULL(campaign_id, campaign_id)  = 30;

そうでない場合は、インデックスを作成し、このインデックス(campaign_id, ip_address)を模倣するトリックを使用できます。INDEX GROUP BY

CREATE INDEX ix_hits_campaign_ip ON hits(campaign_id, ip_address)

SELECT SUM(cnt)
FROM (
SELECT CASE WHEN @r = ip_address THEN 0 ELSE 1 END AS cnt,
  @r := ip_address
FROM
  (SELECT @r:='') r,
  (
  SELECT ip_address
  FROM hits
  WHERE campaign_id = 30
  ORDER BY ip_address
  ) i
) o

ここでのトリックは単純です。結果は必要なく、カウントだけが必要なので、実際の値をスキャンする必要はありません。インデックススキャンで十分です。

残念ながら、MySQL のドキュメントではルーズ インデックス スキャンについてここに記載されていますが、複合インデックスでは実際には動作しません。そのため、 を模倣する必要がありINDEX SCAN WITH GROUP BYます。

ソート順でINDEX RANGE SCANすべてのレコードを取得することを MySQL に強制することでそれを行います。次に、最初のサブクエリで空の文字列に初期化されたセッション変数を使用して 'esをカウントします。campaign_id = 30ip_addressDISTINCT ip_address@r

最初のフィールドでは0、以前のip_address(変数に格納された) 値が現在の値と等しい場合に変数を設定します。それ以外の場合は、に設定し1ます。2 番目のフィールドでは、現在の値をip_address変数に割り当てます。

最後SUMに、最初のフィールドで を取得します。これはもちろんCOUNT (DISTINCT ip_address).

于 2009-02-14T11:54:12.770 に答える
2

インデックス on(campaign_id,id)は、最初のものをかなりうまく処理する必要があります。しかし、明確なものは少しトリッキーです...

編集: MySQL は 1 つのクエリに複数のインデックスを使用しません。そうです、クエリに含まれるすべてのフィールドをカバーする1 つのインデックスが必要です。

于 2009-02-14T05:15:34.880 に答える
1

EXPLAINを使用して、クエリがどのように実行されているかを確認する必要があります。本番環境または本番環境のようなデータでそれを行う必要がありますが、明らかに本番システムでそれを行うべきではありません(もちろん、この演習では開発と本番環境で同じソフトウェアを使用する必要があります)-上記はそれが全表スキャン; これは、使用できるインデックスがないか、カーディナリティが低いなどの理由でインデックスを使用しないことを選択していることが原因である可能性があります。

次に、インデックスを改善するために追加できるインデックスを評価し、それらを追加して再テストし、インデックスを追加してもアプリケーション内の他の機能が損なわれず、他の場所でパフォーマンスが低下しないことを確認して、変更をQAする必要があります。 。スペースとパフォーマンスへの影響を分析する必要があります。これも、テストシステムの本番環境のようなデータを使用して実行できます(もちろん、パフォーマンステストは本番仕様のハードウェアで実行する必要があります)。

インデックスを追加することが正しいことであると確信したら、通常どおりにそれらの変更をソフトウェアリリースにロールインできます。ただし、大きなテーブルではALTER TABLEに注意してください。時間がかかり、テーブルへの書き込みがブロックされます(ただし、120k行はおそらく大きなテーブルではありません)。変更をロールアウトする前に、所要時間と本番環境への影響を確認してください。

于 2009-02-14T23:23:43.440 に答える
0

ただの推測。

SELECT * FROM hits WHERE (campaign_id = 30 AND id > 0) ORDER BY id DESC LIMIT 10;

うまくいけば、My​​SQLはインデックスをマージします。幸運を。

于 2009-02-14T23:20:38.330 に答える