在 Dynamics 365 Customer Engagement 中检索审计数据的性能问题

Performance issue of retrieving Audit data in Dynamics 365 Customer Engagement

我们有一个从 Dynamics 365 Customer Engagement 检索审计数据的解决方案。自 2019 年 3 月以来,我们 运行 在检索审计数据时遇到了相当多的性能问题或超时或错误消息。请参阅 post 的末尾了解一些异常消息的详细信息。

我们正在使用 RetrieveMultipleRequest 和 fetch XML 检索审计数据的 ID,然后根据这些审计记录 ID 使用 RetrieveAuditDetailsRequest 检索详细信息。请参阅 post 末尾以获取 XML。第一个 运行 将只有 le 条件(小于当前时间戳),随后的 运行s 将具有 gt 和 le 大约 6 小时的时间范围。

我们以前 ID 和详细信息检索的批量大小都是 500,审计检索也很好。在审核详细信息检索期间,我们会收到 TimeoutException(我们现在有 10 分钟的超时时间)或 ObjectDisposedException(请参阅 post 末尾的完整堆栈),即使在某些情况下是少量记录。我们将详细记录检索的批量大小减少到 200,同时将 ID 检索保持为 500,以减少 CRM 环境的负载,这似乎有所帮助。

在一些 运行 我们在 customer Dynamics env 中,对于从 50 到 400 的各种批大小,检索率约为 0.7 record/per 秒。这里的性能很差。

基于内部 CRM,这里是审计 table 结构:

CREATE TABLE [dbo].[AuditBase](
   [AttributeMask] [nvarchar](max) NULL,
   [TransactionId] [uniqueidentifier] NOT NULL,
   [Action] [int] NULL,
   [ObjectId] [uniqueidentifier] NOT NULL,
   [ObjectIdName] [nvarchar](1) NULL,
   [UserId] [uniqueidentifier] NOT NULL,
   [ChangeData] [nvarchar](max) NULL,
   [CreatedOn] [datetime] NOT NULL,
   [Operation] [int] NOT NULL,
   [AuditId] [uniqueidentifier] NOT NULL,
   [CallingUserId] [uniqueidentifier] NULL,
   [ObjectTypeCode] [int] NULL,
   [RegardingObjectId] [uniqueidentifier] NULL,
   [RegardingObjectIdName] [nvarchar](4000) NULL,
   [UserAdditionalInfo] [nvarchar](400) NULL
)
GO
ALTER TABLE [dbo].[AuditBase] ADD  DEFAULT (newsequentialid()) FOR [AuditId]

请注意,根本没有密钥,所有实体的审计数据将存储在同一个 table 中。另外,我们正在检索具有某些 gt 和 le 条件的记录,CRM 备份是否也会按“CreatedOn”对记录进行排序?通过排序,检索肯定会很慢,尽管我不确定它在内部排序的数据量是多少。

任何人都可以对此有所了解吗?

[更新]:我们尝试使用条件较少的 Fetch XML 按照答案中的以下建议,见下文 [Fetch XML 2],也没什么用。现在性能瓶颈不在审计 ID 检索,而是审计细节检索。


获取XML:

    <fetch no-lock='true'>
    <entity name='audit'>
        <filter type='and' >
            <condition attribute='operation' operator='in' >
                <value>2</value>
                <value>3</value>
            </condition>
            <condition attribute='action' operator='not-in' >
                <value>14</value>
                <value>15</value>
                <value>48</value>
                <value>49</value>
                <value>53</value>
                <value>54</value>
                <value>55</value>
                <value>56</value>
                <value>57</value>
                <value>58</value>
                <value>59</value>
                <value>60</value>
                <value>62</value>
                <value>63</value>
                <value>64</value>
                <value>65</value>
                <value>100</value>
                <value>101</value>
                <value>102</value>
                <value>103</value>
                <value>104</value>
                <value>105</value>
                <value>106</value>
                <value>107</value>
                <value>108</value>
                <value>109</value>
                <value>110</value>
                <value>111</value>
                <value>112</value>
                <value>113</value>
            </condition>
            <condition attribute='objecttypecode' operator='eq' value='1' />
            <condition attribute='createdon' operator='gt' value='2019-06-18T10:01:13.8571635Z' />
            <condition attribute='createdon' operator='le' value='2019-06-18T16:00:53.9247159Z' />
        </filter>
    </entity>
