Обеспечение надежности при синхр-ии таблиц БД чере вебсервис
От: MozgC США http://nightcoder.livejournal.com
Дата: 26.11.07 19:05
Оценка:
Добрый день,
Есть центральная локальная БД (главная), есть БД с похожей структурой на сайте. Необходимые обновления (например обновления или расширение прайс-листа) шлются из центральной БД на удаленную через веб-сервис. Шлю партиями, например по 1000 строк за раз.

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

Мне предложили сделать версионность, т.е. на удаленной БД в таблице вводится колонка version, строки теперь только добавляются (а не добавляются и обновляются) с выставлением поля version на 1 больше версии при последнем удачном апдейте. В случае возникновения ошибки — производим откат, т.е. удаляем все строки с нужным номером версии. При выборке же данных (например показе прайс-листа на сайте) выбираем строку с максимальной версией, т.е. например в случае mysql так:

SELECT * FROM price WHERE ... ORDER BY version DESC LIMIT 1

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

Вопрос:
Может кто-то предложит более лучший (по каким-либо параметрам) подход к обеспечению надежности синхронизации?

Заранее спасибо.
Re: Обеспечение надежности при синхр-ии таблиц БД чере вебсе
От: Mikhail Polykovsky Россия  
Дата: 26.11.07 19:18
Оценка:
MC>Мне предложили сделать версионность, т.е. на удаленной БД в таблице вводится колонка version, строки теперь только добавляются (а не добавляются и обновляются) с выставлением поля version на 1 больше версии при последнем удачном апдейте. В случае возникновения ошибки — производим откат, т.е. удаляем все строки с нужным номером версии. При выборке же данных (например показе прайс-листа на сайте) выбираем строку с максимальной версией, т.е. например в случае mysql так:

По-моему, вполне рабочий вариант. Можно его еще слегка упростить. Заводим поле status с двумя значениями — ready и new
1) Заливаем данные, указывая статус new
1.5) в случае ошибки удаляем их по статусу
2) Одной транзакцией (ваша БД умеет транзакции?) стираем данные со статусом ready и меняем статус оставшихся данных с new на ready

Если транзакции нельзя, можно слегка усложнить алгоритм.
Re[2]: Обеспечение надежности при синхр-ии таблиц БД чере ве
От: MozgC США http://nightcoder.livejournal.com
Дата: 26.11.07 20:25
Оценка:
Не понял второй пункт...
Re[3]: Обеспечение надежности при синхр-ии таблиц БД чере ве
От: Mikhail Polykovsky Россия  
Дата: 27.11.07 04:15
Оценка:
Здравствуйте, MozgC, Вы писали:

MC>Не понял второй пункт...


START TRANSACTION
DELETE FROM Table WHERE status='ready'
UPDATE Table SET status='ready'
COMMIT
Re[2]: Обеспечение надежности при синхр-ии таблиц БД чере ве
От: MindWrapper  
Дата: 27.11.07 11:24
Оценка:
Здравствуйте, Mikhail Polykovsky, Вы писали:

MC>>Мне предложили сделать версионность, т.е. на удаленной БД в таблице вводится колонка version, строки теперь только добавляются (а не добавляются и обновляются) с выставлением поля version на 1 больше версии при последнем удачном апдейте. В случае возникновения ошибки — производим откат, т.е. удаляем все строки с нужным номером версии. При выборке же данных (например показе прайс-листа на сайте) выбираем строку с максимальной версией, т.е. например в случае mysql так:


MP>По-моему, вполне рабочий вариант. Можно его еще слегка упростить. Заводим поле status с двумя значениями — ready и new

MP>1) Заливаем данные, указывая статус new
MP>1.5) в случае ошибки удаляем их по статусу
MP>2) Одной транзакцией (ваша БД умеет транзакции?) стираем данные со статусом ready и меняем статус оставшихся данных с new на ready

MP>Если транзакции нельзя, можно слегка усложнить алгоритм.

Привет. Можно подумать вот над какой схемой

Привет. Можно подумать вот над какой схемой

1. Клиент получает порцию данных и обновляет записи в своей локальной б.д.
2. Клиент шлет серверу подтверждение что пакет обновлений был успешно обработан.
3. Сервер помечает, что такие-то записи такому-то клиенту слать больше не надо
4. Сервер готовит следующую порцию обновлений.

Что касается п3. то вместе с каждой записью можно хранить битовую или текстовую маску . Позиция соотв. порядковому номеру клиента. 0 — запись не была получена клиентом., 1 — запись была получена клиентом.
При обновлении ценный на сервере все позиции выставляются в 0.

