2012-08-09 108 views
3

我有這個表的MySQL索引用於特定查詢

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_idevent_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第一查詢attendance表合併這兩個查詢。

最後的total部分

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

這些是將這些表上運行的主要查詢。在這一刻,查詢運行的平均值爲150 - 200ms(根據phpMyAdmin),我認爲這很慢。 EXPLAIN告訴我,我的單位使用正在使用

因此,這裏是我的問題:

  1. 有沒有辦法,我可以修改我的indeces和查詢,使這個更快的任何其他方式?
  2. 我假設MySQL有編譯語句的緩存。我不是在談論結果緩存,認爲PHP操作碼vs HTML緩存。我已經嘗試SQL_NO_CACHE,我仍然得到相同的響應時間,query_cache_size是0.我可以發誓,我看到phpMyAdmin在800ms報告查詢一次(這是不可接受的),但我現在沒有得到它們。如何在每次運行時測量查詢的真實速度?
  3. 如果我把這些查詢放在存儲過程中,這些會更快嗎?
  4. 存儲方法的任何想法?該數據庫目前大小約爲400MB。一年後,我不知道,也許3GB?這是可擴展的嗎?當談到DBA時,我真的很新,我讀過主從式複製和分區,但我不知道它是否適合這種情況。

如果您需要更多信息,請在下面評論。我會盡力提供它。我真的嘗試獨自做到這一點,但鑑於龐大的數據庫的要求(我的迄今爲止規模最大)和高性能,我真的需要一些建議:d

感謝

編輯

我剛剛意識到我的邏輯存在一個可怕的缺陷,新登記的成員將顯示出勤率低,因爲第三個查詢沒有考慮登記日期。我在我的成員表中有一個registration_date列,有什麼方法可以將該變量合併到查詢中嗎?或者將所有三個查詢合併一次?因爲它們都返回依賴於每個用戶的值。

編輯

我已經設法前兩個查詢合併:

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 

此查詢925ms運行在後續請求第一次運行和15ms的。

這是結果上述查詢的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 
+0

什麼是MySQL服務器版本? – 2012-08-10 01:53:24

+0

5.5.25a社區服務器 – 2012-08-10 02:34:03

+0

表是MyISAM還是InnoDB? – 2012-08-10 09:08:24

回答

2

上表將爲您提供最佳的性能添加covering或聚簇索引:

  1. 成員的indeces:能工作臺部件,也增加額外指數(member_id,dept_id爲)

  2. 您可以啓用Query Cache緩存查詢輸出,但查詢緩存不能與程序工作。要衡量確切的查詢速度,您可以使用mysqlslap client utility。在存儲過程中

  3. 查詢將沒有太大的差別在速度方面,但它會節省查詢解析的一些額外的開銷和發送輸出到客戶端。

  4. 使用分片或複製在不同服務器上分配數據將有助於您提高可伸縮性。在巨大的桌子上分區也將使您受益。

+0

對於#4,這種設置適合分區和複製嗎?可以同時使用嗎? – 2012-08-10 05:52:56

+0

是的,你可以在同一個表上進行分區和複製。 – Omesh 2012-08-10 07:46:56

+0

關於我的問題更新的任何想法? – 2012-08-10 08:32:12

0
  1. 你的設計似乎有效。我認爲,在200ms內完成報告(甚至高達800ms)對於報告應用程序來說是完美的。至於新的指數,我會首先檢查一下它是否值得這樣做,'因爲,比如說,如果你的所有成員均等分佈在5個部門中,那麼member.dept_id上的索引將不會有用 - 執行完整部分會更便宜在這種情況下掃描。

  2. 我沒有看到測量查詢的「真實」速度的點,因爲數據庫是通過有緩存effectivelly您的數據,以加快數據訪問。所以,如果你在一個情況是一個剛開始DB服務器上查詢時需要800ms的圓,並進一步執行次下井50-100ms,那麼這是一個很好的設置,這就是我的目標在我的日常工作。

  3. 我對此表示懷疑,因爲與調用時間過程解析所有語句的好處相比,存儲過程會給您一小部分額外的時間來執行過程並獲得結果。

  4. 目前,您的速度對於非OLTP應用程序來說還不錯。對於我來說,分區attendance表的attendance_week列會給你一個很好的性能提升,因爲所有的查詢都圍繞着這一列。但是,如果系統中有更多數據,至少需要3-4周才能看到好處。

但是,我的假設可能是錯誤的,但對於OLTP系統。你能指定所提供示例的整合使用區域嗎?

另外,查詢EXPLAIN語句的實際輸出結果是很好的。

+0

這實際上是一個OLTP系統(如果我正確理解維基百科的說法)。我在這裏詳細介紹的是每個運營商在一系列交易後產生的系統報告部分。以下是報告輸入部分的描述:http://serverfault.com/questions/411804/system-requirements-of-a-write-heavy-applications-serving-hundreds-of-requests-p我已經對出席的請求進行了一些修改,現在按批次發送,但是member_id查找仍然按每個成員進行。但是,我認爲,這仍然是一個沉重的應用程序。 – 2012-08-10 01:06:38

+0

我編輯了這個問題:D – 2012-08-10 08:32:27

+0

@RolandoCruz,好吧,在15ms內完成查詢是很好的。你的'EXPLAIN'輸出看起來非常好。你還想做什麼?我對這樣的結果感到滿意。 – vyegorov 2012-08-10 08:51:59