3

私はこのテーブルを持っています

attendance (4M rows at the moment, growing 1.2M per week):

-------------------------------------------------------------
| member_id | attendance_week | attendance_date | event_id  |
------------------------------------------------------------
|  INT (10) |   TINYINT(2)    |   TIMESTAMP     |TINYINT(3) |
-------------------------------------------------------------

attendance indeces:
--------------------------------------------------
| PRIMARY (attendance_week, member_id, event_id) |
| member_id (member_id)                          |
| event_id (event_id, attendance_week)
| total (attendance_week, event_id)              |
--------------------------------------------------

members (400k rows at the moment growing 750 a week):
-------------------------
| member_id |  dept_id  |
-------------------------
|  INT (10) |SMALLINT(5)|
-------------------------

member indeces:
-----------------------
| PRIMARY (member_id) |
| 
-----------------------

member_idイベントは毎週行われます。つまり、毎週とのペアが表示event_idされます。

ここで、特定の部署の各イベントcurrent attendance(つまり、このメンバーが既にチェックインしている場合) と、少なくとも 4 週間にわたる出席 (つまりattendedtotal一定期間のイベント)のレポートを生成する必要があります。

これはcurrent_attendanceレポートの一部です。部門のすべてのメンバーをフェッチしLEFT JOIN、今週のイベントNULLで欠席を取得します。

SELECT
  m.member_id AS id,
  a.event_id AS attended
FROM
  members AS m
LEFT JOIN
  attendance AS a
  ON
    a.member_id = m.member_id AND
    a.attendance_week = :week AND
    a.event_id = :event
WHERE
  m.dept_id = :dept
GROUP BY
  m.member_id

これはattendedレポートの一部です。:

SELECT
  a.member_id,
  COUNT(a.event_id)
FROM
  attendance a 
  JOIN
    members m 
    ON 
      a.member_id = m.member_id AND
      m.dept_id = :dept
WHERE
  a.attendance_week BETWEEN :start AND :end
GROUP BY
  a.member_id

おそらく、最初のクエリでテーブルを再度LEFT JOIN-ingするだけで、これら 2 つのクエリをマージできます。attendance

そして最後に、そのtotal部分について

SELECT
  attendance_week,
  COUNT(DISTINCT event_id)
FROM
  attendance
WHERE
  attendance_week BETWEEN :start AND :end
GROUP BY
  attendance_week

これらは、これらのテーブルに対して実行される主なクエリです。現時点では、クエリは平均150 ~ 200 ミリ秒(phpMyAdmin によると) 実行されますが、これは遅いと思います。私のインデックスが使用されEXPLAINていることを教えてくれます。

だからここに私の質問があります:

  1. これを高速化するためにインデックスとクエリを修正できる他の方法はありますか?
  2. MySQL にはコンパイルされたステートメントのキャッシュがあると思います。結果のキャッシュについて話しているのではありません。PHP オペコードと HTML キャッシュを考えてください。私はすでに試してみSQL_NO_CACHEましたが、それでも同じ応答時間が得られ、 0 です。phpMyAdminquery_cache_sizeが約 800 ミリ秒でクエリを報告するのを見たと断言できますが(これは受け入れられません)、今は取得できません。クエリが実行されるたびに実際の速度を測定するにはどうすればよいですか?
  3. これらのクエリをストアド プロシージャに入れると、これらはより高速になりますか?
  4. 保管方法について何か考えはありますか?現在、データベースのサイズは約 400MB です。1年後、わかりませんが、おそらく3GBですか?これはスケーラブルですか?私は DBA に関してはまったくの初心者です。マスターとスレーブのレプリケーションとパーティショニングについては読んだことがありますが、これが役に立つかどうかはわかりません。

さらに情報が必要な場合は、以下にコメントしてください。提供してみます。私は本当にこれを一人でやろうとしましたが、巨大なデータベース(これまでで最大のもの)と高いパフォーマンスの要求を考えると、本当にアドバイスが必要です:D

ありがとう

編集

3 番目のクエリでは登録日が考慮されていないため、新しく登録されたメンバーの出席率が低く表示されます。members テーブルに registration_date 列があります。その変数をクエリに組み込む方法はありますか? または、3 つのクエリすべてを一度にマージしますか? それらはすべて、各ユーザーに依存する値を返すためです。

編集