Напрмер

HDD seagate baracuda 250 баксов 1010000000

в данной случае новую цену получили 0-й и 2-й клиенты




Так же можно можно вести доп. таблицу с жуналом доставки или что-нить подобное.
Re: Обеспечение надежности при синхр-ии таблиц БД чере вебсе
От: stenkil  
Дата: 27.11.07 13:21
Оценка:
Здравствуйте, MozgC, Вы писали:

MC>Добрый день,

MC> Есть центральная локальная БД (главная), есть БД с похожей структурой на сайте. Необходимые обновления (например обновления или расширение прайс-листа) шлются из центральной БД на удаленную через веб-сервис. Шлю партиями, например по 1000 строк за раз.

Сервер
1. На каждого удаленного клиента выделяешь курсор данных.
2. Отправляешь пачку данных.
3. Если получил подтверждение что все прошло успешно и например КС совпала, переводит курсор на след. порцию данных, нет повторяет передачу пакета.

Клиент
1. Принимает данные, в цикле для каждой записи
1.  update
2.  если поймал исключение, запись не существует, insert
3.  если и здесь исключение fatal error на сервер
4.  если все нормально, подсчитал например КС

5. Закончил прием данных, отправляет на сервер КС или fatal error

выигрываешь в точной копии данных, даже при добавлении новых записей, проиграешь в скорости, т.к. нельзя сделать пакетную обработку на удаленном клиенте
Re[3]: Обеспечение надежности при синхр-ии таблиц БД чере ве
От: denisio_mcp  
Дата: 27.11.07 15:11
Оценка:
Здравствуйте, MindWrapper, Вы писали:

MW>Что касается п3. то вместе с каждой записью можно хранить битовую или текстовую маску . Позиция соотв. порядковому номеру клиента. 0 — запись не была получена клиентом., 1 — запись была получена клиентом.

MW>При обновлении ценный на сервере все позиции выставляются в 0.
MW>Напрмер
MW>HDD seagate baracuda 250 баксов 1010000000
MW>в данной случае новую цену получили 0-й и 2-й клиенты

Изначально кривой вариант. Кол-во клиентов 500. Как будете делать?
А завтра добавилось еще 15 клиентов. Варианты?
... << RSDN@Home 1.2.0 alpha rev. 0>>
Re[4]: Обеспечение надежности при синхр-ии таблиц БД чере ве
От: MindWrapper  
Дата: 28.11.07 08:17
Оценка:
Здравствуйте, denisio_mcp, Вы писали:

_>Здравствуйте, MindWrapper, Вы писали:


MW>>Что касается п3. то вместе с каждой записью можно хранить битовую или текстовую маску . Позиция соотв. порядковому номеру клиента. 0 — запись не была получена клиентом., 1 — запись была получена клиентом.

MW>>При обновлении ценный на сервере все позиции выставляются в 0.
MW>>Напрмер
MW>>HDD seagate baracuda 250 баксов 1010000000
MW>>в данной случае новую цену получили 0-й и 2-й клиенты

_>Изначально кривой вариант. Кол-во клиентов 500. Как будете делать?

_>А завтра добавилось еще 15 клиентов. Варианты?

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

Если структура имеет тенденцию меняться, то можно придумать что-нибудь более гибкое, скажем, дополнительную таблицу,
в которой бы хранились данные о том, какую запись из какой таблицы какому клиенту нужно доставить. Что-нибудь в этом духе.
Re[5]: Обеспечение надежности при синхр-ии таблиц БД чере ве
От: MozgC США http://nightcoder.livejournal.com
Дата: 28.11.07 14:14
Оценка:
Всем большое спасибо за участие в теме!

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

Надеюсь понятно объяснил, если кому вдруг интересно или пригодится...

Такое решение понравилось простотой и скоростью реализации.
Re[6]: Обеспечение надежности при синхр-ии таблиц БД чере ве
От: MindWrapper  
Дата: 28.11.07 14:51
Оценка:
Здравствуйте, MozgC, Вы писали:

MC>Всем большое спасибо за участие в теме!


MC>Сделал так:

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

MC>Надеюсь понятно объяснил, если кому вдруг интересно или пригодится...


MC>Такое решение понравилось простотой и скоростью реализации.


А кто и когда вычисляет таймстапм?
Re[7]: Обеспечение надежности при синхр-ии таблиц БД чере ве
От: MozgC США http://nightcoder.livejournal.com
Дата: 28.11.07 14:59
Оценка:
MW>А кто и когда вычисляет таймстапм?

