我仍然在努力解决与CQRS风格架构相关的基本(和解决)问题:
我们如何实现依赖于一组聚合根的业务规则?
以一个预订申请为例.它可以让您预订音乐会的门票,电影的座位或餐厅的桌子.在所有情况下,只有有限数量的"物品"可供出售.
让我们想象一下这个活动或地点非常受欢迎.当销售为新事件或时段开放时,预订开始很快到达 - 可能每秒很多.
在查询方面,我们可以进行大规模扩展,并将预留放在队列中以由自治组件异步处理.首先,当我们从队列中取出预约命令时,我们将接受它们,但在某个时间我们将不得不开始拒绝其余的.
我们怎么知道什么时候达到极限?
对于每个预约命令,我们必须查询某种商店以确定我们是否可以容纳请求.这意味着我们需要知道当时已收到多少预订.
但是,如果域存储是非关系数据存储,例如Windows Azure表存储,我们就不能很好地做到 SELECT COUNT(*) FROM ...
一种选择是保持一个单独的聚合根,它只是跟踪当前的计数,如下所示:
第二个聚合根将是第一个聚合根的非规范化聚合,但是当底层数据存储不支持事务时,它们很可能在高容量场景中不同步(这是我们正在尝试的首先解决).
一种可能的解决方案是序列化预留命令的处理,以便一次只处理一个,但这违背了我们的可扩展性(和冗余)目标.
这种情况让我想起了标准的"缺货"情景,但不同之处在于我们不能很好地将预订放在后面.一旦活动售罄,它就卖光了,所以我看不出补偿行动会是什么.
我们如何处理这种情况?
我正在建立一个有愿望清单的网站.我想将愿望清单存储在天蓝色的表存储中,但也希望用户能够在查看时对其愿望清单进行排序,有多种不同的方式 - 添加日期,添加日期,项目名称等.我还想实现分页,我相信我可以通过使用延续令牌来实现.
据我了解,"order by"未实现,结果从表存储返回的顺序基于分区键和行键.因此,如果我想实现我描述的分页和排序,通过使用不同的分区键/行键多次存储愿望清单来实现这一点是最好的方法吗?
在这个简单的情况下,愿望列表可能不会那么大,我实际上可以限制可以出现在列表中的最大项目数,然后摆脱内存中的分页和排序.但是,我有更复杂的情况,我还需要实现分页和排序.
我在我的应用程序中有一个常见的现象,我创建一个查询来获取所有实体,其中partitionkey是常量但rowkey应该在词法范围内(例如,只有以某些前缀开头的行):
//query to get all entities in partition "KnownPartition" where RowKey starts with "Prefix_"
CloudTableQuery<MyEntity> query =
(from e in tableServiceContext.CreateQuery<MyEntity>(tableName)
where e.PartitionKey == "KnownPartition"
&& e.RowKey.CompareTo("Prefix_") > 0
&& e.RowKey.CompareTo("Prefix`") <= 0 // ` is '_' + 1
select e).AsTableServiceQuery();
Run Code Online (Sandbox Code Playgroud)
我必须使用CompareTo,因为在这种查询中不支持诸如StartsWith之类的字符串函数.这有效,但条件难以阅读和重复.因此,我不想用这种难以阅读的条件编写大量查询,而是想制作一个"内联"它的函数:
public static Boolean HasPrefix(this String rowKey, String prefix)
{
return rowKey.CompareTo(prefix + '_') > 0 && rowKey.CompareTo(prefix + '`') <= 0;
}
CloudTableQuery<MyEntity> query =
(from e in tableServiceContext.CreateQuery<MyEntity>(tableName)
where e.PartitionKey == "KnownPartition" && e.RowKey.HasPrefix("Prefix")
select e).AsTableServiceQuery();
Run Code Online (Sandbox Code Playgroud)
但是当我运行它时,我从Azure获得一个关于我的功能不受支持的例外.有没有办法写这个以便它被支持?毕竟,我使用与查询完全相同的条件,只是包含在函数中...
我想用一个简单的TableQuery从我的Azure表中获取前n行.但是使用下面的代码,无论我对Take的限制如何,都会获取所有行.
我究竟做错了什么?
int entryLimit = 5;
var table = GetFromHelperFunc();
TableQuery<MyEntity> query = new TableQuery<MyEntity>()
.Where(TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, "MyPK"))
.Take(entryLimit);
List<FeedEntry> entryList = new List<FeedEntry>();
TableQuerySegment<FeedEntry> currentSegment = null;
while (currentSegment == null || currentSegment.ContinuationToken != null)
{
currentSegment = table.ExecuteQuerySegmented(query, this.EntryResolver, currentSegment != null ? currentSegment.ContinuationToken : null);
entryList.AddRange(currentSegment.Results);
}
Trace.WriteLine(entryList.Count) // <-- Why does this exceed my limit?
Run Code Online (Sandbox Code Playgroud) 我正在针对ATS运行性能测试,并且在针对同一个表/存储帐户使用多个虚拟机时,它的行为有点奇怪.
整个管道是非阻塞的(等待/异步)并使用TPL进行并发和并行执行.
首先,它非常奇怪,通过这种设置,我只得到大约1200次插入.这是在L VM盒上运行的,即4核+ 800mbps.
我正在插入具有独特PK和唯一RK的100.000行,这应该利用最终分布.
更确定的行为如下.
当我运行1个VM时,每秒大约有1200个插入.当我运行3个VM时,每秒插入一次大约730个.
阅读他们指定目标的博客文章非常幽默. https://azure.microsoft.com/en-gb/blog/windows-azures-flat-network-storage-and-2012-scalability-targets/
单表分区 - 表分区是表中具有相同分区键值的所有实体,通常表具有许多分区.单个表分区的吞吐量目标是:
每秒最多2,000个实体
请注意,这适用于单个分区,而不是单个表.因此,具有良好分区的表可以处理多达20,000个实体/秒,这是上述的整体帐户目标.
我该怎么做才能够每秒使用20k,如何每个VM执行超过1,2k?
-
更新:
我现在还尝试为每个单独的节点使用3个存储帐户,并且仍然获得性能/限制行为.我无法找到合乎逻辑的理由.
-
更新2:
我已经进一步优化了代码,现在我可以执行大约1550.
-
更新3:
我现在也曾在美国西部尝试过.那里的表现更糟糕.降低约33%.
-
更新4:
我尝试从XL机器执行代码.这是8个内核而不是4个内存和带宽的两倍,并且性能提高了2%,所以很明显这个问题不在我身边..
在文档中,它声明Azure表存储分区的最低速度为500次/秒.
如果我的数据分区正确,那么每个分区上的并行操作是否会相互影响?
例如,如果我不得不在分区A(最多500个实体/秒)上进行昂贵的全表扫描,那么分区B上发生的任何操作的性能是否会受到影响?
存储帐户的限制为每秒5000次操作.这是否意味着我可以在它们开始影响彼此性能之前最多可以输出10个分区?
执行以下代码时出现此错误:
var insert = new TableBatchOperation();
foreach (var entity in entities)
{
insert.Insert(entity);
}
cloudTable.ExecuteBatch(insert);
Run Code Online (Sandbox Code Playgroud)
实体集合包含512个元素.Azure SDK通过StorageException:
"Unexpected response code for operation : 99"
Run Code Online (Sandbox Code Playgroud)
这个错误意味着什么,我该如何解决?
InsertOrReplace在Azure表存储上执行操作时,有没有办法确定实体是刚刚插入还是发生了替换?
TableOperation insertOperation = TableOperation.InsertOrReplace(entity);
TableResult result = table.Execute(insertOperation);
Run Code Online (Sandbox Code Playgroud)
TableResult 似乎没有表明这一点.
我是Azure的新手!目的是根据存储在RowKey中的时间戳返回行.由于每个查询都有交易成本,我希望在保持性能的同时最小化事务/查询的数量
这些是建议的分区和行键:
传奇:
我想从单个查询返回行和任何>或<DateOfMessage_MessageId的子项
这可以通过我提出的PartitionKeys和RowKeys来完成吗?
ie ..(在伪代码中)
var results = ctx.PartitionKey.StartsWith(TextCache_AccountId)
&& ctx.RowKey > (TimeStamp)_MessageId
Run Code Online (Sandbox Code Playgroud)
其次,如果我有多个帐户,并且只想返回前10个帐户,可以通过单个查询完成
ie ..(在伪代码中)
var results = (
(
ctx.PartitionKey.StartsWith(TextCache_(AccountId1)) &&
&& ctx.RowKey > (TimeStamp1)_MessageId1 )
)
||
(
ctx.PartitionKey.StartsWith(TextCache_(AccountId2)) &&
&& ctx.RowKey > (TimeStamp2)_MessageId2 )
) ...
)
.Take(10)
Run Code Online (Sandbox Code Playgroud) 我有一些软件可以在很长一段时间内收集数据,每秒大约200个读数.它使用SQL数据库.我希望使用Azure将大量旧的"归档"数据移动到.
该软件使用多租户类型的体系结构,因此我计划为每个租户使用一个Azure表.每个租户可能正在监控10-20个不同的指标,因此我计划使用指标ID(int)作为分区键.
由于每个指标每分钟只有一个读数(最大值),我计划使用DateTime.Ticks.ToString("d19")作为我的RowKey.
我对这将如何扩展缺乏一点了解; 所以希望有人能够清除这一点:
为了提高性能,Azure将/可能会通过partitionkey拆分我的表,以保持良好和快速.在这种情况下,这将导致每个度量标准一个分区.
但是,我的rowkey可能代表大约5年的数据,所以我估计大约250万行.
Azure是否足够聪明,然后根据rowkey进行拆分,还是我在设计未来的瓶颈?我知道通常不会过早地进行优化,但是像Azure这样的东西看起来并不像平时那么明智!
寻找Azure专家,让我知道我是否在正确的位置,或者我是否应该将我的数据划分为更多的表.