2012-08-09 74 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