摘要: 本人微信和易信公眾號: 微軟動態CRM專家羅勇 ,回復263或者20170828可方便獲取本文,同時可以在第一間得到我發布的最新的博文信息,follow me!我的網站是 www.luoyong.me 。
項目升級到Dynamics 365以后,過了些時間,有的賬號反饋使用很慢,甚至同一個地方同一台電腦用不同的賬號特別是進行數據修改的時候會發現有明顯的速度差別,Why?
初步的表象發現是POST 到這個請求 /AppWebServices/InlineEditWebService.asmx 比較慢,這個是系統標准的東西,就算是有問題,我們能做的可能也有限。奇怪的是我們使用同樣業務部門同樣角色的賬號處理類似單據的時候並不能重現此問題,而且通過SQL Profiler抓取的SQL執行語句也沒有發現執行時間很長的SQL。
有問題就有解決問題的方法,大神們日夜分析,發現卡頓的主要原因是執行部分SQL后會卡頓,等待然后再執行,這樣導致的總體時間很長,整個過程甚至能超過20秒。再次詳細分析,發現每次這個卡頓都會又一次查詢 BusinessProcessflowInstanceBase 這個表,奇怪啊,我們並沒有使用業務流程。后來微軟技術支持中心向更加廣泛的范圍求助,終於找到答案,原來是Dynamics 365開始,CRM中的一條記錄可能有多個活動業務流程,每個人根據其權限等顯示的業務流程又不盡相同。所以會查詢這個表,當然在查詢這個表之前會先查詢這條記錄相關的MRU(Most Recently Used List,最近訪問列表),但是如果這個MRU非常大,就會非常耗時導致等待。
那么這么MRU怎么看?當然你可以通過SQL直接來看,這個MUR跟用戶和實體的Typecode有關,下面是查看某個用戶的所有MRU:
1 select ueus.RecentlyViewedXml, 2 len(ueus.RecentlyViewedXml) as RecentlyViewedXmlLength, 3 sysuser.FullName, 4 entity.LogicalName 5 from UserEntityUISettings ueus 6 left join SystemUserBase sysuser on ueus.OwnerId = sysuser.SystemUserId 7 left join entity on ueus.ObjectTypeCode = entity.ObjectTypeCode
我看到我這個賬號經常使用ly_test這個實體,發現其RecentlyViewedXml大小也有 8832 字節,如果你用的狠的話,這個數字可能會超過1Mb。這個里面的內容我簡化下如下:
1 <RecentlyViewedEntityData etc="10007"> 2 <RecentlyViewedItem> 3 <Type>0</Type> 4 <ObjectId>{5DA888D6-06A3-E611-816B-000D3A80C8B8}</ObjectId> 5 <EntityTypeCode>10007</EntityTypeCode> 6 <DisplayName>羅勇測試</DisplayName> 7 <Title>批量操作創建的羅勇測試記錄</Title> 8 <Action></Action> 9 <IconPath></IconPath> 10 <PinStatus>false</PinStatus> 11 <ProcessInstanceId></ProcessInstanceId> 12 <ProcessId></ProcessId> 13 <LastAccessed>11/05/2016 03:20:58</LastAccessed> 14 </RecentlyViewedItem> 15 </RecentlyViewedEntityData>
怎么解決?當然你可以通過組織服務來清除這個字段的值,也可以直接用SQL來處理,比如將長度大於1000的清除:
1 update UserEntityUISettings 2 set RecentlyViewedXml=null 3 where len(RecentlyViewedXml)>=1000
用SQL來清除這個MRU有個弊端就是需要重啟IIS,因為它在服務器端有緩存。
當然也可以寫程序來清除,目前沒有看到界面上提供手動清除MRU的地方:
1 Console.WriteLine("本程序用於清理你輸入賬號的最近訪問記錄。"); 2 string userName = string.Empty; 3 string passWord = string.Empty; 4 Console.WriteLine("請輸入登錄的用戶名,輸入完畢后按回車鍵確認:"); 5 userName = Console.ReadLine().ToString().Trim(); 6 Console.WriteLine("請輸入登錄的密碼,輸入完畢后按回車鍵確認:"); 7 while (true) 8 { 9 ConsoleKeyInfo ck = Console.ReadKey(true); 10 if (ck.Key != ConsoleKey.Enter) 11 { 12 if (ck.Key != ConsoleKey.Backspace) 13 { 14 passWord += ck.KeyChar.ToString(); 15 Console.Write("*"); 16 } 17 else 18 { 19 Console.Write("\b \b"); 20 } 21 } 22 else 23 { 24 break; 25 } 26 } 27 ClientCredentials cc = new ClientCredentials(); 28 cc.UserName.UserName = userName; 29 cc.UserName.Password = passWord; 30 OrganizationServiceProxy orgSvc = new OrganizationServiceProxy(new Uri("https://demo.luoyong.me/XRMServices/2011/Organization.svc"), 31 null, cc, null); 32 WhoAmIRequest whoReq = new WhoAmIRequest(); 33 WhoAmIResponse whoRep = orgSvc.Execute(whoReq) as WhoAmIResponse; 34 var userEntity = orgSvc.Retrieve("systemuser", whoRep.UserId, new ColumnSet("fullname")); 35 Console.WriteLine(string.Format("登錄成功,歡迎{0},繼續操作請輸入y!", userEntity.GetAttributeValue<string>("fullname"))); 36 var input = Console.ReadLine().ToString().ToUpper(); 37 if (input == "Y") 38 { 39 QueryExpression qe = new QueryExpression("userentityuisettings"); 40 qe.ColumnSet = new ColumnSet("recentlyviewedxml"); 41 qe.Criteria.AddCondition("ownerid", ConditionOperator.Equal, whoRep.UserId); 42 var usersettings = orgSvc.RetrieveMultiple(qe); 43 if (usersettings.Entities.Count >= 1) 44 { 45 Console.WriteLine("共找到" + usersettings.Entities.Count + "條最近訪問記錄!"); 46 foreach (var item in usersettings.Entities) 47 { 48 item["recentlyviewedxml"] = null; 49 orgSvc.Update(item); 50 } 51 } 52 } 53 else 54 { 55 Console.WriteLine("你選擇了取消操作!"); 56 } 57 Console.WriteLine("程序運行完成!"); 58 Console.ReadKey();
據我對這個MRU的觀察,MRU的緩存在IIS上有緩存,用戶登錄會從服務器上獲取緩存,本地並不是通過瀏覽器來緩存,會在用戶退出登錄時候將訪問記錄寫入到服務器端。所以僅僅依靠程序或者SQL來清除MRU還不管用,需要配合IIS重啟。
系統在這個設計上可能欠缺考慮,比如針對某個實體的MRU如果超過一定的數量采用先進先出的方法自動清除掉前面的訪問記錄,畢竟保存那么多也沒有很大的用處,除非你要做訪問記錄的審核。也可以考慮再某個地方讓用戶自行清理。希望微軟Dynamics CRM產品組早日就此問題提出根本解決方案。