宇宙设计问题

时间:2018-05-06 12:05:43

标签: azure-cosmosdb

我的Cosmos项目有以下顶级简化设计。一些介绍然后跟进一些问题。提前感谢您阅读本文。

我有3个集合:MembershipsDiscountsVouchers

enter image description here

每个集合存储不同的实体。每个集合中的实体不同,但属于同一个分区并具有继承的属性:

  • id - 唯一身份证(宇宙要求......我叮叮当当)
  • upsertDate - 在upsert时更新,因此我可以订购文件(见下文)

每个集合实体都有:

  • code - 分区键
  • type - 集合中不同实体的鉴别器。

有会员,优惠券和折扣的thousdans。例如,当系统收到会员代码时,它可以快速查找文档,因为它是分区键。

如果这样做,它将返回属于同一code的所有实体文档:

SELECT * FROM c WHERE c.code = 'YTR3IO'

如果有人这样做,它会返回属于同一codetype的所有实体文档:

SELECT * FROM c WHERE c.code = 'YTR3IO' and c.type = 10

type通常是可以存储在特定集合中的不同实体的枚举。例如,对于成员资格,类型可以是noticationcommentrequesttransaction

我在每个文档中都有upsertDate的原因,所以我可以这样做:

SELECT * FROM c WHERE c.code = 'YTR3IO' AND c.type = 10 ORDER BY c.upsertDate DESC

对于索引政策,我已将政策覆盖到我认为最好的方式:

DocumentCollection collectionDefinition = new DocumentCollection();
collectionDefinition.Id = _docDbMembershipsCollectionName;
collectionDefinition.PartitionKey.Paths.Add("/code");
collectionDefinition.DefaultTimeToLive = -1;

// If queries are known upfront, index just the properties we need
collectionDefinition.IndexingPolicy.Automatic = true;
collectionDefinition.IndexingPolicy.IndexingMode = IndexingMode.Consistent;
collectionDefinition.IndexingPolicy.IncludedPaths.Clear();

IncludedPath path = new IncludedPath();
path.Path = "/code/?";
path.Indexes.Add(new RangeIndex(DataType.String) { Precision = -1 });
collectionDefinition.IndexingPolicy.IncludedPaths.Add(path);

path = new IncludedPath();
path.Path = "/upsertDate/?";
path.Indexes.Add(new RangeIndex(DataType.String) { Precision = -1 });
collectionDefinition.IndexingPolicy.IncludedPaths.Add(path);

path = new IncludedPath();
path.Path = "/type/?";
path.Indexes.Add(new RangeIndex(DataType.Number) { Precision = -1 });
collectionDefinition.IndexingPolicy.IncludedPaths.Add(path);

collectionDefinition.IndexingPolicy.ExcludedPaths.Clear();
collectionDefinition.IndexingPolicy.ExcludedPaths.Add(new ExcludedPath { Path = "/*" });

DocumentCollection ttlEnabledCollection = await 
_docDbClient.CreateDocumentCollectionAsync(
UriFactory.CreateDatabaseUri(_docDbDatabaseName), collectionDefinition, new RequestOptions { OfferThroughput = 1000 });

问题:

问题1:如果我有upsertDate的查询,则成本会上升:

FeedOptions queryOptions = new FeedOptions { MaxItemCount = 100, PartitionKey = new Microsoft.Azure.Documents.PartitionKey(code.ToUpper()) };

var query =  this._docDbClient.CreateDocumentQuery<MembershipDeviceRegistrationItem>(
                UriFactory.CreateDocumentCollectionUri(_docDbDatabaseName, _docDbMembershipsCollectionName), queryOptions)
                .Where(e => e.CollectionType == MembershipCollectionTypes.DeviceRegistrationItem && e.Code.ToUpper() == code.ToUpper())
                .OrderByDescending(e => e.UpsertDate)
                .AsDocumentQuery();

List<MembershipDeviceRegistrationItem> allDocuments = new List<MembershipDeviceRegistrationItem>();
while (query.HasMoreResults)
{
    var queryResult = await query.ExecuteNextAsync<MembershipDeviceRegistrationItem>();
    cost += queryResult.RequestCharge;
    allDocuments.AddRange(queryResult.ToList());
}

return allDocuments.ToList();

如何使用upsertDate订购我的文档并选择最新的文档?

问题2:如何计算具有聚合的查询的成本?

FeedOptions queryOptions = new FeedOptions { MaxItemCount = 1, PartitionKey = new Microsoft.Azure.Documents.PartitionKey(code.ToUpper()) };

