6/2/2009 11:28:47 AM

Занялся оптимизацией продукта, который вот-вот выйдет в свет и над которым я работал последние месяцы. Не с начала, в дизайне архитектуры я участия не принимал, только правил его потом, насколько было возможно.

Так вот, столкнулся с такой ерундой. Есть таблица в БД, одна из основных. В ней находятся миллионы записей и эти миллионы постоянно в работе (то есть, это не логи и не калькуляция, которые можно просчитывать и сохранять как-то еще). Скажем, это сообщения. Тысячи пользователей их шлют друг другу, читают, раскладывают по папкам, пролистывают и т.д.

В таблице этой примерно 1000 (одна тысяча) колонок. В колонках – данные, одна запись в таблице – одно сообщение со всеми-всеми своими атрибутами (коих может быть немерено). Вроде так же делает Sharepoint (не проверял).

Колонки, в целях экономии дискового пространства, почти все (кроме основных: ID, Name, etc) - sparse (это SQL Server 2008 у нас).

Вопрос:

Запросы типа SELECT [Id], [Name], [nVarchar1], [Integer1], [XmlData1], [XmlData3] FROM [Message].[Messages] WHERE TypeID = 3765

приводят к Table Scan. Возможно по причине sparse-колонок, я не уверен. Это происходит даже при наличии индекса на TypeId. Сервер предпочитает не использовать этот индекс. Если же его заставить, то время исполнения запроса только увеличивается.
Понятное дело, что это дело все неприемлемо медленно. Понятное дело, что всё это еще медленнее, если в WHERE появляется что-то более интересное.
Понятно, что при просмотре execution plan оно предлагает построить индекс по TypeId и включить в него все колонки, учавствующие в выборке. Но так, как их тысяча…
Нет, я даже попробовал. Ради интереса. Индекс (некластерный) строился час с лишним. Построился. На времени выборки это практически не сказалось, а вот время апдейта какого-нибудь nVarChar1 для одной строки выросло до 5 секунд! А было мгновенно. И должно быть быстро. Так что такие вот огромные индексы – тоже не панацея.

Может у кого-то есть какие-то идеи или опыт по работе с большими таблицами? Или на предмет как это можно реструктурировать, чтобы было поэффективнее, а?

Tags:

Comments (12) -

6/3/2009 9:47:01 AM

WondeRu

Я бы посоветовал следующее:
Выкинуть всю "требуху" ([nVarchar1], [Integer1], [XmlData1], [XmlData3]...) в таблицы вида ContentTableXXXX, который содержат ID, начинающиеся с 1. TypeID - остается в Messages.
XXXX - старшие цифры Messages.Id, например, если ID=456399235, то данное сообщение будет лежать в таблице ContentTable4563, где внутренний ContentTable4563.ID=99235.
При выполнении выборки мы легко можем определить таблицу "требухи" и внутренний ID  по Messages.Id.

WondeRu Russia

6/3/2009 10:10:47 AM

Vitalii

IMHO, возможно индекс не работает эфективно из-за того что размер строки слишком большой (фактически всегда превышающий 8К, я прав?). В нормальной ситуации, даже на мега-больших таблицах, скан по индексу (даже некластерному) практически моментальный. Из способов реструктуризации могу предложить выделить несколько главных столбцов, а все остальное запихнуть в поле XML - очень хорошо подходит под твою логику использования таблицы.

Кстати, "Индекс (некластерный) строился час с лишним." вполне нормально, во всяком случае на моих данных. Smile

Vitalii

6/3/2009 12:05:26 PM

Alexey Raga

WondeRu, интересно.
Это у тебя горизонтальный сплит получается, как я понимаю. Остается проблема в этом случае с поиском: скажем, WHERE nVarChar1 LIKE '%hello%'.
Была уже мысль попробовать заюзать полнотекстовый поиск для этого. Надо подумать, спасибо.

Alexey Raga

6/3/2009 12:09:55 PM

Alexey Raga

Виталий, размер фактически не имеет значения, ибо колонки все равно sparse. Делать их не-sparse слишком накладно и неэффективно.
Построение индекса по _всем_ полям, как я уже писал, абсолютно ничего не дает, кроме вреда при вставке и изменении.
Поле XML - была мысль сделать Column Set из всего этого sparse-богатства, но вот беда - в этом случае нельзя будет использовать полнотекстовый поиск, да и все равно остается проблема с критериями.
Кроме того, этот XML придется включать в индекс, не факт, что будет быстрее на вставках и изменениях, опять же.

Alexey Raga

6/3/2009 3:32:16 PM

Vitalii

Все-таки "Размер имеет значение" Smile, так как способ сохранения строк меняется в зависимости от размера (потенциально меньше 8К или больше 8К), а для sparse (так же как и для varchar) выбирается наихудший случай и сервер прогнозирует storage на основании этой информации (иначе, при обновлении sparse с null на что-нить другое, у тебя бы строка перемещалась со стореджа одного типа в другой, что очевидно не эфективно). Иначе, как обьяснить такое поведение?
FYI, у меня есть таблица почти на 1b записей, поиск и выборка по некластеризированом ключю занимает < секунды, но таблица специально спроэктирована так чтобы записи были меньше 8К.
По XML-ю очень даже можно организовать выборку, поиск и индексы, используя XML Schema и XPath - если будет интересно, с удовольствием поделюсь информацией.

Vitalii

6/4/2009 2:31:16 PM

Alexey Raga

Для sparse, так же, как и для nvarchar(max), данные просто хранятся в отдельных страницах. Вот и всё. Размер значения не имеет, ибо nvarchar(max) в таблице так же _потенциально_ означает "забудь, Виталик, про страничные 8К" Smile Строка выходит за 8К - и всё, nvarchar(max) отправляется гулять в LOB Smile Просто sparse намного эффективнее в плане дискового пространства.
Как-то это дело будем рефакторить... Отпишусь, когда что-то получится.

Alexey Raga

6/4/2009 3:39:04 PM

Vitalii

Так и есть Smile, но вопрос-то был почему тормозит выборка по индексу? Smile Будет очень интересно узнать причины, как найдете ;).

Vitalii

6/5/2009 5:50:41 AM

Alexey Raga

Вопрос был не в этом. Where - да, по индексу. Но в выборку включены и поля, которые не включены в индекс.
Вот тогда и тормозит.
Есть мысль попробовать filtered indexes, хотя идея рефакторинга мне нравится больше. Будем пробовать ;)

Alexey Raga

6/6/2009 3:36:35 AM

Vadim

Вот такие вопросы можно на stackoverflow.com спрашивать Smile

Vadim

6/6/2009 5:04:04 AM

Alexey Raga

Поступили проще - после праздников придут ребята из Майкрософт, инженеры по SQL Server, поковыряются и дадут рекомендации Smile

Alexey Raga

6/8/2009 9:30:31 AM

Vadim

На stackoverflow можно было с большой долей вероятности от них же помощь на халяву получить Smile Скорее всего не настолько делтальную, конечно, но все же.
Я тут недавно про Firefox спрашивал, так мне не только разработчик ответил, он еще и писателя документации пнул, чтобы тот ее обновил Smile

Vadim

6/9/2009 2:30:09 PM

Alexey Raga

Вряд ли можно было бы... Там нужно смотреть, пробовать..
День был продуктивный. Завтра устроим еще один Smile

Alexey Raga Australia

Comments are closed

Powered by BlogEngine.NET 2.5.0.6

About the author

Alexey Raga Alexey Raga
.NET software developer.

E-mail me Send mail

Twitter


Recent posts

Archive

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2012

Sign in