</fetch>

获取XML 2:

<fetch no-lock='true'>
  <entity name='audit'>
  <attribute name='auditid' />
  <filter type='and' >
    <condition attribute='objecttypecode' operator='eq' value='1' />
    <condition attribute='createdon' operator='gt' value='2020-01-18T16:01:13.8571635Z' />
    <condition attribute='createdon' operator='le' value='2020-02-13T16:00:53.9247159Z' />
  </filter>
 </entity>
</fetch>

异常 1:

    System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'System.ServiceModel.Security.TransportSecurityProtocol'.
Server stack trace: 
at System.ServiceModel.Channels.CommunicationObject.ThrowIfClosedOrNotOpen()
at System.ServiceModel.Security.TransportSecurityProtocol.VerifyIncomingMessage(Message& message, TimeSpan timeout)
at System.ServiceModel.Security.SecurityProtocol.VerifyIncomingMessage(Message& message, TimeSpan timeout, SecurityProtocolCorrelationState[] correlationStates)
at System.ServiceModel.Channels.SecurityChannelFactory`1.SecurityRequestChannel.ProcessReply(Message reply, SecurityProtocolCorrelationState correlationState, TimeSpan timeout)
at System.ServiceModel.Channels.SecurityChannelFactory`1.SecurityRequestChannel.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]: 
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at Microsoft.Xrm.Sdk.IOrganizationService.Execute(OrganizationRequest request)

异常 2:

    System.TimeoutException: The request channel timed out while waiting for a reply after 00:09:59.9990005. Increase the timeout value passed to the call to Request or increase the SendTimeout value on the Binding. The time allotted to this operation may have been a portion of a longer timeout. ---> System.TimeoutException: The HTTP request to 'https://xxx.dynamics.com/XRMServices/2011/Organization.svc' has exceeded the allotted timeout of 00:10:00. The time allotted to this operation may have been a portion of a longer timeout. ---> System.Net.WebException: The operation has timed out
   at System.Net.HttpWebRequest.GetResponse()
   at System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
   --- End of inner exception stack trace ---
   at System.ServiceModel.Channels.HttpChannelUtilities.ProcessGetResponseWebException(WebException webException, HttpWebRequest request, HttpAbortReason abortReason)
   at System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
   at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
   --- End of inner exception stack trace ---
Server stack trace: 
   at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
   at System.ServiceModel.Channels.SecurityChannelFactory`1.SecurityRequestChannel.Request(Message message, TimeSpan timeout)
   at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
   at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]: 
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
   at Microsoft.Xrm.Sdk.IOrganizationService.Execute(OrganizationRequest request)
   at Microsoft.Xrm.Sdk.Client.OrganizationServiceProxy.ExecuteCore(OrganizationRequest request)
   at Microsoft.Xrm.Sdk.Client.OrganizationServiceProxy.Execute(OrganizationRequest request)

该 FetchXML 中的条件数可能是导致该问题的原因。 我的想法是:

  1. 看看您是否可以寻求 Microsoft 支持来帮助您将 custom indexes 添加到 ObjectTypeCode、Operation、Action 和 CreatedOn,或者可能是此特定查询的 "covering index"。

  2. 大大缩小条件 - 看看它如何仅使用 ObjectTypeCode 和 CreatedOn 条件。然后在内存中进行额外的过滤。是的,您将检索到更多记录,但在 RAM 中过滤速度的提高可能是值得的。

  3. 您可能还想尝试只检索某些列而不是整个实体。一种方法是在第一次通过时仅检索进行过滤所需的列,然后通过这些列进行分页以获取所需的实际记录的 ID。然后可能会翻阅 ID 并发出一次带有 50 个 ID 的 Fetch 以检索这些记录的其余列。

  4. 为了使审计数据集尽可能小,如果您有任何更新帐户记录的进程或应用程序,请确保它们只更新必要的列。如果审计历史记录显示大量未更改的数据被推回相同的字段,您可以更新代码以减少正在更新的字段。

  5. 也许可以查看将审计数据导出到 SQL 的选项。