Предыстория
Как-то раз для воспроизведения бага мне потребовался бэкап production-базы.
К моему удивлению я столкнулся со следующими ограничениями:
- Бэкап базы был сделан на версии SQL Server 2016 и не был совместим с моей SQL Server 2014.
- На моем рабочем компьютере в качестве ОС использовалась Windows 7, поэтому я не мог обновить SQL Server до версии 2016
- Поддерживаемый продукт был частью более крупной системы с сильно связанной легаси-архитектурой и также обращался к другим продуктам и базам, поэтому его развертывание на другой станции могло занять очень много времени.
Учитывая вышеизложенное, я пришел к выводу, что настало время
Восстановление данных из бэкапа
Я решил использовать виртуальную машину Oracle VM VirtualBox с Windows 10 (можно взять тестовый образ для браузера Edge отсюда). На виртуальную машину был установлен SQL Server 2016 и на нем из бэкапа была восстановлена база данных приложения (инструкция).
Настройка доступа к SQL Server на виртуальной машине
Далее было необходимо предпринять некоторые шаги, чтобы появилась возможность доступа к SQL Server извне:
- Для фаервола добавить правило пропускать запросы на порт 1433.
- Желательно, чтобы доступ к серверу шел не через windows-аутентификация, а через SQL по логину и паролю (проще настроить доступ). Однако в этом случае нужно не забыть включить в свойствах SQL Server возможность SQL-аутентификации.
- В настройках пользователя на SQL Server на вкладке User Mapping указать для восстановленной базы роль пользователя db_securityadmin.
Перенос данных
Собственно сам перенос данных состоит из двух этапов:
- Перенос схемы данных (таблицы, представления, хранимые процедуры и т.д.)
- Перенос самих данных
Перенос схемы данных
Выполняем следующие операции:
- Выбираем Tasks -> Generate Scripts для переносимой базы.
- Выбираем нужные для переноса объекта или оставляем значение по умолчанию (в этом случае будут созданы скрипты для всех объектов базы).
- Указываем настройки для сохранения скрипта. Удобнее всего сохранить скрипт в единый файл в кодировке Unicode. Тогда при сбое не понадобится заново повторять все шаги.
После сохранения скрипта его можно выполнить на исходном SQL Server (старой версии), чтобы создать требуемую базу.
Внимание: после выполнения скрипта необходимо проверить соответствие настроек базы из бэкапа и базы, созданной скриптом. В моем случае в скрипте отсутствовала настройка для COLLATE, что приводило к сбою при переносе данных и
Перенос данных
Перед переносом данных необходимо отключить проверку всех ограничений на базе:
EXEC sp_msforeachtable 'ALTER TABLE ? NOCHECK CONSTRAINT all'
Перенос данных осуществляем с помощью мастера импорта данных Tasks -> Import Data на SQL Server, где находится созданная скриптом база:
- Указываем настройки подключения к источнику (SQL Server 2016 на виртуальной машине). Я использовал Data Source SQL Server Native Client и вышеупомянутую SQL-аутентификацию.
- Указываем настройки подключения к месту назначения (SQL Server 2014 на хост-машине).
- Далее настраиваем маппинг. Необходимо выбрать все не read-only объекты (например, представления выбирать не нужно). В качестве дополнительных опций следует выбрать «Разрешить вставку в identity-столбцы», если такие используются.
Внимание: если при попытке выделить несколько таблиц и проставить им свойство «Разрешить вставку в identity-столбцы» свойство уже было ранее установлено хотя бы для одной из выделенных таблиц, в диалоге будет отмечено, что свойство уже установлено для всех выделенных таблиц. Данный факт может сбить с толку и привести к ошибкам переноса. - Запускаем перенос.
- Восстанавливаем проверку ограничений:
EXEC sp_msforeachtable 'ALTER TABLE ? CHECK CONSTRAINT all'
Если возникли какие-либо ошибки, проверяем настройки, удаляем созданную с ошибками базу, заново создаем ее из скрипта, вносим исправления и повторяем перенос данных.
Заключение
Данная задача встречается довольно редко и возникает только из-за вышеуказанных ограничений. Чаще всего решение заключается в обновлении SQL Server или подключению к удаленному серверу, если это позволяет архитектура приложения. Однако от легаси-кода и
Список использованных источников
- How do I deal with FK constraints when importing data using DTS Import/Export Wizard?
- The column «Column 2» cannot be processed because more than one code page (65001 and 1252) are specified for it.
- How can I connect to SQLServer running on VirtualBox from my host Macbook.
- SQL SERVER – Enable Identity Insert – Import Expert Wizard
- Troubleshooting Microsoft SQL Server Error 18456, Login failed for user
Комментарии (12)
gotch
13.06.2019 11:47Я решил использовать виртуальную машину Oracle VM VirtualBox с Windows 10 (можно взять тестовый образ для браузера Edge отсюда). На виртуальную машину был установлен SQL Server 2016 и на нем из бэкапа была восстановлена база данных приложения (инструкция).
Вот на этом и стоило остановиться. А вдруг баг вопроизводится только на родной платформе.DrPass
13.06.2019 12:04В случае с MS SQL чаще бывает наоборот. Количество говнозапросов, которые успешно переваривает 2016, больше, чем те, с которыми справляются 2014, 2012 и 2008.
fadeinmad Автор
13.06.2019 12:12Такая вероятность была минимальна. Основное подозрение было на сами данные в базе. Некий неучтенный кейс поведения приложения, возможно нарушающий консистентность данных. Остановиться на Server 2016 я не мог, так как для базы требовалось дополнительное окружение (другие базы, сильная связанность данных), настройка которого потребовала бы гораздо большего времени.
Однако, в статье описан не сам поиск бага, а способ, как можно вытащить данные из бэкапа новой версии на сервер более старой версии. Поиск бага — всего лишь причина, по которой пришлось это сделать.
Поводов для использования этого способа немного, но они могут возникнуть. И, как написано в заключении, надеюсь, что это никому не понадобится.
speshuric
13.06.2019 20:35Как обрабатываются поля с timestamp?
Как обрабатываются триггера? А DDL? А Logon?
Как переносить BLOBы? А с учетом хранения в filestream?
Нормально ли обрабатываются дурацкие set ansi nulls off?
Что делать, если объекты содержат синтаксические ошибки?
… И еще вагон граблейfadeinmad Автор
14.06.2019 17:20Перенос с учетом локализации, согласен, довольно сложный. Однако я заострил внимание на скрипте генерации базы. В него необходимо внести дополнительные настройки, если это потребуется.
Насчет остального, опять же, я описал основное направление и те проблемы, с которыми столкнулся. Если кто-то поделится своим опытом и дополнит эту инструкцию, будет просто замечательно.
W001fer
14.06.2019 17:15В принципе, описан единственный способ скопировать базу с новой версии скуля на более старую. Был в свое время такой опыт — разработка велась на версии 2008, а боевая площадка была на 2005, страшно матерились постоянно по этому поводу :) То разработчики «забудут», что каких-то инструкций в 2005 нет и приходилось переделывать, то вот так же надо быстро базу с дева в бой запустить. Весело было. В целом все верно, забыли только хранимые процедуры, они таким методом не переносятся, их надо из студии Create To и вперед :)
fadeinmad Автор
14.06.2019 17:22Хранимые процедуры переносятся: код их создания будет присутствовать в скрипте генерации базы. Есть возможность выгрузки только скриптов хранимых процедур.
Проверено для Sql Server 2014 и Microsoft SQL Server Management Studio 12.0.W001fer
14.06.2019 17:47Эххх, совсем старый стал, отстал от жизни :) В 2008 часть БД, которая относится к Programmability не переезжала в процессе генерации скрипта создания базы, в современных не проверял, всегда придерживаюсь правила, что дев, тест и прод — одной версии. Во избежание, так сказать.
osipov_dv
по-моему про права и пользователей забыли, или ваш замечательный legacy, работает как это «принято» из под sa?
и еще, вот так вы включаете обратно ограничения, но чтобы они стали trusted надо делать
Восстанавливаем проверку ограничений:
EXEC sp_msforeachtable 'ALTER TABLE ? CHECK CHECK CONSTRAINT all'
DrPass
У него же в задаче не миграция продакшен-сервера на предыдущую версию, а просто поднятие бэкапа на более старом серваке. Я, например, со вторым кейсом встречался не раз, а необходимости в первом никогда не видел, и даже не представляю, зачем оно могло бы понадобиться.
fadeinmad Автор
Да, именно второй кейс и был описан. В данной задаче требовались только данные с продакшен-сервера, чтобы найти причину возникновения бага. Сам сервер так и продолжил работу, как и раньше. А воспроизведение ситуации для поиска ошибки велось уже на компьютере с локально установленной базой, на которой ты сам себе админ. Поэтому проблем с правами доступа не было.
osipov_dv
если в базе с правами все сложно, то у вас тупо даже хранимки будут выдавать не то что ожидается. Это не поднятие бэкапа, а просто перенос данных.