Показаны сообщения с ярлыком MS SQL Server. Показать все сообщения
Показаны сообщения с ярлыком MS SQL Server. Показать все сообщения

вторник, 16 ноября 2010 г.

Эволюция MS Full-Text Search: доступ к анализатору полнотекстовых запросов

Так и работает эволюция: незаметно и по чуть-чуть. Недавно на StackOverflow задали вопрос (в вольном переводе):

Можно ли получить доступ к стеммеру, который используется в SQL Server FullText Search?

Моей первой мыслью было “нельзя”. Но на всякий случай решил свериться с MSDN. Так вот оказалось, что “почти можно”: в SQL Server 2008 в FullText Search помимо радикального изменения архитектуры, внесли менее заметные, но полезные изменения. В частности, была добавлена следующая системная табличная функция:

sys.dm_fts_parser('query_string', lcid, stoplist_id, accent_sensitivity)

Она-то и позволяет обратиться к стеммеру (точнее к синтаксическому анализатору полнотекстовых запросов). Вот как это работает (лучше один раз увидеть, как говорится):

select * from sys.dm_fts_parser('FORMSOF(inflectional, Worked)', 1033, 0, 0)

Т.е. мы запросили все словоформы слова “worked” в английской локали (locId = 1033). И вот какие ключевые слова будут соответствовать этому запросу:

image

Было бы еще хорошо иметь возможность заглянуть внутрь самого индекса (как это делает Luke для Lucene), но даже с этими средствами жить  становится намного веселее.

HTH, AlexS

четверг, 29 июля 2010 г.

Чем точнее указан путь – тем быстрее можно по нему идти ((С) Кэп)

Недавно попросили помочь с оптимизацией обработки XML в SQL Server-е. Запрос выглядит примерно следующим образом:

SELECT
  nref.value('./../../@id', 'int') AS articleid,
  nref.value('type[1]', 'nvarchar(256)') AS type,
  nref.value('theme[1]', 'varchar(256)') AS theme,
  nref.value('score[1]', 'varchar(32)') AS score
INTO #tmpTheme
FROM @bulk_xml.nodes('//theme') AS R(nref)

Очень простое действие – указание точного XPath выражения вместо обобщенного ('//theme') позволяет ускорить работу данного запроса в 1.5 – 2 раза.

HTH,

AlexS

воскресенье, 11 июля 2010 г.

Заблуждения: “restore from database”

Недавно пришлось развенчивать миф: “restore from database” (см. скриншот) позволяет использовать журнал транзакций [базы данных А] для “point in time recovery” [базы банных Б, восстановленной из резервной копии базы данных А].

image

Контекст, в котором происходило обсуждение, тоже представляет определенный интерес Есть база данных [пусть будет “База А”], которая довольно сильно нагружена. Она работает в режиме восстановления от сбоев “Full”. Каждую ночь делается полный бэкап этой базы, никакие другие бэкапы (дифференциальный, журнала) не делаются. Совет перевести базу в режим восстановления “Simple” (нагрузка на подсистему ввода/вывода меньше, размер журнала меньше, по возможностям  восстановления ничего не теряем) наталкивается на следующий аргумент  (вольный перевод)

“если База А будет повреждена, то мы сможем восстановить ее из резервной копии и донакатить транзакции из журнала, поэтому Simple нам не подходит”

Так вот, “эта штука так не работает”. Опция “from database” просто-напросто подхватывает историю резервных копий выбранной базы данных – так что пользователю не нужно лазить по дискам в поисках какого-то конкретного бэкапа (или самого свежего бэкапа). Журнал транзакций “намертво” привязан к базе и только SQL Server может что-то с ним делать (пользователь может только посмотреть что в нем, да и то используя недокументированные функции). Единственный [для пользователя] способ использовать информацию об операциях, хранящуюся в журнале, – сделать резервную копию журнала после создания полного бэкапа базы данных. В этом случае резервная копия журнала (а не сам журнал!) может быть использована для повторного выполнения операций в базе, восстановленной из этого полного бэкапа (т.е. “point in time recovery”).

