Синхронизация структуры базы данных между приложениями. Сравнение программ для синхронизации сетевых баз данных. Бесплатная программа для синхронизации

Приложения, используемые торговыми представителями или другими работниками на местах – курьеры службы доставки, выездные медсёстры, сотрудники клининговых компаний и т.д., предназначены для сбора информации на местах с последующей отправкой её в центр обработки данных. Кроме того, периодически необходима обратная связь, т.е. отправка данных обратно от центра обработки к конкретным исполнителям на местах.

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

В любом подобном сценарии использования основная потребность – это постоянный обмен данными между мобильными устройствами на местах и общим хранилищем данных.

Как можно решить подобные задачи

1. Репликация SQL Server. С помощью репликации вы можете осуществлять синхронизацию удалённых баз данных между собой путём копирования данных. Но у неё (репликации SQL Server) есть ряд ограничений:

  • Только более дорогие редакции SQL Server Standard и Enterprise могут выступать в роли издателя (Publisher), поэтому если вы используете бесплатную редакцию SQL Express, то вам придётся обновиться.
  • Конфигурация из издателя (Publisher) SQL Server 2005 и подписчика (Subscriber) SQL Server 2008 не поддерживает web-репликацию.
  • Версия распространителя (Distributor) должна быть строго равна или выше версии издателя (Publisher).
  • В репликации транзакций (Transactional Replication) версия подписчика (Subscriber) не должна отличаться от издателя (Publisher) больше, чем на две версии SQL Server, т.е. издатель SQL Server 2000 не может работать с подписчиком SQL Server 2012.
  • В репликации слиянием (Merge Replication) версия подписчика (Subscriber) должна быть меньше или равна версии издателя (Publisher), т.е. подписчик SQL Server 2012 не сможет работать с издателем SQL Server 2008.

2. Sync Framework – API, которое позволяет создавать приложения для синхронизации данных между разными базами данных. Эта платформа более гибкая для решения подобных задач, но требует дополнительной разработки, что повышает стоимость и сроки реализации.

3. ApexSQL Data Diff – инструмент SQL Server для поиска различий в данных и синхронизации их между собой. Этот инструмент позволяет находить различия не только между базами данных, но и анализировать резервные копии БД, как обычные, так и сжатые. В результате анализа можно получить подробный отчёт о найденных расхождениях и файл синхронизации, который можно выполнить.

Как это сделать

Представьте себе, что у вас есть единое хранилище данных больницы (Central) и базы данных посещения (Visits) на мобильном устройстве медсестры. В больнице создаются записи в таблице посещения (Visits) и каждое утро медсёстры синхронизируют свои устройства, чтобы узнать свой маршрут на предстоящий день. В течении дня медсёстры на мобильных устройствах желают пометки о посещениях в таблице VisitReports.

Для обратной синхронизации всех изменений, которые совершили медсёстры в течении дня необходимо выполнить похожие действия:


Теперь рассмотрим, как автоматизировать процесс синхронизации, при наличии проектов синхронизации.

Для вечерней синхронизации повторите шаги с 3 по 9, чтобы создать ещё одно задание.

С помощью приложения ApexSQL Data Diff вы можете легко настроить синхронизацию ваших удалённых БД SQL Server с центральным хранилищем. При этом вы не привязаны к какой-то конкретной версии или редакции SQL Server, от вас не потребуется дополнительного времени на разработку, при этом вы осуществляете полный контроль над всеми вашими данными до единой записи.

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

Чаще всего используется простой подход - создание набора SQL-скриптов для модификации структуры БД от версии к версии. Конечно, есть такой мощный инструмент, как Red gate , но он во-первых небесплатный, во-вторых не решает проблему полной автоматизации обновления.


Технология migrations, впервые появившаяся в ОРМ Hibernate и реализованная в Linq, очень хороша и удобна, но подразумевает стратегию разработки структуры БД code first, что весьма трудоемко для уже существующих проектов, а использование в БД триггеров, хранимых процедур и функций делает задачу перехода на code first практически невыполнимой.


В данной статье предлагается альтернативный подход к решению этой задачи, использующий хранение эталонной структуры БД в XML-файле и автоматическую генерацию SQL-скрипта на основе сравнения эталонной и существующей структуры. Итак, начнем...

Генерация XML-файла со структурой БД

Для экспериментов будем использовать БД DbSyncSample. Скрипт для создания БД приведен ниже.


