Header RSS-подписка на обновления сайта eMail-подписка на обновления сайта

Версия сервера, версия базы данных и уровни совместимости. Часть 1/4.

Тему статьи предлагаемую сегодня вашему вниманию подсказали вы сами, уважаемые читатели блога sqlCMD.ru. Вообще, как оказалось, блог, помимо прочих своих плюсов, это еще и мощный «генератор» e-посланий, причем посланий по делу. Доля спама-рекламы не превышает 0.5% (может пока везет, может блог еще молод — не знаю), остальное — вполне «годные» вопросы (подчас очень интересные), предложения, замечания по опубликованным материалам, благожелательная критика, просто благодарности... Кстати, пользуясь случаем, хочу еще раз поблагодарить всех участвующих в жизни блога — спасибо за ваши письма/комментарии к статьям, автору важно знать ваше мнение! Так вот, в блоге сама-собой возникла такая добрая традиция: если в течении короткого промежутка времени приходит несколько писем спрашивающих, по сути, об одном и том же, то автор вместо персональных ответов подготавливает общую ответ-статью и публикует ее для общего доступа, дабы в будущем отвечать на аналогичные вопросы простой ссылкой. Так и случилось на этот раз: за прошедшую пару недель автор получил три однотипных письма-вопроса. Максимально сжато суть проблемы в них излагаемых можно выразить такой парой предложений:

Есть база созданная на сервере версии Y, но самого сервера той же версии Y у автора вопроса под рукой нет. Есть лишь сервер версии X, причем X<Y, то есть оригинальная база создавалась на более свежей версии сервера. Почему попытки подключить (attach), а равно восстановить из резервной копии такую базу на сервер версии X обречены на провал?

Без проблем припомнив, что он видел на форумах тот же, по сути, вопрос в десятках (и это не преувеличение) различных редакций, автор без колебаний пришел к выводу о необходимости полноценной статьи по данному вопросу.

В традициях блога sqlCMD.ru мы подойдем к нему основательно и всесторонне, начиная от генеральной идеи приведшей к ограничениям в переносе баз между серверами (даже несмотря на то, что и сервер исходный, и сервер целевой являются представителями одной и той же платформы —SQL Server) и заканчивая низкоуровневой реализацией этой идеи на, буквально, байтовом уровне. Для такой основательной проработки нам потребуется осветить три связанных, но определенно не идентичных вопроса:

  • что есть версия сервера и что она определяет;
  • что есть версия базы данных и что она определяет;
  • что есть уровень совместимости (compatibility level) базы данных и как он связан с двумя предыдущими определениями (если, конечно, он вообще с ними связан).

Строго говоря, для ответа именно на вопрос(-ы) читателей блога третья тема является избыточной, но уж очень плотно она «прилегает» к темам основным. Жаль было бы упустить такую возможность и не расширить рамки ответа совсем чуть-чуть, дабы читатель мог представить себе картину взаимосвязей всех упомянутых сущностей в значительно более полном их варианте. Автор решил такую возможность не упускать, тем более что по его наблюдениям, часть DBA знающих, что помимо версии сервера существует еще, оказывается, версия базы (а знают об этом, поверьте, отнюдь не 100% тех самых DBA), так вот, даже такие осведомленные господа нет-нет, да спутают версию базы и уровень ее совместимости. А стало быть эта «добавочная» тема призвана, помимо прочего, внести стройность в их понимание «SQL-мира».

Начнем мы с того, что научимся просто просматривать номера версий/уровней, причем несколькими способами. Параллельно этому мы будем рассуждать о влиянии каждого из трех чисел на возможности системы, то есть ответим на вопрос за что отвечает каждое из упомянутых чисел. Ну и завершим статью самым, наверно, интересным разделом — о совместимости этих трех чисел между собой. Собственно, понимание этой самой совместимости и будет ответом на исходный вопрос приведший к появлению данной заметки. Поехали!

Версия сервера.


366bef3a

Этот раздел самый понятный, очевидный, а потому и самый короткий. SQL Server ведет свою «родословную» с версии, как ни странно, 1.0. Возможно вы думали, что такой «эпический» продукт должен был начинаться с чего-то возвышенно-романтического, «версия Double Zero» или типа того... Ан нет! Банальные один-точка-ноль. Впрочем дело происходило в аскетичном 89-м году прошлого века, да и версия сервера была под OS/2, да и кто внес больший вклад в написание кода той версии — Microsoft, или Sybase, или Ashton-Tate — неясно (хотя это и была компания идущая в последнем списке под номером два; но это так, реплика в сторону). До романтики ли было... В любом случае, поскольку жанр нашей статьи «техническая литература», а не «историческая беллетристика», нам все эти непростые взаимоотношения сначала партнеров, а затем конкурентов неинтересны. Просто констатируем факт: была версия 1.0. Затем 1.1, затем (пару версий опускаю) 6.0 (почти без участия Sybase), затем 7.0 (абсолютно без участия Sybase), 2000-я... И вот на дворе 2012-й год с одноименной же версией сервера.