HTH, AlexS

воскресенье, 16 мая 2010 г.

Право на жизнь для табличных переменных

С момента появления в SQL Server табличных переменных я все никак не мог понять (для себя): зачем же они прямо так нужны (по большому счету). Аргумент “передавать много параметров в хранимую процедуру в табличной форме” звучал (и до сих пор звучит) не слишком убедительно: никто не отменял упаковки/распаковки (в случае, когда вызов идет из кода) и … собственно таблиц (в случае, когда мы вызываем хранимку из другой хранимки/скрипта).

Недавно на StackOverflow был задан вопрос, который в вольном переводе звучит следующим образом: как мне сохранить какие-то данные из транзакции, которая затем будет отменена? Сценарий, который стоит за этом вопросом, более чем реален: протоколирование. Ответом на этот вопрос и являются табличные переменные: изменения значений переменных не является частью транзакции (и, соответственно, не подлежат отмене).

Проверил: да, таки работает. Но тут как в том старом анекдоте: “ложечки нашлись, но осадок остался”. Табличная переменная на самом деле – это временная таблица (что легко проверить). При этом изменения, вносимые в “обычные” временные таблицы протоколируются и являются частью транзакции, в которой эти изменения происходят (что тоже легко проверяется). Получается некоторое противоречие: табличная переменная – это временная таблица, но изменения, которые в нее вносятся не протоколируются. Покопавшись в гуглоридере, нашел (отмеченный мною же!) пост в блоге SQL Server Storage Engine на эту тему – “перечитал и переосмыслил” :-)

HTH

AlexS

среда, 3 февраля 2010 г.

Первые впечатления от StreamInsight (aka Complex Event Processing от Microsoft)

В состав SQL Server 2008 R2 релиз которого намечен на май этого года, входит технология StreamInsight (официальная страница и блог). Несмотря на очевидную ассоциированность, к SQL Server-у эта технология не имеет практически никакого отношения – кроме того факта, что их “положили в одну коробку”.

StreamInsight – это попытка Microsoft выйти на новый для себя рынок Complex Event Processing (в конце поста есть несколько ссылок на информацию о других игроках). Общее описание идея очень сильно напомнило мне системы обработки сообщений: скорее всего из-за наличия потоков, очередей, обработки и прочих схожих концепций. Ключевым, на мой взгляд, является слово “Complex”: предполагается, что мы производим над входным потоком событий некоторые нетривиальные манипуляции, которые позволяют нам осуществлять анализ этого потока в реальном времени (точнее “с известной задержкой”, поскольку зачастую речь идет об операциях, проводимых на каком-то [скользящем] окне наблюдений). Технология доступна пока только для предварительно просмотра, скачать можно отсюда. Дистрибутив компактен (около 10 Мб) и содержит неплохую подборку примеров и обзорную документацию. Последняя, однако, не настолько актуальна, как MSDN.

В обзорной документации все описано простым и понятным языком:

  1. Определяем входной поток событий.
  2. Задаем механизм обработки - на самом деле Linq-запрос, в котором можно использовать некоторые специфичные для потоков событий функции (вроде обращения к “окну”).
  3. Задаем характеристики выходного потока - поток результатов выполнения запроса из п.2 на входном потоке событий.

Обещается масштабируемость “куда хочешь” и параллельная обработка “как хочешь”.

