【问题标题】:Do I need a second table for this database logic?这个数据库逻辑需要第二张表吗?
【发布时间】:2010-12-31 18:43:32
【问题描述】:

首先,这个 DB 问题可能有点与 DB 无关,但我使用的是 Sql Server 2008,如果它有专门的解决方案来解决这个问题,但如果你不是 MS Sql Server 人员,请继续阅读..请:)


好的,我读入了一个包含游戏数据的日志文件。例如。当玩家连接、断开连接、做事等时。没什么太难的。效果很好。

现在,两种日志文件条目类型是

  • 新连接
  • 丢失连接

我试图跟踪的是当前连接到游戏的玩家。 所以我最初的想法是创建第二个表,其中包含每个玩家最近的新连接。当玩家断开/失去连接时,我会从第二个表中删除此条目。

例如。

Table 1: LogEntries
LogEntryId INT PK NOT NULL
EntryTypeId TINYINT NOT NULL
PlayerId INT NOT NULL
....

Table 2: ConnectedPlayers
LogEntryId INT FK (back to LogEntries table) NOT NULL

然后,我想我可以使用触发器将其插入到 ConnectedPlayers 表的 cache 数据中。别忘了,如果是触发器,它需要handle multiple records,更新和删除。

但我不确定这是否是最好的方法。比如,我可以有一个索引视图吗?

我很想知道人们对此的看法。

哦,还有一件事:为了简单起见,让我们假设当玩家断开连接/延迟/调制解调器死机/等时,应用程序足够聪明,可以知道这一点并将其记录为 LostConnection 条目。不会有幻影用户报告为已连接,当他们真的意外断开连接时等等。


更新:

我在想也许我可以改用view? (如果我愿意,我也可以索引这个视图:))通过Partitioning 我的结果,我可以获得每个玩家的最新事件类型......其中事件是NewConnectionLostConnection。然后只抓取那些最近的 NewConnection .. 这意味着它们已连接。没有第二个表/触发器/额外插入 .NET 代码/任何需要...

例如..

SELECT LogEntryId, EntryTypeId, PlayerId
FROM
    (SELECT LogEntryId, EntryTypeId, PlayerId
         RANK() OVER (PARTITION BY PlayerId ORDER BY LogEntryId DESC) AS MostRecentRank
     FROM LogEntries
     WHERE (EntryTypeId = 2 -- NewConnection
            OR EntryTypeId = 4 -- LostConnection)
     ) SubQuery
WHERE MostRecentRank = 1

听起来/看起来怎么样?