Нас же, во всех этих «этапах большого пути» интересует только один вопрос: что, с технической точки зрения, означало появление очередной версии сервера? И хотя, как кажется автору, ответ самоочевиден — зафиксируем его для будущих референций:

  • новая версия — это новые возможности. То есть «фичи», выражаясь языком IT-субкультуры. Это, конечно, главное отличие версии от версии. Быстрый пример: секционированные таблицы, а заодно и индексы, они же partitioned tables/indexes. До версии 2005-й их не было в принципе, а начиная с нее — пожалуйста. В некоторой степени тем же «разделителем фич» являются различные редакции одной и той же версии и, в еще меньшей степени, сервис-паки одной версии и одной редакции. Но, понятно, что самые «мощные» новые возможности, причем в большом объеме, появляются именно в каждой новой версии, а не редакции/сервис-паке. Так что именно новые версии (и то не все, как будет показано далее) являются главными контрольными точками в которых случаются качественные скачки в развитии продукта;
  • новая версия — это новый синтаксис (или, более корректно — новые элементы синтаксиса) языка общения с продуктом, коим, как все хорошо знают, является язык с гордым именем T-SQL. Часть новых синтаксических элементов просто не может не появиться в силу первого пункта: если бы в 2005-м сервере не появились новые операторы CREATE PARTITION FUNCTION/CREATE PARTITION SCHEME и плюс к тому не изменился синтаксис оператора CREATE TABLE — как бы мы смогли воспользоваться новой «фичей» секционированных таблиц? Но часть новых синтаксических элементов не открывает доступ к новому функционалу платформы, а появляется по причине более банальной — «так удобнее». В том же 2005-м сервере появились обобщенные табличные выражения (common table expression, CTE) не несущие, строго говоря, нового функционала, но зато страшно повышающие читабельность исходников (разумеется, при условии их применения с толком и к месту), а значит упрощающие поддержку готовых решений.

Итак, красивая формула, нарисовавшаяся у нас к текущей точке повествования: новая версия сервера=новые «фичи» + добавления/улучшения в T-SQL. Коротко и ясно.

В настоящее время формат обсуждаемой версии совершенно «устаканился» и обрел типичную для Microsoft (да и вообще для IT-индустрии) четырехкомпонентную структуру: major.minor.build.revision. То есть версия сервера есть ничто иное как четыре числа записанных через точку, причем количество знаков в каждом из этих чисел варьируется от одного до четырех. Первые два числа (major.minor) могут меняться только с выходом именно «новой версии», т.е. программного продукта имеющего собственное коммерческое имя. SQL Server 2005, SQL Server 2008 R2, SQL Server 2012 — это все примеры коммерческих имен. Ясно, что major.minor меняются зело не часто, определенно реже чем раз в год, обычно — значительно реже. Бывает и так, что они «замораживаются» лет на пять, как это было между версиями 2000-й и 2005-й. Интересно отметить, что история знает лишь одну версию SQL Server, когда вторая часть этой пары (minor) была не равна 00. А именно, в сервере 2008R2 она равнялась 50-ти, но это единственное исключение за долгую историю продукта.

Вторая же пара, build.revision, напротив — значительно более «мобильны» и, бывает, успевает смениться пару раз в течении месяца. Это потому, что любой, самый мелкий «фикс», «апдейт», и я уж молчу про сервис-пак приводят к смене значений в этой паре. Иными словами, если код самого сервера (то есть программный код движка) изменился хотя бы на байт — изменилось и сочетание build.revision. Любопытно, что в этой последней паре менее значимый компонент (revision) практически не используется и просто «мешается под ногами». Дело в том, что любое изменение (даже самое незначительное) приводит к изменению части build и роль revision становится исчезающе мала, разве что отмечать «внутрифирменные» сборки продукта никогда не попадающие к конечным потребителям вроде нас с вами. В силу этих причин 99% номеров версий сервера содержит в этой завершающей части просто 0. Исключения встречаются (скажем «голый», без единого «фикса», SQL Server 2008 R2 имеет номер версии 10.50.1600.1), но число таких исключений микроскопично. Именно поэтому с практической точки зрения тот сервер за которым вы работаете однозначно идентифицируется первыми тремя числами — 10.50.1600. Никаких разночтений быть не может, это у нас SQL Server 2008 R2, RTM (release to manufacturing), «без всего». Первый же вышедший после RTM апдейт для R2 «поднял» значение build до 1617.

