私はこのテーブルを持っています
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 週間にわたる出席 (つまりattended
、total
一定期間のイベント)のレポートを生成する必要があります。
これは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
ていることを教えてくれます。
だからここに私の質問があります:
- これを高速化するためにインデックスとクエリを修正できる他の方法はありますか?
- MySQL にはコンパイルされたステートメントのキャッシュがあると思います。結果のキャッシュについて話しているのではありません。PHP オペコードと HTML キャッシュを考えてください。私はすでに試してみ
SQL_NO_CACHE
ましたが、それでも同じ応答時間が得られ、 0 です。phpMyAdminquery_cache_size
が約 800 ミリ秒でクエリを報告するのを見たと断言できますが(これは受け入れられません)、今は取得できません。クエリが実行されるたびに実際の速度を測定するにはどうすればよいですか? - これらのクエリをストアド プロシージャに入れると、これらはより高速になりますか?
- 保管方法について何か考えはありますか?現在、データベースのサイズは約 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