Тот, который в отдельной таблице хранится? Это максимальный timestamp позиций из последней удачной синхронизации.
Re[7]: Обеспечение надежности при синхр-ии таблиц БД чере ве
От: MozgC США http://nightcoder.livejournal.com
Дата: 28.11.07 15:00
Оценка:
Получается перед тем как начать считывать данные из центральной БД для отправки на сайт.
Re[8]: Обеспечение надежности при синхр-ии таблиц БД чере ве
От: MozgC США http://nightcoder.livejournal.com
Дата: 28.11.07 15:04
Оценка:
В общем так:

        private void UpdateWeights()
        {
            if (MessageBox.Show("Are you sure you want to update weights in the online database?", Program.Name, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No) return;
            try
            {
                OnOffControls(false);
                SetSBText("Updating weights...");
                SetPBValue(0);

                MySqlCommand command = new MySqlCommand("", conn);

                // ПОЛУЧАЕМ МАКСИМАЛЬНЫЙ TIMESTAMP ИЗ ПОСЛЕДНЕЙ УДАЧНОЙ СИНХРОНИЗАЦИИ
                // (МЫ ЕГО ХРАНИМ В ТАБЛИЦЕ weights_ts)
                command.CommandText = "SELECT MaxTS FROM weights_ts";
                command.CommandType = CommandType.Text;
                DateTime MaxTS = Convert.ToDateTime(command.ExecuteScalar());

                // ПОЛУЧАЕМ КОЛИЧЕСТВО ЗАПИСЕЙ ДЛЯ АПДЕЙТА
                command.CommandText = "GetNewWeightsCount";
                command.CommandType = CommandType.StoredProcedure;
                command.Parameters.Add("?TS_", MySqlDbType.Datetime).Value = MaxTS;
                command.Parameters.Add("?RetVal", MySqlDbType.Int32).Direction = ParameterDirection.ReturnValue;
                command.ExecuteNonQuery();
                int RowsCount = Convert.ToInt32(command.Parameters["?RetVal"].Value);
                command.Parameters.Clear();

                SetPBMaximum(RowsCount);

                // УЗНАЕМ МАКСИМАЛЬНЫЙ TS В ЛОКАЛЬНОЙ БД, ЧТОБЫ В СЛУЧАЕ УСПЕШНОЙ СИНХРОНИЗАЦИИ
                // ОБНОВИТЬ ЕГО В ТАБЛИЦЕ weights_ts
                command.CommandType = CommandType.Text;
                command.CommandText = "SELECT MAX(TS) FROM weights";
                DateTime MaxLocalTS = Convert.ToDateTime(command.ExecuteScalar());
                command.CommandType = CommandType.StoredProcedure;
                
                // ВЫБИРАЕМ ВСЕ ЗАПИСИ НОВЕЕ ЧЕМ УКАЗАННЫЙ TS И ТАК КАК ЧТЕНИЕ ПОСТРАНИЧНОЕ
                // ТО ЗАДАЕМ НАЧАЛЬНЫЙ ID С КОТОРОГО БУДЕТ ЧИТАТЬСЯ ОЧЕРЕДНАЯ ПОРЦИЯ ЗАПИСЕЙ
                command.CommandText = "GetNewWeights";
                command.Parameters.Add("?TS_", MySqlDbType.Datetime).Value = MaxTS;
                MySqlParameter StartID = command.Parameters.Add("?StartID", MySqlDbType.Int32);

                int RowsRead = 0;    // СКОЛЬКО СТРОК СЧИТАЛИ, НУЖНО ДЛЯ ВЫСТАВЛЕНИЯ ПРОГРЕСС-БАРА
                int LastReadID = 0; // ДЛЯ УКАЗАНИЯ НАЧАЛЬНОГО ID С КОТОРОГО БУДЕТ ЧТЕНИЕ СЛЕД. ПОРЦИИ
                string Razdelitel = ";!;"; // РАЗДЕЛИТЕЛЬ МЕЖДУ ПОЛЯМИ ДЛИННОЙ СТРОКИ
                string LongStr = ""; // ДЛИННАЯ СТРОКА БУДЕТ ПЕРЕДАВАТЬСЯ ВЕБ-СЕРВИСУ (ТАК БЫСТРЕЕ ВСЕГО)
                bool SomethingHasBeenRead = true; // ДЛЯ ОПРЕДЕЛЯНИЯ ЕСТЬ ЛИ ЕЩЕ НОВЫЕ ЗАПИСИ (ВЕСА)
                // И ВЫХОДА ИЗ ЦИКЛА

                while (SomethingHasBeenRead)
                {
                    SomethingHasBeenRead = false;
                    StartID.Value = LastReadID;
                    using (MySqlDataReader reader = command.ExecuteReader())
                    {
                        while (reader.Read())
                        {
                            LongStr += reader["ManufacturerID"].ToString() + Razdelitel +
                                reader["PartN"].ToString() + Razdelitel +
                                reader["Weight"].ToString().Replace(",", ".") + Razdelitel +
                                Convert.ToDateTime(reader["TS"]).ToString("yyyy-MM-dd HH:mm:ss") + Razdelitel;
                            LastReadID = Convert.ToInt32(reader["ID"]);
                            RowsRead++;
                            SomethingHasBeenRead = true;
                        }
                        int res = myserv.SyncWeights(LongStr); // ОТПРАВЛЯЕМ ПОРЦИЮ НА САЙТ
                        if (res != 0)
                            throw new Exception();
                        LongStr = "";
                        SetPBValue(RowsRead);
                    }
                }
                // ОБНОВЛЯЕМ TIMESTAMP ПОСЛЕДНЕЙ УДАЧНОЙ СИНХРОНИЗАЦИИ
                command.CommandText = "UPDATE weights_ts SET MaxTS = '" + MaxLocalTS.ToString("yyyy-MM-dd HH:mm:ss") + "'";
                command.CommandType = CommandType.Text;
                command.ExecuteNonQuery();

                MessageBox.Show("Weights have been successfully updated in the online database!", Program.Name, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
            catch (Exception ex)
            {
                common.ShowException("Updating weights in online database", ex);
            }
            finally
            {
                OnOffControls(true);
                SetPBValue(0);
                SetSBText("Ready...");
            }
        }
Re[9]: Обеспечение надежности при синхр-ии таблиц БД чере ве
От: Нахлобуч Великобритания https://hglabhq.com
Дата: 28.11.07 15:29
Оценка:
Здравствуйте, MozgC, Вы писали:

MC>В общем так:


Ойойой. Откройте для себя MVC/MVP, ORM/Result Set Mapper и разделение на слои.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
HgLab: Mercurial Server and Repository Management for Windows
Re[10]: Обеспечение надежности при синхр-ии таблиц БД чере в
От: MozgC США http://nightcoder.livejournal.com
Дата: 28.11.07 15:55
Оценка:
Н>Ойойой. Откройте для себя MVC/MVP, ORM/Result Set Mapper и разделение на слои.

Я открыл Просто работал всегда либо вдвоем, либо вообще удаленно. Но сейчас уже в ближайших планах почитать Фаулера и новую книгу Нильсона. А вообще может кто поделится ссылкой или советом где есть хорошие примеры разделения на слои? Но не в абстрактных предложениях или несвязанных отрывках кода, а где бы действительно рассматрировалась многослойная архитектура бизнес-проекта с хорошими примерами, после чего можно было бы имееть четкое понимание и представление?
Re[11]: Обеспечение надежности при синхр-ии таблиц БД чере в
От: Нахлобуч Великобритания https://hglabhq.com
Дата: 28.11.07 16:00
Оценка:
Здравствуйте, MozgC, Вы писали:

MC>А вообще может кто поделится ссылкой или советом где есть хорошие примеры разделения на слои?


Навскидку вспоминается только Spring.Air -- демонстрационное приложение из Spring.NET.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
HgLab: Mercurial Server and Repository Management for Windows
Re: Обеспечение надежности при синхр-ии таблиц БД чере вебсе
От: BulatZiganshin  
Дата: 28.11.07 18:16
Оценка:
Здравствуйте, MozgC, Вы писали:

вариантов много, опишу работающий у нас:

при любом добавдлении/изменении записи в любой таблице обновляется поле last_update. операция посылки обновления выполняется ежедневно, но при этом выбирает данные, обновлённые за последнюю неделю. при этом синхронизируемые таблицы/записи меняются только в центральном офисе (скажем, это дилерский прайслист, состояние запасов на центр. складе и т.п.), поэтому конфликтов обнолвения не порисходит. плюс есть таблица deleted, куда заносятся tablename+id удалённых записей и дата удаления — из неё выборка ес-но делается тоже

когда что-то идёт совсем не так — можно просто послать данные за целый год
Люди, я люблю вас! Будьте бдительны!!!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.