用例1:在SharePo列表中存储了多少项目?
有多种方式来回答这个问题我曾经看到很多次个例子是下面这样:
noOfItems = SPContext.Current.List.Items.Count;
这句代码告诉我们在列表中数据项数目为了得到这个结果不得不从内容数据库中获取列表所有项目下面截图显示了当上面代码在访问Count属性时候对象模型内部执行过程:
图片看不清楚?请点击这里查看原图(大图)
对于小型列表这不会有问题查询还是比较迅速不过当列表增长数倍或自定义代码从未在实际数据上测试过话这将成为个问题
对于这种情况微软为SPList提供了另外个属性名为ItemCount正确代码应该是:
noOfItems = SPContext.Current.List.ItemCount;
在这种情况下SharePo只用在内容数据库中查询Lists数据表单个记录在列表中数据项数量会被冗余地存储在这以便无需查询整个AllUserData数据表(所有SharePo列表项目都保存在这里)就可获得这个信息
用例2:使用SPList显示列表中项目?
通过SharePo对象模型有多种思路方法可以遍历SharePo列表数据项我曾经在个实际运行SharePo应用中见到这样个方式——在开发人员机器上或在非常小列表上可能会工作正常但是旦在超过几百条数据项列表上执行这种方式就会出现致命性能问题让我们看下代码片段它被用于个WebPart中从当前上下文SharePo列表中获取前100个数据项:
SPList activeList = SPContext.Current.List;
for( i=0;i<100 && i
SPListItem listItem = activeList.Items[i];
htmlWriter.Write(listItem["Title"]);
}
假定在这个列表中至少有100条数据项——这段代码为了获取前100个SharePo列表数据项100个Title值会往返访问数据库多少次?你也许会很惊讶经过对上述代码执行过程分析在你看到来着数据库视图时候共对数据库进行了200次:
图片看不清楚?请点击这里查看原图(大图)
原因在于在每次循环中当访问Items属性时候我们都请求了个新SPListItemCollection对象Items属性未被缓存Cache因此总是从数据库中反复不断地请求所有数据项下面是第个循环迭代情况:
图片看不清楚?请点击这里查看原图(大图)
正确思路方法
正确思路方法当然是把Items属性返回值存储在个SPListItemCollection变量中这样数据库就只会查询次并且我们接下来也可以遍历存储在集合对象里面结果集下面是修改后举例代码:
SPListItemCollection items = SPContext.Current.List.Items;
for( i=0;i<100 && i
SPListItem listItem = items[i];
htmlWriter.Write(listItem["Title"]);
}
你也能使用foreach循环语句它会被编译为类似代码其充分利用了Items集合IEnumerable接口下面是新循环在内部如何被处理过程:
图片看不清楚?请点击这里查看原图(大图)
用例3:使用SPQuery和SPView只请求你真正需要数据
在必须处理来自数据库数据任何类型应用中我们都能见到个主要性能问题是有太多数据要访问请求比当前用例所需数据量更多信息会导致额外:
在数据库上查询开销以便收集请求信息
在数据库和应用的间通讯开销
在数据库和应用上内存开销
回头看下前面2个用例你会发现被执行SQL Statement总是从请求SharePo列表中选取了所有数据项你可以说你看到SELECT子句是这样书写:SELECT TOP 2147483648……
限制返回行数量
在访问SharePo列表中数据项时候万你只想得到有限数量结果集那么你可以利用SPQuery.RowLimit属性
下面是个例子:
SPQuery query = SPQuery;
query.RowLimit = 100;
SPListItemCollection items = SPContext.Current.List.GetItems(query);
for ( itemIx=0;itemIx
SPListItem listItem = items[itemIx];
}
在SPList.GetItems思路方法中使用SPQuery对象将生成包含如下SELECT子句结果
图片看不清楚?请点击这里查看原图(大图)
在的前例子中我们已经限制了想要获取数据项数目然而我们依旧还是请求了定义在SharePo列表中所有列对于真正需要把所有列都显示给最终用户或者需要所有列来完成某些计算情况这样做毫无问题然而在大部分情况下我们只需要几个而不是全部列
限制检索列
有两种思路方法来限制哪些列要从数据库检索:
使用SharePo视图:SPView
使用SPQuery.ViewFields属性
因此上面举例代码可以用如下两种方式进行修改:
SPQuery query = SPQuery(SPContext.Current.CurrentView.View);
或
SPQuery query = SPQuery;
query.ViewFields = "";
在两种场景中SELECT子句都只包含那些定义在SharePo视图中字段这些字段各自在ViewFields属性中被引用:
图片看不清楚?请点击这里查看原图(大图)
用例4:通过SPQuery来对SharePo列表数据项进行分页
SharePo列表能包含成千上万数据项我们都听说为了获得较好列表性能不应该超过2000条限度当超过这个限度后确实存在性能影响有些思路方法可以克服这个限度就是使用索引列和视图
在访问列表中数据时除了考虑这些原因外还有个方面比较重要在的前用例中已经解释过——只访问你需要数据可以大大降低SharePo内容数据库压力另外SharePo对象模型也提供了些额外特性来加强访问列表数据项能力
数据分页就是其中个技术就是我们在富客户端应用或Web应用中已经熟知使用数据网格类似方式分页让最终用户便捷地导航数据并且——如果正确地实现话——可以减少低层数据库负载
SPQuery对象提供了属性ListItemCollectionPosition通过它你能够设定查询页起始位置RowLimit让你设定每页要获取多少数据项让我们来看些举例代码:
SPQuery query = SPQuery;
query.RowLimit = 10; // that is our page size
do
{
SPListItemCollection items = SPContext.Current.List.GetItems(query);
// do something with the page result
// the position cursor for the next iteration
query.ListItemCollectionPosition = items.ListItemCollectionPosition;
} while (query.ListItemCollectionPosition != null)
SPList.GetItems执行了这个查询每次GetItems只返回10条数据项SPListItemCollection提供 ListItemCollectionPosition属性就像SharePo列表上个指针这个属性能用于任何页面遍历以定义下页起始点下面插图显示了数据库活动:
图片看不清楚?请点击这里查看原图(大图)
仔细看下展现给我们这个SQL Statement它混合了SELECT TOP和WHERE子句用于获取某个页数据项:
图片看不清楚?请点击这里查看原图(大图)
用例5:更新大量SharePo列表数据项
的前用例关注于存储在SharePo列表中数据项读取访问现在来讨论下如何更好地更新或添加新数据项了由于SharePo对象模型提供了丰富接口所以我们又可以在多种方式中进行选择了
在SharePo列表中添加和更新数据项第个显而易见方式是SPListItem.Update要获得个列表数据项既能通过查询个现存数据项又可以用SPListItemCollection.Add来添加个新
让我们看下如下例子:
for ( itemIx=0;itemIx<100;itemIx) {
SPListItem Item = items.Add;
// fill all the individual fields with values
Item.Update;
}
对这段代码进行分析我们看到对Update思路方法每次实际上都了内部思路方法SPListItem.AddOrUpdateItem它事实上是了个存储过程来完成这个任务:
我们看到添加100条数据项到我列表中花费了4.5秒时间
使用批量更新代替单个更新
如果你必须更新大量数据项强烈建议你不要在每个数据项上独立使用Update思路方法而是使用SPWeb提供批量更新ProcessBatchData
ProcessBatchData执行以XML格式定义批量思路方法这里有篇很好文章解释了如何使用批量更新通过利用批量更新可以把上面例子实现为这样:
StringBuilder query = StringBuilder;
for ( itemIx=0;itemIx<100;itemIx) {
query.AppendFormat("" +
"{1}" +
"New" +
"Save" +
"{2}" +
"", itemIx, listGuid, someValue, "urn:schemas-microsoft-com:office:office#");
}
SPContext.Current.Web.ProcessBatchData(
"" +
"{0}", query.)
通过ProcessBatchData添加同样100条数据项通过分析内部机理我们知道在更新过程中花费了多少时间:
两种更新方式对比表明我们可以通过批量更新来获得巨大性能提升:
图片看不清楚?请点击这里查看原图(大图)
注意
批量更新实际上只在执行大量更新时候才被推荐不过请研究下创建批量更新XML花销:
确保使用StringBuilder而不是把些独立串对象连接在起
分割批量更新以保持生成XML足够小不会出现内存溢出异常在执行上述包含了50000个批量更新例子时候我就遇到了OOM(内存溢出)
用例6:哪个是我最慢列表它们如何被使用以及为什么会慢?
我们知道SharePo列表性能随着存储在其中数据项数量增加而降低并且也和显示时候列表如何进行过滤有关你可以找到谈及每个列表2000条数据项限度很多文章和博客帖子
为了做正确事获得良好性能首先需要了解当前使用情况并分析性能问题
有几种方式可以算出你SharePo应用当前访问统计量你可以分析IIS日志文件或者使用SharePo使用率报告特性(SharePo Usage Reporting Feature)
监测列表性能最简单方式就是分析各个SharePo List和SharePo ViewURLHTTP响应次数SharePo URL格式类似这样:http://servername/site/{LISTNAME}/{VIEWNAME}.aspx
为了分析它我们可以基于这两个标记来对请求进行分组我使用dynaTraceBusiness Transaction特性依照正则表达式来分组捕获到PurePath's
图片看不清楚?请点击这里查看原图(大图)
这样结果让我们明白哪些列表和视图使用最频繁它们表现如何
此外对HTTP请求进行分析——它只为那些显示特定列表或视图页面提供准确数据——这样我们就能分析自定义Web部件或自定义页面列表使用率它们往往比单个列表或视图被访问得更多也比那些以特定过滤方式来访问列表情况多
我们也能分析和SharePo对象模型交互情况比如被用于呈现列表和视图SPRequest.RenderViewAsHtml使用情况对SPList和SPView访问情况下面插图显示了对SPRequest思路方法使用率和性能指标:
上面插图给我们展现了列表内部GUID每个列表和视图都通过GUID来唯标识这是查找真实列表名称另外种方式:你可以吧GUID粘贴进URL中以编辑列表和视图设置下面是个例子:
http://servername/_layouts/listedit.aspx?List={GUID} (GUID must be URL-Encoded).
这为我们提供了另外种打开内容数据库和查询AllLists数据表方式这个数据表包含着GUID和List名称
列表为什么慢?
现在由于我们已经知道哪些列表和视图被频繁访问我们就能重点关注那些让性能下降地方为了改善最终用户体验我们应该集中在最频繁访问列表上而不是那些偶尔访问下列表
列表表现缓慢存在有多种原因:
有太多数据项显示在列表视图中
有太多数据项保存于没有过滤和索引列列表中
自定义Web部件进行了无效率数据访问
Conclusion
SharePo对象模型提供了种轻松灵活方式来扩展SharePo应用这个框架提供了区别机制来访问和编辑存储在 SharePo列表中数据然而不是每种可能方式对于每个用例场景都是可取了解SharePo对象模型内部原理可以让我们创建 SharePo应用运行得更好性能更易伸缩
有关作者
Andreas Grabner作为个技术战略决策者工作于dynaTrace Software他角色归属R&D部门影响着dynaTrace产品决策并和关键客户紧密协作为整个应用生命周期实现了性能管理解决方案Andreas Grabner在Java和.NET领域拥有10年构架和开发经验
最新评论