В этой статье мы решили поделиться своими жизненными наблюдениями за проблемой производительности больших списков в SharePoint.
Итак, мы достаточно часто сталкиваемся с ситуациями, когда есть список на портале SharePoint, в котором хранятся и обрабатываются, например, заявки пользователей. Дополнительно всегда возникает желание сделать так, чтобы все заявки хранились в одном месте, с другой стороны — чтобы права на эти элементы были только у пользователя, подавшего заявку, и у группы или групп пользователей, которые участвуют в ее обработке.
ACL (Access Control List) – упорядоченный список принципалов, которые определяют права на элемент или группу элементов.
Security Scopes – набор пар групп или пользователь + ACL.
Рекомендации Microsoft
Microsoft рекомендует соблюдать достаточно простые правила: избегать количества одновременных Security Scopes более 5 000 на одну библиотеку документов или на один список.
Почему так? Откуда взялось ограничение? Для начала ответим на вопрос, каким образом MS SharePoint обрабатывает запрос к списку.
- Делает обращение к БД содержимого и читает:
a. Метаданные списка, т.е. определение списков полей и т.п.
b. Computed fields, определения обработчиков событий и много чего еще.
c. Все scope списка (точнее представления). - На стороне front-end определяет: в scope попадает пользователь.
- Вытаскивает уже непосредственно данные.
Такому подходу есть объяснения, и связаны они с поддержкой модели внешних поставщиков членства и ролей в первую очередь (AD, т.е. AD является наиболее широко распространенным поставщиком членства и ролей).
На практике можно видеть, что проблемы начинаются уже с 1000 элементов с уникальными правами.
С помощью этих SQL-запросов можно посчитать, сколько сейчас у вас Security Scopes:
SELECT [SiteId], [ScopeId], [RoleDefWebId], [WebId], [ScopeUrl], [Acl] FROM [your Content DB].[dbo].[Perms] order by scopeurl
An even better query exposes the web URL
SELECT [fullurl],perms.[SiteId] , perms.[ScopeId], [RoleDefWebId], [WebId], [ScopeUrl], [Acl] FROM [dbo].[Perms],[dbo].allwebs where perms.WebId = allwebs.id order by scopeurl
As an aside, you can see the number of security principals per scope, using this SQL:
select COUNT(ra.PrincipalId) as [Count],p.ScopeUrl from RoleAssignment ra with(nolock) join Perms p with(nolock) on p.SiteId = ra.SiteId and p.ScopeId = ra.ScopeId group by p.ScopeUrl order by p.ScopeUrl desc
Как Microsoft предлагает решать проблему:
- Использовать папки для инкапсуляции прав доступа на группы элементов с похожими правами.
- Разбивать на разные списки или библиотеки документов и использовать наследованные права.
- Никогда не выходить за лимит 5 000 уникальных права на листе.
К чему приводит нарушение этих рекомендаций:
- Снижение производительности на 20% на каждые 1 000 уникальных прав на листе.
- Программные ошибки при попытках разорвать права на элементе и выдать уникальные.
Для решений на базе MS SharePoint критически важно, чтобы ферма держала нагрузку, и вот почему.
Для разработчика нет поддержки транзакций. И наборы операций вида (т.е. если создать элемент, разорвать права, выдать на него права в MS SharePoint):
var item = list.AddItem(...); // ... item.Update(); item.BreakRoleInheritance(false); item.RoleAssignments.Add(new SPRoleAssignment(...));
будут прерываться в неожиданных местах, просто потому что ферма не выдерживает нагрузки (т.е. не из-за логических ошибок).
Понятно, что рекомендации 2 и 4 носят более камерный характер и не решают задачу хранения элементов вместе и уникальных прав, поэтому остановимся на первом как самом действенном способе – использование папок.
Наш пример
Для примера возьмем список из реально работающей системы, в которой на текущий момент 132 073 элементов:
В ней 3 590 Security Scopes – выглядит примерно вот так:
Внутри списка 721 папка…:
… на которые выданы права следующим образом:
По данным Google Analytics, среднее время загрузки страницы менее секунды — при уровне посещений именно этого списка за этот период порядка 968 в день, в рабочее время.
Наш подход
В соответствии с нашим подходом права раздаются вот так:
public static void ProcessItem(SPListItem item, SPFolder folder) { var file = item.Web.GetFile(item.UniqueId); // Внимание не используйте item.File т.к. он во многих случаях будет null // http://msdn.microsoft.com/ru-ru/library/microsoft.sharepoint.splistitem.file.aspx file.MoveTo(string.Format("{0}/{1}", folder.Url, item[SPBuiltInFieldId.LinkFilename])); }
Также хочется добавить, что если наборы прав различаются не разрешениями на чтение видимости, а возможностью изменения, можно использовать Event Handler, который будет запрещать изменения.
class PermissionEventHandler : SPItemEventReceiver { public override void ItemUpdating(SPItemEventProperties properties) { var updatingAllowed = CheckUserHasPermission(properties.Web.CurrentUser, properties.ListItem); // определить может ли пользователь изменять элемент if (!updatingAllowed) { properties.Cancel = true; properties.ErrorMessage = "Access denied"; } } public override void ItemAdding(SPItemEventProperties properties) { //... } //... }
До изобретения этого подхода нам пришлось в течение 5 лет плотно заниматься SharePoint, съесть немало собак и кактусов. У него есть свои нюансы, но он абсолютно точно работает — опробовано уже на двух порталах.
Как всегда, рады были поделиться опытом!
ссылка на оригинал статьи http://habrahabr.ru/company/eastbanctech/blog/211735/
Добавить комментарий