Вам, конечно же, страшно интересно знать — а вот как, к примеру, догадаться, что 10.50.1600 это именно SQL Server 2008 R2, именно RTM, да еще «без всего»? То есть как соотнести цифры с коммерческими именами продукта да еще с, возможно, «фиксами»/«апдейтами»/сервис-паками? По счастью, тут нет никаких проблем. Все это строжайшим образом документируется и официально выкладывается в сеть. Персонально автор для решения поставленной задачи соотнесения одного с другим находит крайне полезным один сайт, специально созданный для решения этого единственного вопроса. А именно — вот этот сайт. Все просто: открываете → поиск по странице (в браузере) → готово! Искать можно как коммерческое имя по номеру так и в обратную сторону. Очень полезно, крайне рекомендую показанную ссылку для «мемориз». Обратите внимание, что номера версий указываются в «практичной», а не в формальной нотации, то есть без revision, см. первый столбик Build. Причину этого мы уже обсудили — завершающая часть номера версии не может сообщить нам никакой дополнительной информации и является откровенно избыточной.

Наконец — как посмотреть версию (все эти major.minor.build.revision) конкретного работающего сервера (а более формально говоря — конкретного экземпляра)? Ну это-то известно, полагаю, совершенно всем читателям данных строк, фразу «а-что-нам-вернет SELECT @@VERSION?» не заметить на любом, хоть сколько-то посещаемом SQL-форуме просто невозможно, настолько часто она там повторяется. Действительно, встроенная функция @@VERSION вернет нам длинную строку текста где, среди прочего, будет содержаться искомая четырехкомпонентная структура:

Microsoft SQL Server 2008 R2 (SP1) - 10.50.2500.0 (X64)   Jun 17 2011 00:54:03   Copyright (c)...

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

НЕ пытайтесь извлечь эту информацию (версию сервера) из пункта меню Help → About в Management Studio! Так вы только узнаете какой версии у вас само приложение(студия). Интересующий вас инстанс может иметь версию совершенно иную. Вместо этого подключитесь к целевому инстансу и выполните на нем код приведенный ниже.

Все ли понятно? Отлично, переходим к версии второй, той что относится к базе данных.

Версия базы.

Строго говоря, технически более корректно назвать исследуемую в текущем разделе сущность версией «первичного файла данных», или, более коротко, версией mdf-файла. Но так уж повелось, что устоялся термин «версия базы», коего мы и будем придерживаться в данной заметке. В конце концов, суть не в бирке, а в той вещи к которой она прикреплена — вот с этой самой вещью и давайте разберемся.

Первое и самое важное — зачем нам это? Нельзя обойтись без умножения сущностей коих и так в SQL Server вагон с прицепом? В данном случае, увы, никак не обойтись — нам всем нужна эта вторая версия, версия базы. Дело в том, что те самые новые «фичи» за которые мы так любим новые версии сервера, требуют поддержки с двух сторон. Во-первых, что самоочевидно, для этого должен измениться код движка сервера. Это, как мы знаем, приведет к смене значения части build в номере его версии, и это как минимум, а вообще могут и major/minor увеличиться. Но, во-вторых, для некоторых «фич» (не для всех, но для изрядной части) должен быть изменен формат (структура) mdf-файла. Снова прибегнем к примеру с partitioned tables/indexes. До 2005-й версии файлы данных не нуждались ни в чем для обслуживания этой «фичи» в виду полного отсутствия последней. Не было разделов этого файла куда «складывались» различные секции (partition) относящиеся к одной (или разным) таблицам, не было заголовков этих разделов «объясняющих» к какой именно таблице относится данная секция и т.д. Много чего не было и структура файла была много, много проще. В версии 2005-й она, структура эта, многократно усложнилась, что, как вы понимаете, является неизбежным — нельзя двигаться вперед оставаясь на месте. Однако сервер любой версии и база данных созданная на нем с помощью команды CREATE DATABASE (а оригинальной базе взяться больше и неоткуда) не являются неотделимыми участниками IT-инфраструктуры. Иными словами, никто и ничто не может помешать вам взять mdf-файл (а равно его резервную копию) появившийся на свет на сервере версии 9.00.xxx.x (то есть любая под-версия 2005-го сервера), и попытаться подключить (attach) его (а равно восстановить из резервной копии) на сервере версии 8.00.xxx.x (то бишь 2000-й). И последний не должен 5 минут пытаться разобраться в формате понять который ему по определению не суждено (как же 2000-й сервер «поймет» про секции если его не программировали на это?), а должен максимально корректно и информативно сообщить администратору что попытка обречена и, идеально, подсказать ему минимальную версию сервера при котором успех возможен (опустим варианты «битых» mdf/bak файлов, будем считать их гарантировано целыми).