说明:
以下是我的历史数据库表的示例:
+----------------------------------------------+
| DATE ID USERNAME CREDITS |
+----------------------------------------------+
| ... 1 X 12 |
| ... 2 E 2 |
| ... 3 X 1 |
| ... 4 X -7 |
| ... 5 O 4 |
+----------------------------------------------+
问题:
我想知道不是这个( 所有历史记录的SELECT SUM 来向用户显示 INSTEAD 如果历史表如此庞大,那么用户的总信用额度会有不同的表 )会出现问题吗? (几年后我们说+100,000,000条记录)
大多数专业程序员都这样做吗? (如果没有,那是什么)
历史记录部分怎么样,如果用户想要查看积分历史记录,我们应该在* SELECT 记录时将其限制为 LIMIT 100 记录em> * ing或no(性能)
这应该在每次页面刷新或每页更改时运行吗? (如果1000个用户在线并且每次刷新都应用此SELECT查询,则不会降低服务器速度)
编辑回答后:
但是如果我们必须将总数保存在不同的表中并自动更新它们,则存在两个问题:
如果我们确切地说,当用户收到一些积分时,用户不可能在同一时间收到两个不同的积分(这是可能的),因为我们不能将自动增量放在总计中表(因为每个用户只有1条记录)我们可能会错过1个信用,或者如果有这个问题的解决方案,我不知道
如果我们设置一个Cron-Job来经常这样做,那么在cron作业刷新总计表之前,用户信用不是最新的
答案 0 :(得分:7)
如果我们完全在用户收到一些信用时这样做,用户可能会在同一时间(非常可能)收到两个不同的信用,并且因为我们无法将自动增量放在总计表中(因为每个用户只有有1条记录)我们可能会错过1个积分并且不会将它添加到总计表中,或者如果有这个问题的解决方案,我不知道,我现在才应该在这些情况下使用AI
我们不会错过。检查以下SQL语句:
INSERT INTO history SET username = 'X', credits = 2;
UPDATE users SET credits_sum = (SELECT SUM(credits) FROM `history` WHERE username = 'X') WHERE username = 'X';
即使存在两个添加信用的事件被激发的情况,我们的credits_sum也会是最新的,因为它是从存储在数据库中的数据更新的(不是在应用程序中 - 在这种情况下,有时可能会有一些差异)。
当然应该使用users
表中的主键而不是username = 'X'
。
答案 1 :(得分:6)
要在数据库中的条目数增加时使其可扩展,您可以考虑以下事项:
创建两个表:一个,“历史总计”,包含每个用户在今天00:00:00之前的总计;第二个可以是(今天的学分)的(相对)小表。
当您需要当前状态时,将“历史表”中的查找添加到“新信用”(小表,因此很快)。在午夜,您将所有当天的积分添加到总计中,然后(在延迟之后)从“今天”表中删除相应的元素。您需要延迟,因此在查询时不会从“当前”表中删除元素。为确保您始终得到正确的答案,您必须使用“计算的最新/时间”字段标记“历史”数据;并且在您更新了总计之后,然后从“当前”数据库中删除“到目前为止的所有信息”。如果您首先检查总计数据库中的总数和&时间戳,然后从当前数据库计算“总和”,应该没有错误的可能性。这就是更新总计和从当前数据库中删除项目之间出现延迟的原因。
答案 2 :(得分:3)
id BETWEEN x AND y
而不是LIMIT 100 OFFSET 500
答案 3 :(得分:3)
我建议使用一个单独的表来跟踪每个用户的总积分,然后使用触发器使该表保持最新状态。
假设跟踪总积分的表看起来像这样:
CREATE TABLE reputation (
username varchar(20) primary key,
total int
)
然后触发器将如下所示:
CREATE TRIGGER historyInsert AFTER INSERT ON history
FOR EACH ROW BEGIN
INSERT INTO reputation (username,total)
VALUES (NEW.username,NEW.credits)
ON DUPLICATE KEY UPDATE total = total + NEW.credits;
END
当您的历史记录表中插入了任何内容时,它会触发此触发器。对于每个插入的行,触发器会将新值插入信誉表,或者如果用户已存在则更新 total 值。
请注意,INSERT ... ON DUPLICATE KEY UPDATE
是MySQL中的原子操作,因此您不必担心同时发生两次更新。
作为创建单独的信誉表的替代方法,如果您已经拥有某种表单的用户表,则可以始终为每个用户存储总信用额。假设每个用户已经有一个条目,因此触发器不必担心创建新条目 - 它只是更新它们。
然后触发代码变得更加简单:
CREATE TRIGGER historyInsert AFTER INSERT ON history
FOR EACH ROW BEGIN
UPDATE users SET total = total + NEW.credits
WHERE username = NEW.username
END
同样,这个UPDATE
查询是原子的。它只是增加总计字段,所以如果同时发生两次更新,它们就不会相互覆盖 - 这两个数量都将被添加到总数中。
这比每次插入新值时在整个历史记录中计算SUM更有效。
答案 4 :(得分:2)
和其他人一样,我主张分为用户信用的“实时”和“历史”表格。您可以每晚(或每周或任何其他)将记录从实时迁移到历史记录。如果你可以保持“实时”表足够紧凑(并且它的支持索引)主要在内存中,性能应该不是问题。您可能希望在用于维护历史表的任何工作结束时添加第三个“总信用额”表:这样,查看信用总计(不包括今天的)是单个索引读取。
据推测,一旦添加,学分就是不可变的。因此,如果他们不改变,强迫你的程序重新添加它们,反复添加就没有什么意义了。如果您不需要历史信用的交易详细信息,请按月汇总。
该限制可以帮助一些,但突出了设计缺陷:不存储您不会引用的记录:它们继续使用磁盘空间,索引空间和内存。你必须对你真正需要的东西保持相当理性(和冷血)。看看您的商业模式:您为什么希望用户能够查看他们的信用记录?如果你切断了他们可以在任意限制下查看的内容,你会疏远他们吗?您必须能够自己确定策略,因为您了解自己的业务和用户。但要使技术成为政策的仆人,而不是相反。
这些问题涉及整体架构:如果这些查询很昂贵,肯定会在网络会话过程中缓存查询结果。这取决于您的整体架构和您正在使用的技术堆栈。
---第二组问题
在日间边界将信用转移到历史记录中。即使在“实时”表格中,也可以使用当前日期作为选择条件的一部分。这样,你就不会无意中掉落(或重复计算)积分。
不确定我理解。积分将在获得的确切时刻插入“实时”表格,然后复制到日期边界的历史表格中。 “实时”表格总是是当天最新的,历史表格始终是最新的超过一天的东西。
我希望你的项目进展顺利......
答案 5 :(得分:1)
我要说的是跟踪您现在的历史数据,但也将最终结果缓存到信用表或用户表的属性中。
在伪代码中:
function postCreditTransaction($username, integer $credit){
$db->insert("credit_history", array("USERNAME"=>$username, "CREDIT"=>$credit));
$db->update("update user_table set credit = credit + $credit where username = ".$db->quote($username));
}
这将为您提供信用记录提供的详细信息,但对总数的访问权限较低。
要确保所有内容都在步骤中,您可以针对缓存字段中的缓存值执行credit_history表的定期审核。
答案 6 :(得分:1)
好的,让我们从短暂的简历开始:
我的意思是 - 不要使用“所有历史的SELECT SUM ...”来计算“reputation_sum”的新值。 当您从“历史”表中添加/更新/删除记录时,计算total_reputation_change_value并更新“reputation_sum”而不重新计算“历史”表的所有记录上的总和。 INSERT操作的total_reputation_change_value将是 - “credits”字段的值; DELETE也一样,但是一元减去; UPDATE的旧值和新值之间的差异。 如果声誉经常变化,这将提供更多的请求/ s。 这也会更多地违反数据完整性。如果你害怕这一点 - 制作特殊的cron作业,它将通过定期记录历史记录来刷新“reputation_sum”数据。但在大多数情况下(具有正确的工作流程),没有必要这样做。
另外,我建议您不要将USERNAME用作外键(如果您有“users”表,这是外键)。最好使整数USERID。它将在历史表中更快地搜索。
现在让我回答你的问题。
我想知道是不是这个(所有历史记录的SELECT SUM显示用户信用INSTEAD有一个不同的表用于用户的总信用)如果历史表如此巨大会产生问题? (几年后我们说+100,000,000条记录)
是的,如果每次从表中计算出声誉,其中“在几年之后就会说+100,000,000条记录”,由于计算量的原因,这将是非常低效的。如果你有足够的服务器,也许没有滞后,但我相信他们会这样做)
这是大多数专业程序员的工作吗? (如果没有,那是什么)。
这是常见的解决方案,在大多数情况下都可以正常使用。 也许它不适合你,但我们没有足够的信息来提供更好的建议。 在这种情况下,专业程序员可以使用一堆方法,取决于项目的具体情况。
此类问题的良好解决方案是缓存数据。但它有一些不同的需求。您应确保用户发出复杂但相同的请求,并且不经常更改数据。
如果数据不经常更改,那么其他优秀的优化技巧 - making index。
历史部分怎么样,如果用户想要查看积分历史记录,我们应该使用LIMIT 100记录来限制它* * * *或*(性能)
当然你应该。在大多数情况下,用户无法同时看到所有100(200,300)个项目。他们也会查找所有记录(据我所知,他们将在本节中有很多记录)并非每次都有。 即使用户将看到所有记录,无论如何,这将花费一些时间在几秒或几分钟。对单个请求使用限制将随时间分配负载并降低负载峰值。这将提高用户的平均表现。
因此,为了获得性能优势,您应该为大量内容提供部分加载功能。
这应该在每次页面刷新或每个页面更改时运行吗? (如果1000个用户在线,并且每次刷新都应用此SELECT查询,则不会降低服务器速度)
用户的任何活动都会降低服务器的速度,这是不可能解决的问题:)但在这里我们讨论使用不同方法的有效性,以获得所需的功能。 至于我,我不知道“如果1000个用户在线并且每次刷新都应用了这个SELECT查询”意味着什么。这是一个论坛,您可以在其中看到很多具有声誉的用户记录吗?或者它可能只是一个声誉的个人资料页面?或者您可能希望看到1000名在线用户的声誉,而不是离线?
如果我们确实在用户收到一些积分时这样做,那么用户就不可能在同一时间收到两个不同的积分(这是可能的),因为我们不能将自动递增放在总计表中(因为我们可能会错过1个积分,或者如果有解决此问题的解决方案,我不知道这个
您不应该关心事务完整性,因为它是DBMS问题。您应该只在每次声誉更改时将更改带到“reputation_sum”字段。我的意思是 - 只做SQL请求。
如果我们设置一个Cron-Job来经常这样做,那么在cron job刷新总计表之前,用户信用不是最新的
不要使用cron。或者,如果您愿意,也可以仅用于数据实现。