В ходе попытки самостоятельно реализовать приложение, использующее эту технологию, пришлось столкнуться со следующими проблемами:

  • Для того, чтобы “воткнуть” поток в CEP Engine (который занимается вычислениями/обработкой) необходимо реализовать InputStreamAdapter (который выполняется в контексте того же процесса, который выполняет CEP Engine). Этот адаптер на самом деле является конечным автоматом (aka State Machine). Состояния вроде бы описаны, но машина далеко не так проста, как хотелось бы. Причем со всеми этими переходами приходится быть чрезвычайно аккуратным, ибо в примерах есть грозные предупреждения вида “если не сделать вот этот ‘финт ушами’, то в CEP Engine будет утечка памяти”. Таким образом, для реализации простейшего адаптера приходится писать довольно много не относящегося к делу кода. Аналогичная ситуация и с выходными потоками – тот же конечный автомат и те же проблемы: довольно много сервисного кода на ровном месте.
  • Не до конца понятен механизм типизации событий: она (типизация) вроде бы заложена в сам поток (который является generic-классом), но отсутствует в адаптере потока (который, собственно, и “производит” поток для CEP Engine).
  • Пока не совсем понятно, как все-таки эта штука масштабируется и какая у нее пропускная способность.
  • Также не очень ясно, насколько сложный анализ можно производить: в расчете простейших агрегаций особого (революционного) смысла нет, а вот относительно более сложных расчетов возникают вопросы: как их “скрестить” с Linq? какую величину окна сможет безболезненно (для производительности) сохранять CEP Engine? Ответов я пока не нашел.

Однако “попилим еще” и будет видно. Глядишь, к моменту выхода (или к версии 2 ;-) ) станет более понятно что к чему.

Где еще можно об этом почитать:

HTH,

AlexS

понедельник, 11 января 2010 г.

SqlDependency: размножение дикобразов

Как размножаются дикобразы?

Очень, очень осторожно либо безрезультатно – что-то дикобразов нигде не видно.

(С) “Афера Томаса Крауна”

В связи с переходом на новую работу, на мне “подвисла” одна задача, которую я не успел решить в старом проекте. Связана она была, как нетрудно догадаться из темы, с SqlDependency. Дело получается относительно давнее (вот уже больше двух месяцев как я в этом проекте не участвую), но поскольку недавно стало известно, что проблема решена, я  ставлю “зарубку на память”.

Вкратце предыстория такова: в какой-то момент времени было принято решение использовать SqlDependency для очистки кэша настроек приложения – сценарий достаточно стандартный. Как всегда нестандартными (на первый взгляд) были проблемы, возникшие позже. Настройки хранятся в специальной БД (ConfigDB), к которой обращаются приложения (ASP.NET App, несколько экземпляров потенциально на разных серверах) и [вспомогательные] “сервисы” в виде служб и консольных приложений, запускаемых по расписанию.

В какой-то момент с ConfigDB начинаются проблемы:

  • невозможно сохранить измененные настройки (timeout)
  • измененные настройки не подхватываются клиентами
  • SqlServer начинает ощутимо тормозить (хотя поначалу это не связывается с ConfigDB)
  • журнал ошибок SqlServer-а забивается сообщениями с “жалобами” на query norifications (“The query notifications dialog conversation handle ‘{xxxxxx….}’ closed due to the following error …”, with error code 8490 or 8470)

Проблема живет только на продакшине и воспроизвести ее в тестовом окружении не удается. Попытки хоть как-то локализовать проблему дают следующий результат:

  • создание и использование новой ConfigDB помогает, правда ненадолго – через пару дней/неделю все возвращается на круги своя
  • восстановление ConfigDB из резервной копии занимает очень много времени – база объемом 500 Мб (из которых 400 – это журнал транзакций) при восстановлении на двухьядерной машине с 2 Гб оперативной памяти намертво подвешивает ее (процесс не завершился за 1.5 часа)
  • выясняется, что в ConfigDB очень много открытых подписок (query notification) и их количество растет

Проблему в итоге удалось устранить путем:

  1. Вдумчивого чтения и переработки кода, который работает с объектами SqlDependency.
  2. Закрытия всех открытых подписок (об этом ниже).