【问题讨论】:

  • 或者,您也可以在 LogEntries 表中设置一个 is_current 标志,当您断开连接时更新该标志。
  • 嗯。我不明白这有什么帮助,罗兰。 LogEntries 表是文件中记录的数据的简单一对一映射。
  • 这似乎不是您想要保留的那种信息,而是只保留在内存中,因为如果服务器重新启动,您无论如何都会失去所有连接。你坚持的理由是什么?如果您只想记录谁连接的日志,那没关系,但您为什么要从日志中读取以找出现在谁连接了?
  • 因为我没有进入游戏的权限。只有实时的流式日志:((但是是的,我同意你的观点:P)
  • @Mark Byers:当服务器重新启动(或应用程序池重新启动)时,连接丢失并不总是正确的。将会话持久化到数据库以防止这种情况是很常见的。

标签: sql tsql view triggers


【解决方案1】:

您不需要第二个表,但确实需要一个日期列,我认为这是您的日志数据的一部分。我会规范化数据并避免过早优化的诱惑。确保索引关键列,主要是查询时的 LogEntryDate 和 PlayerId 列。

然后,使用标准聚合查询确定每个用户的最新日志条目,然后过滤掉未连接的日志条目。您可以通过仅从过去 24 小时(或上周或对您的应用程序有意义的任何内容)的日志条目中进行选择来进一步优化这一点。

select l.* 
from ( 
    select PlayerId, max(LogEntryDate) as MaxLogEntryDate
    from LogEntries
    where EntryTypeId in (2,4) 
        and LogEntryDate > GetDate() - 7 --only look at the last week, as connections older than that have timed out
    group by PlayerId
) lm
inner join LogEntries l on lm.PlayerId = l.PlayerId and lm.MaxLogEntryDate = l.LogEntryDate
where l.EntryTypeId = 2 --new connections only

如果您发现仍无法从查询中获得所需的速度,然后查看优化策略。您似乎不愿意在应用程序层中缓存,因此您的索引视图建议可行。您可以使用上面的查询作为基础来创建一个包含布尔值 IsConnected 列的 Player 视图。

注意:如果您没有收到每个日志条目的日期,但 LogEntryId 是由游戏生成的,那么它应该可以代替日期。如果您在插入时生成 LogEntryId,我会提醒您不要依赖它,因为它只需要一次无序导入即可丢弃所有数据。

【讨论】:

  • 但是,即使我确实有这个聚合查询,我仍然只能在一段时间内获得数据的快照视图。不是准确/准确的数据。如果我没记错的话,这个查询不只是向我们展示了最近一周内每个玩家的最新连接吗?如果他们在这个新连接之后断开连接怎么办?我的RANK() OVER (PARTITION ... ) 看起来更准确吗?
  • 它显示了最近一周的数据,但这只是因为我认为它们可能会超时。如果不是这种情况,请删除该优化。否则,查询会显示最新的准确结果。不知道你说的快照是什么意思...
  • 为了澄清,查询获取每个用户的最新日志条目(在上周,但您可以删除该 where 子句)。然后,它会过滤掉那些不是新连接的连接,留下当前连接用户的日志条目。
  • 已编辑查询以将 EntryTypeId 添加到 where 子句,否则会错过一些用户...
  • 啊,所以 MAX(..) 基本上是在说,抓住我最近的日志条目结果(即 1 条记录),它是 2 或 4 .. 对于时间段.. . 正确吗?
【解决方案2】:

根据原始表 LogEntries 的大小,这似乎有点过头了。

触发器必须随着原始表的每次更改而更新,如果使用正确的索引,当您需要数据时,一个简单的查询可以为您提供这些结果。

因此我会反对辅助表的选择。

【讨论】:

  • 这也是我的担心。这张桌子很大......这就是我问这个问题的原因:P 如果不是辅助桌子......那么我还有什么其他选择?这可以在单个存储过程中处理吗?
  • 你说的是什么意思。如果表被正确索引,这应该很容易在查询中处理。
  • 正确回复:大。为了简单起见,假设有 1000 万行。
【解决方案3】:

我会创建一个 is_connected 标志,login_time,仅此而已。 您可以使用简单的 MySQL 查询甚至每 10/60 秒检查一次,并将数据缓存在文件中。

其中is_connected=1,按login_time限制10/20/100排序...

第二张桌子似乎太多了,而且毫无用处。可以对文件进行额外的缓存(如果需要,在一个巨大的数据库中......)。

【讨论】:

  • 文件?呃……这让我害怕,伙计。对不起。我希望将其保存在数据库中。
【解决方案4】:

我个人会使用一个视图来获取每个玩家的最新 NewConnection 或 LostConnection(以较新者为准),这意味着您需要日志中的某种日期时间戳或不断增加的 id,并且然后过滤进一步丢弃所有 LostConnection 条目。这将使所有玩家都拥有 NewConnection 而没有更新的 LostConnection,因此他们已连接。

使用这种方法可能会遇到的问题是日志表可能很大。我可能会尝试使用时间戳列上的索引或您用来确定“最新”条目的任何列进行性能测试。

【讨论】:

  • 如果你要做一个视图,你能给我一些sql代码吗?如果之前没有 LostConnection,我不确定如何获取最新的 NewConnection。
【解决方案5】:

据我了解,你真正需要的是

玩家(ID) 游戏 (ID) 连接(PlayerID、GameID、LastActivity 日期时间) ..

@interestingTime 比当前时间早一些时间

select PlayerID, GameID
from Connection
where LastActivity > @interestingTime

为您提供所有当前连接的玩家。

select PlayerID, GameID
from Connection
where LastActivity <= @interestingTime

让您失去连接。

【讨论】:

  • 这有点类似于我建议的第二张桌子。那么在这种情况下,如何将数据放入Connection 表中?扳机?另外,您的代码是否考虑了该时间段内的多个连接/重新连接/断开连接?
猜你喜欢
  • 2021-06-28
  • 2017-04-02
  • 1970-01-01
  • 1970-01-01
  • 2023-04-11
  • 2011-01-06
  • 2015-10-02
  • 1970-01-01
  • 2023-03-29
相关资源
最近更新 更多