USE GO /****** Object: Table . Script Date: 06/01/2017 10:37:43 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE .( IDENTITY(1,1) NOT NULL, (50) NULL, NULL, (18, 2) NOT NULL, CONSTRAINT PRIMARY KEY CLUSTERED ( ASC)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON ) ON GO CREATE NONCLUSTERED INDEX ON . ( ASC)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON GO /****** Object: Table . Script Date: 06/01/2017 10:37:43 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE .( IDENTITY(1,1) NOT NULL, (150) NULL, NULL, (18, 2) NOT NULL, CONSTRAINT PRIMARY KEY CLUSTERED ( ASC)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON ) ON GO /****** Object: Trigger Script Date: 06/01/2017 10:37:43 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TRIGGER . ON . AFTER INSERT,UPDATE AS BEGIN UPDATE Orders SET TotalCost = s.Total FROM (SELECT i.OrderId OId, SUM(d.Cost) Total FROM Details d JOIN inserted i ON d.OrderId=i.OrderId GROUP BY i.OrderId) s WHERE Id=s.OId END GO /****** Object: Trigger Script Date: 06/01/2017 10:37:43 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TRIGGER . ON . AFTER DELETE AS BEGIN UPDATE Orders SET TotalCost = s.Total FROM (SELECT i.OrderId OId, SUM(d.Cost) Total FROM Details d JOIN deleted i ON d.OrderId=i.OrderId GROUP BY i.OrderId) s WHERE Id=s.OId END GO /****** Object: Default Script Date: 06/01/2017 10:37:43 ******/ ALTER TABLE . ADD CONSTRAINT DEFAULT ((0)) FOR GO /****** Object: Default Script Date: 06/01/2017 10:37:43 ******/ ALTER TABLE . ADD CONSTRAINT DEFAULT ((0)) FOR GO /****** Object: ForeignKey Script Date: 06/01/2017 10:37:43 ******/ ALTER TABLE . WITH CHECK ADD CONSTRAINT FOREIGN KEY() REFERENCES . () GO ALTER TABLE . CHECK CONSTRAINT GO

Для экспериментов создаем консольное приложение. Подключаем к нему nuget-пакет Shed.DbSync .


Структуру БД в виде XML получаем следующим образом:


