【问题标题】:Simulating server-side group and sort in Azure table storage在 Azure 表存储中模拟服务器端组和排序
【发布时间】:2015-09-16 16:30:53
【问题描述】:

我有一个表,每当用户查看特定资源时,我都会向其中添加记录。关键字段是

  • 用户名
  • 资源
  • 查看日期

在我的应用程序的历史记录页面上,我想显示用户最近查看的资源的一组数字(例如,前 5 个),但我想按资源分组,这样如果有一些被多次查看,则只有显示每个最新的。

要清楚,如果原始数据看起来像这样:

UserA | ResourceA | Jan 1
UserA | ResourceA | Jan 2
UserA | ResourceB | Jan 3
UserA | ResourceA | Jan 4
...

...只有底部的两条记录会出现在历史页面中。

我知道您可以通过使用从 PartitionKey 或 RowKey 字段中的日期派生的字符串来获得服务器端的时间排序。

我还看到,您可以通过使用 Username 和 Resource 作为您的 PartitionKey 和 RowKey 字段,然后使用 Insert-or-update 来维护一个表,您可以在该表中保存最新值的指针每个组合。但是,那些 记录不会按时间顺序排序。

有没有什么方法可以设计一组表,这样我就可以获得所需的数据,而无需检索大量额外的实体并在客户端进行排序?如果需要的话,我愿意对设计进行详细说明。提前致谢!

【问题讨论】:

  • 请分享更多关于您的设计的细节。另请描述您要获取哪些数据。
  • @GauravMantri 感谢您的回复。正如我上面所说,该表包含三个重要字段。您可以认为 Resource 字段只是一个字符串标识符,因此我们有 Username(字符串)、ResourceName(字符串)和 DateViewed(DateTimeOffset)。我想获取按资源名称分组的最近访问的前 n 个资源名称的列表,以及最近访问的时间。因此,如果该表有 200 条资源 A 记录和 200 条资源 B 记录(在许多不同时间查看),我将获取资源 A 和 B 的最新行。在 SQL 中很简单,但在 Azure 中很棘手!

标签: sorting azure grouping azure-table-storage


【解决方案1】:

首先,我强烈建议您阅读存储团队的这篇出色的 Azure Storage Table Design Guide: Designing Scalable and Performant Tables 文档。

是的,我同意 Azure 表存储有点棘手,但它是可行的 :)。

您需要做的是保留相同数据的多个副本。每个副本都有不同的用途。

考虑到您想要获取资源 A 和 B 的最新行的场景,您的实体结构如下所示:

  • PartitionKey:日期/时间(以 Ticks 为单位)反转,即 DateTime.MaxValue.Ticks - LastAccessedDateTime.Ticks。需要反向勾选才能使最近的条目显示在表格顶部。
  • RowKey:资源名称。
  • AccessDate:表示上次访问日期/时间。
  • User:访问该资源的用户名。

因此,当您只想找出最近使用的资源时,您可以从顶部开始获取记录。

简而言之,您的数据存储方法应主要取决于您希望如何获取数据。这甚至意味着您必须多次保存相同的数据。

更新

如下面的 cmets 所述,表服务不直接支持Server Side Grouping。这是您需要自己做的事情。您可以做的是创建一个单独的表来存储访问计数。在访问资源时,您基本上要么在该表中插入一条新记录,要么更新该表中该资源的计数。

假设您总是对找出某个日期/时间范围内的资源访问计数感兴趣,您的实体结构如下所示:

  • PartitionKey:日期/时间(以刻度为单位)。精度取决于您的报告要求。例如,如果您想按天维护访问计数,那么您的精度就是一天。
  • RowKey:资源名称。
  • AccessCount:当资源被访问时,该字段会不断更新。
  • LastAccessDateTime:此字段将表示上次访问资源的时间。