return _docDbClient.CreateDocumentQuery<int>(
            UriFactory.CreateDocumentCollectionUri(_docDbDatabaseName, _docDbMembershipsCollectionName),
            new SqlQuerySpec()
            {
                QueryText = $"SELECT value count(m.id) FROM {_docDbMembershipsCollectionName} m WHERE m.code = @code AND m.collectionType = @type",
                Parameters = new SqlParameterCollection()
                {
                    new SqlParameter("@code", code.ToUpper()),
                    new SqlParameter("@type", MembershipCollectionTypes.DeviceRegistrationItem)
                }
            }, queryOptions).AsEnumerable().First();

问题3:我需要制作一个Omni文档,其中包含有关特定成员资格的所有内容并返回给某个客户,以便他们可以显示其不同的部分。所以我创建了这样的东西(简化):

FeedOptions queryOptions = new FeedOptions { MaxItemCount = -1, PartitionKey = new Microsoft.Azure.Documents.PartitionKey(code.ToUpper()) };
var query = _docDbClient.CreateDocumentQuery<dynamic>(
            UriFactory.CreateDocumentCollectionUri(_docDbDatabaseName, _docDbMembershipsCollectionName),
            new SqlQuerySpec()
            {
                QueryText = $"SELECT * FROM {_docDbMembershipsCollectionName} m WHERE m.code = @code",
                Parameters = new SqlParameterCollection()
                {
                    new SqlParameter("@code", code.ToUpper())
                }
            }, queryOptions).AsDocumentQuery();

List<dynamic> items = new List<dynamic>();
while (query.HasMoreResults)
{
    var queryResult = await query.ExecuteNextAsync();
    cost += queryResult.RequestCharge;
    items.AddRange(queryResult.ToList());
}

var omni = new DigitalMembershipOmni();
foreach (var item in items)
{
    var collectionType = (MembershipCollectionTypes)item.collectionType;
    if (collectionType == MembershipCollectionTypes.Membership)
    {
        omni.Clone((DigitalMembership)item);
    }
    else if (collectionType == MembershipCollectionTypes.RefreshItem)
    {
        omni.RefreshItems.Add((DigitalMembershipRefreshItem)item);
    }
    else if (collectionType == MembershipCollectionTypes.OfferItem)
    {
        omni.OfferItems.Add((DigitalMembershipOfferItem)item);
    }
    else if (collectionType == MembershipCollectionTypes.TrackingTransactionItem)
    {
        omni.TrackingTransactionItems.Add((DigitalMembershipTrackingTransactionItem)item);
    }
    else if (collectionType == MembershipCollectionTypes.BookingTransactionItem)
    {
        omni.BookingTransactionItems.Add((DigitalMembershipBookingTransactionItem)item);
    }
    else if (collectionType == MembershipCollectionTypes.MembershipTransactionItem)
    {
        omni.MembershipTransactionItems.Add((DigitalMembershipTransactionItem)item);
    }
    else if (collectionType == DigitalMembershipCollectionTypes.NotificationItem)
    {
        omni.NotificationItems.Add((DigitalMembershipNotificationItem)item);
    }
    else if (collectionType == DigitalMembershipCollectionTypes.CommentItem)
    {
        omni.CommentItems.Add((DigitalMembershipCommentItem)item);
    }
    else if (collectionType == DigitalMembershipCollectionTypes.ContactUsItem)
    {
        omni.ContactUsItems.Add((DigitalMembershipContactUsItem)item);
    }
    else if (collectionType == DigitalMembershipCollectionTypes.ReferralItem)
    {
        omni.ReferralItems.Add((DigitalMembershipReferralItem)item);
    }
    else if (collectionType == DigitalMembershipCollectionTypes.EcertPinItem)
    {
        omni.EcertPinItems.Add((DigitalMembershipEcertPinItem)item);
    }
    else if (collectionType == DigitalMembershipCollectionTypes.ProfileUpdateItem)
    {
        omni.ProfileUpdateItems.Add((DigitalMembershipProfileUpdateItem)item);
    }
}

return omni;

上述工作....但它的成本很高......而且......它不能很好地扩展......所以如果一个成员会积累更多的数据,成本将会显着上升。我尝试对每种类型执行查询,指定要生成的顶级项目,例如前100个通知...但upsertDate的订单(如上所述)会使查询非常昂贵....因此总成本甚至是最糟糕的。如何以合理的成本和性能完成这样的事情。

问题4:membershipCodevouchers集合中有discounts属性,如何进行JOIN查询以获取属于特定成员资格的凭证耗尽我的RU并杀死我的表现?我认为JOIN查询最终将使用不同的分区扫描整个vouchers集合,并且可能会产生显着的成本和吞吐量问题。我能够使其合理运行的唯一方法是使用表DB将vouchers链接到memberships。 Table DB查询很快......但这给我的小项目增加了一个重要的复杂性。

我很欣赏任何指针。

问候

0 个答案:

没有答案