我们最近更新了我们的日志记录以使用Azure表存储,由于它在行和分区查询时的低成本和高性能非常适合此目的。
我们正在尝试遵循文档Designing a Scalable Partitioning Strategy for Azure Table Storage中给出的指南。由于我们正在为此表格进行大量插入(并且希望随着我们的扩展而增加数量),我们需要确保不会达到我们的限制,从而导致日志丢失。我们将设计结构如下:
我们每个环境都有一个Azure存储帐户(DEV,TEST,PROD)。
我们每个产品都有一张表。
我们正在为行键使用TicksReversed + GUID,以便我们可以 查询某些时间之间的结果块与高 性能。
我们最初选择使用Logger对表进行分区,这对我们来说 是产品的广泛领域,如API,应用程序,性能 和缓存。但是,由于我们的分区数量很少 担心这导致所谓的“热门”分区很多 在给定时间段内在一个分区上执行插入。所以 我们改为上下文分区(对我们来说,类名或API 资源)。
然而,在实践中我们发现这不太理想,因为当我们一目了然地查看我们的日志时,我们希望它们按时间顺序出现。我们最终会得到按上下文分组的结果块,如果我们想按时间排序,我们必须得到所有分区。
我们有一些想法
使用分区键的时间块(比如1小时)按时间排序(导致热分区1小时)
使用一些随机的GUID进行分区键尝试分发日志(我们无法快速查询上下文等功能)。
由于这是Azure表存储的常见应用程序,因此必须有某种标准过程。 分区用于存储日志的Azure表的最佳做法是什么?
使用廉价的Azure存储(表存储似乎是明显的选择)
快速,可扩展的写入
丢失日志的可能性很小(即,超过Azure表存储中每秒2000个实体的分区写入速率)。
按日期排序,最近一次排序。
如果可能,可以对查询有用的内容进行分区(例如产品区域)。
答案 0 :(得分:7)
根据我的经验,我遇到了类似的情况:
每当在azure存储表上触发查询时,如果未提供正确的分区键,它将执行全表扫描。换句话说,存储表在分区键上编入索引,正确分区数据是获得快速结果的关键。
那就是说,现在你必须考虑你会在桌面上发出什么样的疑问。例如,在一段时间内发生的日志,对于产品等。
一种方法是使用高达小时精度的反向刻度,而不是使用精确刻度作为分区键的一部分。这样,可以根据此分区键查询一小时的数据。根据落入每个分区的行数,您可以将精度更改为一天。此外,将相关数据存储在一起是明智的,这意味着每个产品的数据将转到不同的表。这样,您可以减少每个分区中的分区数和行数。
基本上,确保您事先知道分区键(精确或范围)并针对此类特定分区键触发查询以更快地获得结果。
要加快写入表格,您可以使用批量操作。但要谨慎,好像批处理中的一个实体未能完成整批操作失败。正确的重试和错误检查可以为您节省时间。
同时,您可以使用blob存储来存储大量相关数据。这个想法是将一大块相关的序列化数据存储为一个blob。您可以点击一个这样的blob来获取其中的所有数据,并在客户端进行进一步的预测。例如,产品的一小时数据将转到blob,您可以设计特定的blob前缀命名模式,并在需要时点击确切的blob。这将有助于您快速获取数据,而不是为每个查询执行表扫描。
我使用blob方法并且使用它几年没有麻烦。我将我的集合转换为IList<IDictionary<string,string>>
并使用二进制序列化和Gzip存储每个blob。我使用基于Reflection.Emmit的辅助方法来快速访问实体属性,因此序列化和反序列化不会对CPU和内存产生影响。
在Blob中存储数据可以帮助我以更少的价格存储更多内容并更快地获取数据。
答案 1 :(得分:3)
有一个非常普遍的技巧可以避免写入时的热点,同时增加读取成本。
定义N个分区(如10个左右)。将一行写入随机分区时。分区可以按时间内部排序。
阅读时,您需要从所有N个分区读取(可能按时间过滤和排序)并合并查询结果。
这会将写入可扩展性提高N倍,并且会使查询成本增加相同的往返次数和查询次数。
此外,您可以考虑将日志存储在其他位置。 Azure产品的非常严格的人为限制会导致您无法获得的人工成本。
选择N 更高比达到每个帐户限制每秒20,000次操作所需,以便不太可能出现随机发生的热点。选择N是最低限度所需的两倍就足够了。
答案 2 :(得分:2)
如果我正确地阅读了这个问题,以下是解决方案的限制:
已经提出了几个很好的解决方案,但我认为没有一个完美地满足所有约束条件的答案。
似乎最接近满足您的约束的解决方案由usr提供。将您的产品区域分区划分为N,但不要使用GUID,只需使用数字(ProductArea-5)。使用GUID使查询问题变得更加困难。如果使用数字,则可以在单个查询中查询产品区域的所有分区,甚至可以并行查询。然后继续对RowKey使用TicksReversed + GUID。
单个查询:PartitionKey ge&#39; ProductArea&#39;和PartitionKey le&#39; ProductArea-~&#39;和RowKey ge&#39; StartDateTimeReverseTicks&#39;和RowKey le&#39; EndDateTimeReverseTicks&#39;
并行查询:PartitionKey ge&#39; ProductArea-1&#39;和RowKey ge&#39; StartDateTimeReverseTicks&#39;和RowKey le&#39; EndDateTimeReverseTicks&#39; ... PartitionKey ge&#39; ProductArea-N&#39;和RowKey ge&#39; StartDateTimeReverseTicks&#39;和RowKey le&#39; EndDateTimeReverseTicks&#39;
此解决方案并不满足按时间自动排序,但您可以通过RowKey进行客户端排序,以便按顺序查看它们。如果必须对客户端进行排序对你没问题,那么这个解决方案应该能够满足其余的约束条件。
答案 3 :(得分:1)
对你的问题不是一个具体的答案,但这是我的一些想法:
您真正需要考虑的是如何基于此查询数据并设计数据存储/分区策略(请记住分区策略指南)。例如,
可能的解决方案
一种可能的解决方案是存储相同数据的多个副本并相应地使用这些副本。由于存储便宜,您可以保存相同数据的两个副本。在第一个副本中你可以有PK =日期/时间和RK =你决定的任何东西,在第二个副本中你可以有PK = Logger和RK = TicksReversed + GUID。然后,当您想要获取所有日志而不管记录器时,您只需查询第一个副本(PK =日期/时间),如果您想查询特定记录器类型的日志,您可以简单地查询第二个副本(PK = Logger) ,RK&gt; =日期/时间开始&amp; RK&lt; =日期/时间结束)。
您可能还会发现此链接很有用:http://azure.microsoft.com/en-us/documentation/articles/storage-table-design-guide/