По ходу работы пару раз слышались голоса “SqlDependency – это зло”, “и зачем вы вообще это используете”, “да никогда и ни в жизни не пользовать ЭТО”, “лучше сделать все ‘руками’”. В итоге скептики были посрамлены, механизм работает как и ожидалось, проблему устранили. Но должен признать, что работать с SqlDependency необходимо очень осторожно и внимательно.

Итак, о чем нужно помнить при использовании этой технологии:

  • Строго следите за тем, чтобы на каждый вызов SqlDependency.Start приходился вызов SqlDependency.Stop – об этом явно написано в MSDN, но на моих глазах (в 2х случаях из 2х) об этом “забывали”, а потом долго искали решение возникших проблем. Хорошим решением будет обернуть всю работу с SqlDependency в некий класс (отвечающий за кеширование/инвалидацию), реализаций IDisposable, и помещение вызовов Start и Stop в конструктор и Dispose соответственно.
  • В обработчике события SqlDependency.OnChange не ленитесь анализировать причину вызова обработчика (SqlNotificationEventArgs.Info). Игнорирование этого простого совета может привести (и рано или поздно приводит) к возникновению эффекта, похожего на "положительную обратную связь”: когда на сервере что-то пошло не так, вызвался обработчик, мы тут же создали новую подписку, добавив тем самым проблем серверу, который тут же снова вызовет наш обработчик.
  • Не забывайте о протоколировании – наличие информации о том, когда и с какими параметрами вызывается обработчик события OnChange здорово упрощает диагностику.
  • Проводите мониторинг SqlServer-а. Отслеживание количества активных подписок (select count(*) from sys.dm_qn_subscriptions) позволит вам на самых ранних этапах обнаружить “утечку” и устранить ее пока это не переросло в БОЛЬШУЮ проблему. Обычно именно мониторингом все склонны пренебрегать.
  • Считайте количество подписок, которые будут регистрироваться на сервере в ходе нормального (штатного) функционирования вашего приложения. Десятки подписок – не проблема. При сотнях стоит уже быть ооочень осторожным. Если же расчетное количество подписок исчисляется тысячами, то следует подумать о изменении архитектуры с целью уменьшения этого числа.
  • Не самой лучше идеей будет [активно] использовать этот механизм в базе данных с высокой нагрузкой.
  • Если что-то пошло не так, помним о возможности “убить” подписку: KILL QUERY NOTIFICATION SUBSCRIPTION <subscription id/ALL>.

UPDATE: как было совершенно справедливо замечено Airex-ом, при использовании SqlDependency в контексте ASP.NET, наилучшим местом для вызова SqlDependency.Stop является Application_End (в Global.asax). И не стоит надеяться на деструктор того класса, в который вы “запрячете” всю работу с SqlDependency – в этой связи немного обновил (уточнил) первый пункт выше.

HTH,

AlexS

понедельник, 6 июля 2009 г.

Журнал транзакций и запросы на выборку данных

Решил разобраться в том, что же на самом деле представляет из себя журнал транзакций (transaction log) в SQL Server-е.

Весь Books Online/MSDN пронизан мыслью о том, что журнал транзакций - это жизненно важный компонент базы данны и содержит данные о всех транзакциях, которые в ней выполняются. Эта мысль понятна, но меня долгое время занимал вопрос: как же быть с запросами на выборку данных? Они ведь тоже имеют свой уровень изоляции и выполняются в транзакции (пусть и неявной). Записываются ли они в журнал? И если да, то в каком виде и зачем?

Провел пару экспериментов, посмотрел сам журнал и выяснилось: в журнал транзакций не записываются "нерезультативные" тразакции (те, тразакции, которые не приводят к изменению данных). Перефразирую: журнал транзакций содержит "изменения, вносимые в БД", а не "запросы, выполняемые к БД".

