我有这张桌子
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
合并这两个查询 - 在第一个查询中再次使用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
告诉我,正在使用。
所以这是我的问题:
SQL_NO_CACHE
,我仍然得到相同的响应时间,query_cache_size
为0.我可以发誓我看到phpMyAdmin报告查询大约 800ms 一次(这是不可接受的但是我现在不懂。如何在每次运行时测量查询的真实速度?如果您需要更多信息,请在下方发表评论。我会尝试提供它。我确实尝试过这样做,但考虑到庞大的数据库(我迄今为止最大的数据库)和高性能的需求,我真的需要一些建议: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 :(得分:2)
在表上添加covering或聚簇索引将为您提供最佳性能:
您还可以在表成员上添加额外的索引:
成员indeces:(member_id,dept_id)
您可以启用Query Cache来缓存查询输出,但查询缓存不适用于过程。要衡量查询的准确速度,您可以使用mysqlslap client utility
。
存储过程中的查询在速度方面没有太大区别,但它会节省一些额外的查询解析开销并将输出发送给客户端。
使用分片或复制在不同服务器上分发数据将在可伸缩性方面提供帮助。对巨大的桌子进行分区也会对您有所帮助。
答案 1 :(得分:0)
您的设计似乎有效。我认为,报告应用程序在200毫秒(甚至高达800毫秒)内完成报告是完全正常的。至于新的索引,我首先会检查它是否真的值得做,'但是,如果你有所有成员平均只分布在5个depts上,那么member.dept_id
上的索引将没有用 - 它更便宜在这种情况下执行全面扫描。
我没有看到测量查询“真实”速度的重点,因为数据库可以通过有效缓存数据来加速数据访问。因此,如果你处于一个新启动的数据库服务器上,你的查询需要800毫秒,进一步执行的时间会减少到50-100毫秒,那么这是一个很好的设置,这就是我在日常工作中的目标
我对此表示怀疑,因为与调用时间过程解析的所有语句的好处相比,存储过程将为您提供执行过程并获得结果所需的额外时间。
目前您的速度适用于非OLTP应用程序。对我而言,似乎通过attendance
列对attendance_week
表进行分区将为您提供良好的性能提升,因为您的所有查询都围绕此列进行。但是,如果系统中有更多数据,至少需要3-4周的数据,那么效益就会显现出来。
但是,对于OLTP系统,我的假设可能是错误的。您能指定所提供示例的有用使用区域吗?
此外,您可以查看EXPLAIN
语句的实际输出结果。