最初の 2 つのクエリをマージすることができました。

    SELECT
      m.member_id AS id,
      a.event_id AS attended,
      COUNT(b.event_id) AS total_attended
    FROM
      members AS m
      LEFT JOIN
        attendance AS a
        ON
          a.member_id = m.member_id AND
          a.attendance_week = :week AND
          a.event_id = :event
      LEFT JOIN
        attendance AS b
        ON
          b.member_id = m.member_id AND
          b.attendance_week BETWEEN :start AND :end
    WHERE
      m.dept_id = :dept
    GROUP BY
      m.member_id

このクエリは、最初の実行で 925 ミリ秒、後続のリクエストで 15 ミリ秒実行されます。

これは上記のクエリの結果ですEXPLAIN

members table:
id:            1
select_type:   SIMPLE
table:         m
type:          ref
possible_keys: dept_id
key:           dept_id
key_len:       3
ref:           const
rows:          88
Extra:         Using where; Using index

attendance table 1 (for the boolean attended part):
id:            1
select_type:   SIMPLE
table:         a
type:          eq_ref
possible_keys: PRIMARY,member_id,event_id,total
key:           PRIMARY
key_len:       6
ref:           const,arms_db.m.member_id,const
rows:          1
Extra:         Using index

attendance table 2 (for the total attendanded part):
id:            1
select_type:   SIMPLE
table:         b
type:          ref
possible_keys: PRIMARY,member_id,total
key:           member_id
key_len:       4
ref:           arms_db.m.member_id
rows:          5
Extra:         Using index

そして、EXPLAIN最後のクエリの場合:

id:            1
select_type:   SIMPLE
table:         attendance
type:          range
possible_keys: PRIMARY,toral
key:           total
key_len:       2
ref:           NULL
rows:          9
Extra:         Using where; Using index for groub-by
4

2 に答える 2

2

テーブルにカバリングインデックスまたはクラスター化インデックスを追加すると、最高のパフォーマンスが得られます。

  1. テーブルメンバーに追加のインデックスを追加することもできます:

    メンバーインデックス:(member_id, dept_id)

  2. クエリ キャッシュを有効にしてクエリ出力をキャッシュすることはできますが、クエリ キャッシュはプロシージャでは機能しません。クエリの正確な速度を測定するには、mysqlslap client utility .

  3. ストアド プロシージャ内のクエリは、速度の点で大きな違いはありませんが、クエリの解析とクライアントへの出力の送信にかかる追加のオーバーヘッドを節約できます。

  4. シャーディングまたはレプリケーションを使用して異なるサーバーにデータを分散すると、スケーラビリティの点で役立ちます。巨大なテーブルでのパーティショニングもメリットがあります。

于 2012-08-09T10:32:11.160 に答える
0
  1. あなたのデザインは有効なようです。レポートアプリケーションでは、200ミリ秒以内(最大800ミリ秒でも)にレポートを作成することはまったく問題ないと思います。新しいインデックスについては、最初に実行する価値があるかどうかを確認しました。たとえば、メンバー全員が5つの部門に均等に分散している場合、インデックスオンmember.dept_idは役に立ちません。フルスキャンを実行する方が安価です。このような場合には。

  2. データベースはデータを効果的にキャッシュすることでデータアクセスを高速化するために存在するため、クエリの「真の」速度を測定する意味がわかりません。したがって、新しく起動したDBサーバーでクエリが約800ミリ秒かかり、それ以降の実行時間が50〜100ミリ秒に短縮される状況にある場合、これは適切な設定であり、これが私の日常業務で目指していることです。 。

  3. ストアドプロシージャを使用すると、プロシージャが呼び出されるまでにすべてのステートメントを解析する利点と比較して、プロシージャを実行してその結果を取得するために必要な時間が少し余分にかかるため、疑問です。

  4. 現時点では、OLTP以外のアプリケーションの速度は問題ありません。そして、私にとっては、すべてのクエリがこの列を巡回するためattendance、列ごとにテーブルを分割attendance_weekすると、パフォーマンスが大幅に向上するようです。ただし、システムに少なくとも3〜4週間分のデータがあれば、メリットが明らかになります。

ただし、OLTPシステムの場合、私の仮定は間違っている可能性があります。提供された例の使用目的領域を指定できますか?

EXPLAINまた、クエリのステートメントの実際の出力を確認するとよいでしょう。

于 2012-08-09T10:42:25.167 に答える