Ниже приведу факты, которые (на мой взгляд) существенны для понимания того, чем является и чем не является журнал тразакций в SQL Server:

  1. Запись в журнал производится ДО того, как происходит изменение данных.
  2. Каждая запись содержит идентификатор транзакции, в рамках которой производится данное изменение - это позволяет откатить или заново выполнить любую запротоколированную транзакцию.
  3. Все записи в журнале (и, соответственно, все действия, производимые на физическом увроне с БД) делятся на две категории: те, для отката/повторения которых записывается логическая операция и те, для отката/повторения которых записывается образ данных до и после выполенния операции.
  4. Протоколируются только действия, приводящие к изменению данных - т.е. фактически журнал транзакций содержит результат обработки входящего потока транзакций, а не сами транзакции.

HTH,

AlexS

среда, 24 июня 2009 г.

T-SQL Simple Facts

В последние пару недель читал блоги/Books Online и (заново) открыл для себя некоторые "простые" факты, некоторые из которых здорово меня удивили (признаюсь). Некоторые другие всплыли "по ассоциации", в итоге решил поделиться всеми сразу:

  1. Всем хорошо известен оператор GO, означающий окончание пакета. Так вот, полный синтаксит этого оператора: GO [count] - где count - положительное целое число, указывающее, что предшествующий пакет необходимо выполнить count раз (квадратные скобки указывают, что этот параметр может отсутствовать (что и происходит в ошеломляющем большинстве случаев)).
  2. Для типа данных datetime определена операция сложения, причем вторым аргументом может быть целое число, которое интерпретируется как количество дней. Так:
    DECLARE @D datetime
    SET @D = '2009/06/24 15:00'
    SET @D = @D + 2
    PRINT @D
    выдаст 26 июня 2009, 15:00 ... когда я увидел эту запись, то признаться сильно удивился.
  3. Для добавления данных в таблицу можно использовать следующий синтаксис:
    INSERT INTO <Имя таблицы> DEFAULT VALUES;
    Сработает, конечно, только в том случае, когда для всех полей таблицы, не считая автоинкрементных и timestamp, определены значения по-умолчанию, но все-равно любопытно.
  4. Тип данных timestamp не имеет ничего общего с *nix timestamp (отсчитывающего количество миллисекунд с начала Unix-эры). В SQL Server колонки этого типа данных обновляются автоматически при любых операциях добавления/изменения записи - т.е. это некий аналог "версии строки", за который отвечает сам сервер. Гарантируется уникальность значений полей timestamp в рамках каждой базы данных.
  5. Переменные не покрываются транзакцией:
    DECLARE @i
    SET @i = 10
    BEGIN TRANSACTION
    SET @i = 20
    INSERT INTO SomeTable VALUES (@i)
    ROLLBACK TRANSACTION
    PRINT @i
    напечатает 20 (а не 10, как можно было бы ожидать).
  6. Синтаксис ALTER TABLE ... ALTER позволяет изменить определение поля таблицы но не его имя (что можно было бы ожидать). Для переименования необходимо воспользоваться хранимой процедурой sp_rename (не слишком элегантно, но действенно).

HTH,

AlexS

понедельник, 20 апреля 2009 г.

Полнотекстовый поиск: MS Full-Text Search

Вторая часть серии о полнотекстовом поиске несколько задержалась - работа не давала продохнуть. Впрочем, это лирика, приступим к делу.

Итак, я не буду повторять массу открыто доступной информации о Microsoft Full-Text Search (общее описание от Microsoft (eng/рус) и Wikipedia, архитектура), не буду и переписывать простые и не очень примеры. Сосредоточимся на следующем сценарии: у нас имеется некоторое не слишком сложное приложение (веб-приложение, два-три слоя, до несколько Гб данных (до десятка-другого), пара-тройка вспомогательных сервисов) и нам необходимо обеспечить "интеллектуальный поиск" для некоторых сущностей этого приложения. "Интеллектуальность" поиска - маркетинговый прием, которым sales/accounts привлекают пользователей. С чисто технической точки зрения за ним может скрываться следующее:

  • находить не только точные вхождения слов из поискового запроса, но и словоформ (мама/маме/мамы/маму ...)
  • находить не только слова, введенные пользователем, но и синонимы
  • ранжировать результаты поиска по релевантности
  • искать не только по атрибутам сущности, но и в теле файла, который с этой сущностью связан
  • выполнение сложных запросов (вроде "чтобы вот эти два термина находились поблизости", задание весов для слов в поисковом запросе и много чего еще)

