保持非规范化架构最新的最佳实践?

时间:2009-05-12 23:01:53

标签: mysql database-design optimization

我正在创建一个带点数的小游戏,所以我有一个架构:

create table points (
  id int,
  points int,
  reason varchar(10)
)

并获得用户拥有的积分数是微不足道的:

select sum(points) as total from points where id = ?
然而,随着积分表的扩展,性能变得越来越重要。我想做点什么:

create table pointtotal (
  id int,
  totalpoints int
)

保持同步的最佳做法是什么?我是否尝试在每次更改时更新pointtotal?我是否运行每日脚本?

(假设我有正确的钥匙 - 他们因为简洁而被排除在外)

编辑:

以下是我遗漏但应该有所帮助的一些特征:

点数的插入/更新并不常见 有大量的条目,并且有大量的请求 - 正如您所见,密钥非常简单。

8 个答案:

答案 0 :(得分:8)

最佳实践是使用规范化的数据库架构。然后DBMS使其保持最新,因此您不必这样做。

但我理解使非规范化设计具有吸引力的权衡。在这种情况下,最佳做法是更新每次更改的总数。调查触发器。这种做法的优点是,您可以使总数与变化保持同步,这样您就不必考虑它是否已过时。如果提交了一个更改,则也会提交更新的总计。

然而,这在并发更改方面存在一些缺点。如果您需要同时更改相同的总计,并且您可以容忍总数“最终一致”,那么使用总计的定期重新计算,这样您一次只能确保一个流程正在改变总数。

另一个好的做法是缓存汇总数据库之外的总计,例如memcached或应用程序变量,因此每次需要显示值时都不必访问数据库。


查询“select sum(points) as total from points where id = ?”应该需要2秒,即使您有大量行和大量请求。

如果covering index定义超过(id, points),则查询可以生成结果而无需从表中读取数据;它可以通过读取索引本身的值来计算总数。使用EXPLAIN分析您的查询,并在Extra列中查找“Using index”注释。

CREATE TABLE Points (
  id     INT,
  points INT,
  reason VARCHAR(10),
  KEY    id (id,points)
);

EXPLAIN SELECT SUM(points) AS total FROM Points WHERE id = 1;

+----+-------------+--------+------+---------------+------+---------+-------+------+--------------------------+
| id | select_type | table  | type | possible_keys | key  | key_len | ref   | rows | Extra                    |
+----+-------------+--------+------+---------------+------+---------+-------+------+--------------------------+
|  1 | SIMPLE      | points | ref  | id            | id   | 5       | const |    9 | Using where; Using index | 
+----+-------------+--------+------+---------------+------+---------+-------+------+--------------------------+

答案 1 :(得分:2)

一定要保持基础表的规范化。如果您可以处理可能有一天的数据,则每次运行一个脚本(您可以安排它),进行汇总并填充新表。最好只在每天晚上从源表重新创建一个东西,以防止两者之间的任何不一致。

那就是说,根据记录的大小,你必须要么服务器速度很慢,要么记录数量非常大,因为记录很小,id上带索引字段的记录应该很快就能为你求得 - 但是,我我的心态是,如果你可以提高用户响应时间甚至几秒钟,就没有理由不使用汇总表 - 即使数据库纯粹主义者反对。

答案 2 :(得分:1)

在同一个表上有额外的totalpoints列,并为每行创建/更新创建/更新totalpoints的值。

如果您需要特定记录的总分,则可以在不计算总分的情况下查找该值。例如,如果您需要totalpoint的最后一个值,您可以这样得到它:

SELECT totalpoint FROM point ORDER BY id DESC LIMIT 1;

答案 3 :(得分:1)

还有另一种方法:缓存。即使它只缓存了几秒钟或几分钟,这也是经常访问的值的胜利。并且可以使用缓存更新来解除缓存提取。这样,始终以恒定时间返回合理的当前值。棘手的一点是让fetch产生一个新进程来进行更新。

答案 4 :(得分:1)

我建议您创建一个用于访问和修改数据的图层。您可以使用这些数据库访问功能将数据维护封装在所有表中,以使冗余数据保持同步。

答案 5 :(得分:1)

在这种情况下你可以采用任何一种方式,因为它不是很复杂。

作为一般规则,我更倾向于允许数据暂时不一致,具有足够的冗余,并且周期性过程可以解决不一致问题。但是,采用触发机制来鼓励及早执行周期性流程没有任何害处。

我之所以这样,是因为依赖基于事件的通知式代码来保持一致性,在更复杂的情况下,会使代码复杂化并使验证变得困难。

答案 6 :(得分:1)

您还可以创建另一个报告模式,并通过执行计算的某个过程以固定的时间间隔重新加载。这不适用于实时信息 - 但这是非常标准的做事方式。

答案 7 :(得分:0)