class Program { private const string OrigConnString = "data source=.;initial catalog=FiocoKb;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework"; static void Main(string args) { // получаем XML со структурой БД var db = new Shed.DbSync.DataBase(OrigConnString); var xml = db.GetXml(); File.WriteAllText("DbStructure.xml", xml); } }

После запуска программы в файле DbStructure.xml видим следующее:


0 1 int 4 false true false 2 nvarchar 100 true false false 3 datetime 8 true false false 4 decimal 9 false false false 1 CLUSTERED true true false 1 1 false 2 NONCLUSTERED false false false 2 1 false 1 4 ((0))
1 int 4 false true false 2 nvarchar 300 true false false 3 int 4 true false false 4 decimal 9 false false false 1 CLUSTERED true true false 1 1 false 1 2137058649 1 3 1 NO_ACTION NO_ACTION 4 ((0))
CREATE TRIGGER . ON dbo.Details AFTER INSERT,UPDATE AS BEGIN UPDATE Orders SET TotalCost = s.Total FROM (SELECT i.OrderId OId, SUM(d.Cost) Total FROM Details d JOIN inserted i ON d.OrderId=i.OrderId GROUP BY i.OrderId) s WHERE Id=s.OId END SQL_TRIGGER CREATE TRIGGER . ON dbo.Details AFTER DELETE AS BEGIN UPDATE Orders SET TotalCost = s.Total FROM (SELECT i.OrderId OId, SUM(d.Cost) Total FROM Details d JOIN deleted i ON d.OrderId=i.OrderId GROUP BY i.OrderId) s WHERE Id=s.OId END SQL_TRIGGER

Разворачивание/обновление структуры БД при помощи полученного XML.

Теперь научимся использовать полученный XML. Создаем еще одну пустую БД DbSyncSampleCopy, в код нашей консольной программы добавляем следующее:


class Program { private const string OrigConnString = "data source=.;initial catalog=DbSyncSample;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework"; private const string TargetConnString = "data source=.;initial catalog=DbSyncSampleCopy;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework"; static void Main(string args) { // получаем XML со структурой эталонной БД var dborig = new Shed.DbSync.DataBase(OrigConnString); var xml = dborig.GetXml(); File.WriteAllText("DbStructure.xml", xml); // если нужно предварительно очистить структуру целевой БД, используем // Shed.DbSync.DataBase.ClearDb(TargetConnString); // обновляем структуру целевой БД var dbcopy = Shed.DbSync.DataBase.CreateFromXml(xml); dbcopy.UpdateDb(TargetConnString); // на самом деле можно обойтись одной строкой: // dborig.UpdateDb(TargetConnString); // dbcopy создаем только для демонстрации создания объекта базы из XML } }

После запуска программы можно убедиться, что в DbSyncSampleCopy появилась структура таблиц, идентичная эталонной БД. Эксперименты с изменением эталонной структуры и обновлением целевой оставляю читателю.


В сценариях тестирования может понадобиться создание тестовой БД каждый раз с нуля. В этом случае будет полезно использовать функцию Shed.DbSync.DataBase.ClearDb(string connString)

Автоматическое слежение за структурой БД.

Слежение за структурой оформим в виде отдельной функции, которую следует вызывать при старте/рестарте приложения либо в другом месте по желанию разработчика.


static void SyncDb() { // автоматическое слежение за структурой БД Shed.DbSync.DataBase.Syncronize(OrigConnString, @"Struct\DbStructure.xml", // путь к файлу структуры @"Struct\Logs", // путь к папке логов синхронизации @"Struct\update_script.sql" // (необяз.) в случае определения этого параметра // в него будет записан скрипт, сгенерированный // для обновления БД); }

Слежение производится при помощи параметра (тега) Version в XML. Сценарий использования процедуры такой:

  1. Назначить версию БД. В Microsoft SqlServer Management Studio на узле нужной базы данных правой кнопкой выбрать Properties.
  2. Далее Extended Properties и в таблице свойств добавить свойство Version со значением 1. При каждой последующей модификации структуры это свойство следует наращивать на 1.
  3. При запуске приложения, если XML-файла нет или его версия меньше, чем у БД, он создается.
  4. Если версия XML-файла больше, чем у БД, генерируется скрипт на обновление БД и исполняется.
  5. Если в процессе исполнения скрипта возникли ошибки, все изменения откатываются.
  6. Результаты синхронизации пишутся в log-файл, создаваемый в папке, указанной параметром logDitPath.
  7. Если указан параметр SqlScriptPath, создается файл со скриптом из п.4.

Эксперименты оставляю читателям. Успехов вам!

Теги:

  • ms sql
  • синхронизация баз данных
  • database
  • syncronize
  • dbsync
  • sql
  • shed
  • shed.dbsync
Добавить метки

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

1. PHP SQLDIFF, a.k.a. SQLDiff

PHP-скрипт, позволяющий увидеть полные различия (как в структуре, так и в данных) между любыми таблицами двух БД. В инструменте отсутствуют какие-либо средства по автоматической синхронизации структуры или данных – предоставляется лишь визуальная информация. Еще из существенных недостатков – возможность подключения только к БД, к которым возможен доступ напрямую (не через ssh-тоннель). Медленная скорость работы на больших объемах данных (работа через pear-модуль, который не блещет ни новизной, ни скоростью). Считаю данный скрипт весьма полезным для разработчика в случаях, когда необходимо понимание и визуальное представление различий между разными таблицами - имеет удобный интерфейс, быстрая настройка. Охарактеризую скорее как полезную карманную утилиту для быстрого получения понимания о рассинхронизации таблиц, которые в теории должны быть идентичны, нежели как серьезный инструмент, который можно применить для автоматизации процессов синхронизации.

2. LIQUIBASE

Удобный многофункциональный и простой в использовании мигратор структуры БД на java. Вижу для себя в этом плюс, если использовать в связке с Jenkins.
Пример (host1 - сервер, с которого необходимо копировать структуру БД; host2 - сервер, на который необходимо перенести структуру с host1):

Java -jar liquibase.jar --driver=com.mysql.jdbc.Driver --classpath=mysql-connector-java-5.1.xx-bin.jar --logFile=db.ExampleChangelog.xml --url="jdbc:mysql://host2" --defaultSchemaName=db_name --username=username --password="password" --referenceUrl=jdbc:mysql://host1 --referenceUsername=username --referencePassword="password" diffChangeLog > ChangeSet.xml

Формирует changeset в формате xml, дальнейшая миграция которого приводит структуру бд на host2 в состояние, идентичное host1.

Запуск миграции:

Java -jar liquibase.jar --driver=com.mysql.jdbc.Driver --classpath=/path/to/classes --changeLogFile=ChangeSet.xml --url="jdbc:mysql://host2" --username=user --password="password" migrate

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

Changeset также можно формировать и в других форматах - в sql, в json (не только в xml). Формирование changeset"а в sql будет полезным в тех случаях, когда для миграции используются средства другой утилиты.

3. schemasync

Инструмент для синхронизации структуры БД. Для работы необходим python и соответствующий интерфейс для mysql.
Из существенных различий с liquibase:
- schemasync создает не только ченжсет, но и файл, позволяющий откатить изменения (самое важное и самое ценное преимущество, хотя, на мой взгляд, не избавляет от необходимости делать backup перед синхронизацией)
- liquibase позволяет не только получить ченжсет, но и сразу же запустить миграцию средствами самой утилиты. Может быть, не киллер-фича, но все равно удобно и полезно

schemasync работает только с sql – никаких промежуточных xml и аналогов – вижу для себя в этом как преимущества, так и недостатки.
Очень лаконичный синтаксис, минимум настроек. Позволяет не синхронизировать комментарии и автоинкремент (настраивается) - безусловный плюс.

Пример использования:

Schemasync mysql://user:pass@dev-host:3306/dev_db mysql://user:pass@prod-host:3306/production_db

  • Сергей Савенков

    какой то “куцый” обзор… как будто спешили куда то