Из описания можно заключить что скорее всего этот функционал все-таки в большей степени ориентирован на конечного пользователя (и скорее всего "продвинутого пользователя") и врядли будет использоваться в каких-то вспомогательных сервисах (хотя как знать ...). Теперь рассмотрим насколько сложно/просто будет все это сделать и отгрузить заказчику. Обратимся к следующим основным вопросам: изменения в архитектуре приложения, поддержка/сопровождение и альтернативы.

Архитектура

Здесь Microsoft потрудилась на славу: технология достается нам бесплатно не только в смысле денег (идет в комплекте с SQL Server и есть даже у SQL Server Express (with Advanced Services)) но и в смысле интеграции. Вся сложность ложится на плечи SQL Server-а:

  1. Создаем полнотекстовый каталог
  2. Создаем полнотекстовый индекс
  3. Используем специальные функции (CONTAINS, CONTAINSTABLE, FREETEXT, FREETEXTTABLE) для обращения к индексам из T-SQL кода

Таким образом, код приложения может быть полностью абстрагирован от того, каким образом получены данные: с применением полнотекстового поиска или без него. В обмен на эту простоту на стороне СУБД нас поджидает:

  1. Необходимость проектирования физического уровня: сколько каталогов нам нужно и где они будут храниться
  2. Логический уровень: какие индексы нам нужны, какие колонки индексировать
  3. Подробности алгоритмов обработки данных: стоп-список (noise words (в SQL Server 2005 - фиксированный, один на сервер, в SQL Server 2008 можно создавать пользовательские)), алгоритм разбиения на слова (word breaker), выделение словоформ (stammer), фильтрация содержимого (content filters)

Первые два уровня для начала/в несложных случаях, пожалуй, можно отдать на откуп SQL Server-у, а вот третий пункт таит в себе массу подводных камней, которые могут значительно усложнить жизнь "среднестатистическому разработчику":

  • стоп-список: слова, входящие в него, попросту игнорируются и не включаются в индекс (поэтому неудивительно, что найти их тоже не получится)
  • разбиение на слова: зависит от выбранного языка (локали), может быть нейтральным (по пробелам/знакам препинания)
  • выделение словоформ: зависит от языка, список доступных языков можно узнать из системного представления sys.fulltext_languages (зависит от версии SQL Server, в 2005м нужно было некоторые языки (в том числе и русский) регистрировать вручную). Если выберем "неправильный" язык (например в поле, индексируемом согласно правилам английского языка, будем хранить русский текст), то словоформы работать не будут - все слова будут искаться "как есть" (буквально). Проблемы также возникнут с синонимами и "похожестью" слов.
  • фильтрация содержимого: фактически дает возможность проиндексировать документы, хранящиеся в БД (в поле типа image/varbinary(max)). Хорошая новость заключается в том, что поддерживаются все форматы, которые "понимает" Windows, на которой выполняется SQL Server - нужно лишь задать специальное поле, в котором для данной строки будет храниться формат потока (фактически расширение файла).

Самая большая проблема, конечно же, заключается в правильной обработке мультилокальных данных (и в нашем глобальном плоском мире с каждым днем все меньше и меньше приложений, который могут обойтись поддержкой одного языка). Альтернатив целых две:

  1. Хранить данные для всех языков в одних и тех же полях вперемешку - проще всего, но теряется значительная часть функциональности
  2. Для каждого языка создавать отдельное поле/поля (например Title_EN, Title_RU и т.д.) - функциональность остается при нас, но придется довольно много "плясать" вокруг этих данных (в том числе и в приложении - чтобы правильно сохранить/загрузить)

