我正在创建一个带点数的小游戏,所以我有一个架构:
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?我是否运行每日脚本?
(假设我有正确的钥匙 - 他们因为简洁而被排除在外)
编辑:
以下是我遗漏但应该有所帮助的一些特征:
点数的插入/更新并不常见 有大量的条目,并且有大量的请求 - 正如您所见,密钥非常简单。
答案 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)