为了更新访问计数,我建议您使用后台进程。基本上在这种方法中,当访问资源时,您在队列中添加一条消息。此消息将包含资源名称和上次访问资源的日期/时间。然后让后台进程轮询此队列并获取消息。收到消息后,您首先获取该资源的当前计数和上次访问日期/时间。如果没有找到记录,您只需在此表中插入一条记录,计数为 1。如果找到记录,则将表中的日期/时间与消息中发送的日期/时间进行比较。如果表中的日期/时间小于消息中发送的日期/时间,则更新计数(将其增加 1)和上次访问日期/时间。如果表格中的日期/时间大于消息中发送的日期/时间,您只需更新计数。

现在要查找一段时间内访问次数最多的资源,您只需查询此表。假设资源数量有限(例如 100 个),您可以通过至少 1 个请求从表中获取此信息。由于您正在处理少量数据,您可以简单地在客户端下载这些数据并以您认为合适的方式订购它。但是,要查看特定资源的访问详细信息,您必须获取详细数据(一次 1000 个实体)。

【讨论】:

  • 感谢您的回复。我已经阅读了该文档,并且我目前正在这样存储我的数据。问题是,如果没有分组,就无法知道需要检索多少行。例如,用户可能访问过资源 A 一次或 1000 次。为了获得 N 个资源的最近访问记录,您可能需要前 N 行,或者您可能需要一些任意更高的数字——可以是 N+1,也可以是 N+100,000。唯一的方法真的是取一些行,检查它们,然后再取一些,等等吗?看起来效率太低了。
  • 你想知道一个资源被使用了多少次吗?
  • 对不起,我想我已经解释得很好了。你在看我的帖子吗?不,我不希望访问资源的次数。正如我所说,我想要最近访问的 N 个资源,按资源分组(这样就不会有重复的资源名称)。使用您的方法,无法知道应该获取多少行才能获取此数据。我不确定如何才能更清楚地解释这一点。
  • 对不起,我的错 :(。所以 Azure 表没有直接的服务器端分组。您可以做的一件事是单独保存访问计数(可能在单独的表中)并作为当访问资源时,您可以更新那里的计数。当您需要获取资源时,您可以先从该表中找到计数,然后从包含详细数据的表中获取那么多行。
  • 感谢您的想法。我没有想到这一点。感觉它可能在正确的轨道上,但它仍然没有解决问题。考虑 10 个资源,每个资源都以某种未知顺序访问了 100 次。我们的目标是检索最近访问的 n 个资源(比如说 5 个)和最后一次访问的日期。如果我们查阅您提到的表,将正确得知完整访问表中有 1000 个条目。但这并不能节省我们从一开始就下载整个主表的任何努力。也许现在没有好的解决方案。
【解决方案2】:

您的部分大脑可能仍会不知不觉地陷入关系表设计范式中,我自己仍在处理这个问题。

与其将表存储视为数据库表(具有与之相伴的“查询能力”),不如尝试用更简单(愚蠢)的术语对其进行可视化。

我现在正在处理的一个设计问题是存储金融交易数据,我想知道这些交易的总金额是多少。因为 Azure 表存储(还没有?)提供聚合函数,所以我不能简单地使用 .Sum()。为了解决这个问题,我将:

  • 在我将应用中的交易值传递给 azure 之前,求和它们。
  • 然后我会将总和的结果作为单独的一条信息传递到 Azure,称为 RunningTotal
  • 稍后我可以只返回RunningTotal 而不是拉下所有交易,并且我可以通过每次获得新交易时增加RunningTotal 的值来重复该过程。

这当然存在风险,但该应用程序是个人应用程序,因此风险级别较低且易于管理,至少作为概念验证。

也许您可以使用类似的方法来设计您的系统:提前计算有用的值。我几乎会将表存储用作长期缓存而不是数据库。

【讨论】:

    猜你喜欢
    • 2016-03-04
    • 2013-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-20
    相关资源
    最近更新 更多