Что бы мы ни выбрали в конечном итоге, за языковыми настройками индексации каждого поля (как и за collation ;-) ) надо следить.

Поддержка/сопровождение

Здесь хорошая новость заключается в том, что полнотекстовые каталоги являются частью базы данные и, как следствие, входят в состав резервной копии/восстанавливаются. Более сложным является вопрос поддержания полнотекстовых индексов в актуальном состоянии. Здесь есть два основных варианта:

  • автоматическое наполнение/обновление (CHANGE_TRACKING AUTO): сервер сам будет следить за актуальностью индексов - хорошее решение со многих точек зрения, но могут возникнуть некоторые неожиданные побочные эффекты, связанные с тем, что к нашей базе данных будет обращаться еще один (системный) процесс, накладывающий некоторое количество блокировок
  • наполнение/синхронизация индексов по запросу (CHANGE_TRACKING MANUAL): больше ответственности, но и больше уверенности в том что и когда мы делаем - может потребоваться в основном при большой загрузке, когда мы хотим максимально разгрузить сервер БД в течении рабочего времени и готовы смириться с некоторой степенью неактуальности информации в индексах

Довольно сложно дела обстоят и с мониторингом/оптимизацией производительности. Microsoft не разглашает внутреннюю структуру хранения полнотекстовых индексов поэтому на них не распространяется опыт оптимизации "обычных" индексов. Единственное, что можно сказать наверняка: чем больше индексов (в том числе и полнотекстовых) "навернуто" на таблицу, тем больше времени будут занимать операции добавления/обновления/удаления данных.

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

image

Выводы делаем сами (впрочем, я думаю и так понятно, что бесплатного в этом мире ничего не бывает).

Альтернативы

Если не рассматривать в качестве альтернативы переход на другую СУБД (в PostgreSQL, Oracle и MySql есть аналогичные технологии), то остается применение неких внешних по отношению к нашему приложению сервисов, производящих индексацию содержимого. Тут можно упомянуть:

  • Windows Search - встроен в Windows (следовательно "бесплатен"), позволяет индексировать файлы, хранящиеся на компьютере
  • Google Desktop - аналогичный продукт от Google
  • Apache Lucene / Solr - индексатор / поисковый сервер от Apache Foundation

Первые два продукта скорее ориентированы на индексацию файлов и подойдут скорее в том случае, если перед нами поставлена задача индексировать какие-то документы (довольно широкая тема), в то время как последний - именно внешний индекс, который может "втянуть" почти все, что угодно, физически представляет из себя веб-службу, поддерживает уйму разного функционала, распределенные индексы, очень гибко настраивается и много чего еще. Об этом чудо-средстве я планирую написать отдельный пост (а может быть и не один).

 

HTH

AlexS

вторник, 24 февраля 2009 г.

Использование и настройка псевдонимов (server alias)

Сегодня почти весь день (с перерывами на работу) пришлось заниматься "разруливанием проблем" с приложением на стороне заказчика. Один из сервисов все никак не хотел подключаться к SQL Server-у. Проблема усугублялась тем, что у нашей команды нет доступа к целевому окружению (вообще никакого доступа). Есть доступ только к индусам, которые в свою очередь имеют доступ к этому самому окружению. Причем "интерфейс" к индусам - электронная почта и Skype. После многочисленных советов "разобраться в конфигурации" и столь же многочисленных ответов "все точно так же, как и на стэйджинге", причина таки нашлась: на "проблемном" сервере не был сконфигурирован псевдоним (alias).

Мне сразу по приходу в этот проект не понравилось использование этой "фичи" - и вот опасения материализовались (правда это первый раз когда все было так серьезно). Не могу не признать, что в определенной степени alias - это удобно. Он, например, дает свободу в выборе сервера: можно переключиться с "общественного" на локальный меняя не строку подключения, а настройки псевдонима - т.е. действуя снаружи приложения. Причем псевдоним действует в рамках комьютера, т.е. (потенциально) можно влиять сразу на целый пакет приложений и сервисов. Побочный эффект: пока не посмотришь в настройки - не узнаешь куда же приложение "ходит" за данными. В целом я бы, наверное, избегал использования этой возможности.

Возник, однако, другой вопрос. В случае, когда на компьютере установлен SQL Server Client Tools (на машине разработчика есть с вероятностью 99%) все просто: запускаем SQL Server Configuration Manager, идем в раздел SQL Native Client Configuration, далее Aliases. А если этого пакета нет (на сервере - не редкость)? Как быть? Вариантов несколько:

  • "для слабаков": установить SQL Server Client Tools и получить SQL Server Configuration Manager
  • "для простых админов": в комплекте с клиентскими библиотеками SQL Server идет утилита cliconfg.exe (находится в папке Windows\systems32 так что можно вызывать из командной строки), в которой можно сконфигурировать не только псевдоним, но и много чего еще
  • "для хардкорных админов": воспользоваться Power Shell и SMO (используя объект Microsoft.SqlServer.Management.Smo.Wmi.ServerAlias) - правда для этого придется установить на компьютер PowerShell (в Server 2008 и новее есть всегда) и собственно SMO

Рекомендуемый вариант - "для админов". Или "для хардкорных админов" (возможно с вариациями) - если нужно чтобы настройка происходила автоматически и/или была повторяемой.

За деталями отсылаю к MSDN и документации по PowerShell. А еще рекомендую блог SQL Protocols - такой подробной информации о протоколах SQL Server и решении проблем с соединением вы больше нигде не найдете.

HTH

понедельник, 9 февраля 2009 г.

Об опциях соединения вообще и об IDENTITY_INSERT в частности

Сегодня пришлось "играться" с IDENTITY_INSERT. С удивлением обнаружил прочувствовал что это таки "установка уровня соединения". Не скажу, что это прямо так удивительно, в MSDN об этом явно сказано:

At any time, only one table in a session can have the IDENTITY_INSERT property set to ON. If a table already has this property set to ON, and a SET IDENTITY_INSERT ON statement is issued for another table, SQL Server 2000/2005/2008 returns an error message that states SET IDENTITY_INSERT is already ON and reports the table it is set ON for.

но как-то до сих пор я не особенно обращал внимание на это примечание.

Ну да ладно. Сама ситуация в очередной раз напомнила мне о том, что опции соединения - это вам не просто так и что за ними нужно следить. Главная проблема (с точки зрения программиста) с ними заключается даже не в том, как они инициализируются (это отдельная непростая тема), а в том, что они не сбрасываются при закритии соединения (точнее: при возвращении соединения в пул).

Для того, чтобы постигнуть "всю глубину наших глубин", рассмотрим такой сценарий. Мы выполняем некоторый SQL код и хотим повысить уровень его изоляции:

using(SqlConnection = new SqlConnection())

{

con.Open();

SqlCOmmand com = new SqlCommand("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; -- очень важный SQL код", con);

com.ExecuteNonQuery();

}

отлично. Все вроде бы довольны. Кроме того несчастного, которому в очередной раз попадется из пула именно это соединение с уровнем изоляции ... SERIALIZABLE. Для выполнения монстроидального запроса, объединяющего три-четыре-пять не самых маленьких таблиц. Приплыли ... ловить ошибки, возникающие в результате такого неосторожного обращения с опциями соединения, можно ооооочень долго.

Граждане! Будьте бдительны применяя опции соединения (особенно уровень изоляции).

HTH