BackUp: восстановление конкретных записей

clevel

Новичок
BackUp: восстановление конкретных записей

С прошедшим НГ и рождеством :)

Возникла необходимость не полностью восстанавливать данные, а лишь частично.
Например, есть бекап счетов - который делается каждый день. И вот случайно удалили какой-то из счетов.
Задача - дать визуальный интерфейс -- вывели список счетов - отметили - какие восстановить, они восстановились.

В связи с этим несколько проблем:

1. Счета - эта одна из сущностей конкретно взятой системы. Состоит из данных в 4 таблицах.
В данной системе еще с десяток аналогичных сущностей, со своими наборами зависимостей и своими таблицами.

Получается, что для реализации данной задачи нужно будет писать отдельно библиотечки под каждую из сущностей.
Взять данные из такой то таблички, по списку ID из такого поля вязть данные из такой-то таблички и т.д.

Либо можно это сделать по принципу Vbulleten Import: есть общий класс, которому передаются данные в стандартизированном виде, и он уже вставляет их в базу. И под каждый движок - откуда идет импорт - свой класс/прослойка, который приводит данные к стандартной форме.

В таком случае - под каждый модуль - просто свой конфиг файл

Помимо этого, возникает проблема с автоинкрементными полями: добавили счет - удалили его, потом новый вставили.
ID удаленного (не флаг стоит - удален, а DELETE FROM было) = ID вновь вставленного

2. С течением времени система расширяется, в таблицы добавляются новые поля, у сущностей появляется доп. функционал.
Поэтому бекап 3-х месячной давности по набору полей не совпадает с текущим.

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

Прошу подсказок, кто сталкивался с аналогичными проблемами: как решали, какие причины в пользу того или иного решения.
Заранее спасибо!
 

x-yuri

Новичок
Помимо этого, возникает проблема с автоинкрементными полями: добавили счет - удалили его, потом новый вставили.
ID удаленного (не флаг стоит - удален, а DELETE FROM было) = ID вновь вставленного
какой у тебя тип таблиц и кто тебе сказал, что значения AUTO_INCREMENT полей повторно используются? на данный момент это происходит в исключительных ситуациях

кстати подумай по поводу не удаления счетов из таблиц, а "пометки" их флагом deleted
 

clevel

Новичок
Тип таблиц - MyIsam
Насчет AUTO_INCREMENT - ставлю первичный ключ на три поля: owner,year,id и на поле id - автоинкримент.

Добавляю запись - овнер - 1, год - 2009. ID ставится - 1
Удаляю запись, после вставки второй записи - id опять 1

Насчет флага - удалено - подумаю...
 

x-yuri

Новичок
Тип таблиц - MyIsam
Насчет AUTO_INCREMENT - ставлю первичный ключ на три поля: owner,year,id и на поле id - автоинкримент.

Добавляю запись - овнер - 1, год - 2009. ID ставится - 1
Удаляю запись, после вставки второй записи - id опять 1
это как раз один из тех нечастых случаев)))
а тебе нужно, чтобы для каждого owner, year была своя последовательность id?
 

clevel

Новичок
Да, нужна.

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

Поэтому вариант с флагом - удален - кажется единственным выходом. С этим вроде понятно.

Но это далеко не основная проблема.
Меня больше беспокоит сама схема реализации списка счетов - и восстановление выбранных. Когда это одна сущность - не проблема, но их же много. Код многократно раздувается. Как быть?
 

x-yuri

Новичок
просто мне непонятно как у тебя организован доступ к сущностям/таблицам

Если например у тебя код знает про все связи между таблицами, можно организовать такую штуку как каскадное восстановление (undelete) )), которое не зависит от конкретной сущности

и почему у тебя счет разбит на 4ре таблицы? Т.е. для чего каждая таблица и как они связаны?
 

clevel

Новичок
В первой таблице - сами счета
Во второй - список товаров/услуг, которые в этот счет входят
Связь - многие к одному.

То есть при восстановлении я восстанавливаю счет, потом во второй таблице должен для этого счета восстановить записи товаров/услуг

Другие таблицы - либо служебные, либо взаимосвязаны с в главной (с/фактуры,акты, ТТН завязаны на счет, в счете стоит id контраегнта и id менеджера, и т.д.)
 

x-yuri

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

просто мне непонятно как у тебя организован доступ к сущностям/таблицам
?
 

clevel

Новичок
я так понимаю, +1 за второй варант, что я расписал - когда есть конфиг файл - и общий класс, который его разбирает и восстанавливает данные.

Попробую составить такой конфиг, посмотрим - смогу ли все стандартизировать.

При том же апдейте (update!=delete) списка товаров/услуг в подчиненной таблице надо сделать сначал delete по первичному ключу счета, а потом insert нужные данные. Чтобы личшие товары/услуги убрать.

Попутно такой вопрос возник: бекап - это архив с .sql файлами - под каждую таблицу. Как лучше их развораивать? Какую-то тестовую/временную БД создавать? Или тупо префикс - timestamp и в текущую БД закидывать?

И остается открытым вопрос - как бороться с разными версиями бекапа?
 

x-yuri

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

При том же апдейте (update!=delete) списка товаров/услуг в подчиненной таблице надо сделать сначал delete по первичному ключу счета, а потом insert нужные данные. Чтобы личшие товары/услуги убрать.
а разве не стоит только добавлять товары услуги? у тебя же иначе их id могут изменится

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

И остается открытым вопрос - как бороться с разными версиями бекапа?
если ты не будешь удалять записи из бд, то зачем тебе боротся с разными версиями бэкапа? Есть в принципе такая утилита mysqldiff и в pear что-то было для сравнения двух дампов (правда, когда я его смотрел, они его еще не доделали)
 

clevel

Новичок
стоп! Я себе представляю процедуру частичного восстановления так: либо все такблички из бекапа заливаются во временную базу, либо в кетущую, но у название - префикс.

И потом я через простые запросы mysql - выбираю нужные данные и вставляю в живые таблицы.

Или ты предлагаешь выборку по ключам делать силами пхп, разбирая sql файл?

Насчет удаления товаров из счета. У каждого товара нет автоинкрементного поля.

Там есть id, но он простой от 1 до 25. Именно столько максимально товаров может быть в счете. Поэтому если я при редактировании счета удалил как-то товар - то мне либо на него надо ставить флаг - что удален, либо физически удалять.

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

Насчет mysqldiff и разных версий бекапа. Ты не совсем понял.
Что делать, если со временем появляются новые поля. Где-то можно дефолтные значения поставить, где-то надо по другому.

Пример: сначала в счетах было поле - контрагент как строка.
Потом появился сл временем словарь контрагентов, и я завел второе поле ID контрагента. Где скриптом проставил айдишки этих фирм.

Если вязть самый первый бекап - то при восстановлении ID будет нулевым, что не есть хорошо.

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

x-yuri

Новичок
так вот я и говорю: ты себе слишком сложную задачу поставил - восстанавливать информацию из бэкапа. Ее лучше упростить: не удалять информацию, а просто помечать ее как удаленную. У тебя сразу станет меньше проблем ;-)

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

clevel

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

Насчет оперирования только флагами - помечен на уделение - скорее всего не получится.

Тот же список товаров - который к счету привязан.
Как запись помечать - если я этот товар отредактировал (само название, скажем), а через пару дней - еще раз.
Получается, что нужно вести журнал изменений какой-то, чтобы этот список товаров обновить на нужный.

Как тут быть?
 

x-yuri

Новичок
думаю держать в бд историю изменений будет проще, чем из бекапа восстанавливать

Если пользователю надо будет он ее тоже сможет себе получить и восстановить что надо

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

clevel

Новичок
Хорошо. Систему с флагами + журналом изменений - попробую продумать.

Если все таки посмотреть на систему - когда есть таблички с текущими данными + залитый архив с .sql файлами бекапа на какой-то день. Как лучше довести структуру данных из бекапа до нужной? Как вариант - в бекапе держать номер версии, скажем, 1.1. А текущая - 1.5 Тогда проходимся по списку апйдейтов - и инклудим апейтеры каждой версии, которые добавляют поля в таблицах + заполняют их данными.

м?
 

x-yuri

Новичок
да, импортировать бэкап во временную бд, вносить изменения произошедшие со времени бэкапа и восстанавливать что нужно

только не будет ли зависимостей при восстановлении? т.е. чтобы восстановить одну сущность надо восстановить другую?
 

clevel

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

Если же контрагента удалили - которому выставлены счеты - то восстанавливать не будем - дадим сообщение об ошибке и предложение восстановить сначала контрагента..
 

x-yuri

Новичок
кстати, по поводу журнала в бд - не обязательно каждое изменение отслеживать, можно делать периодические снэпшоты (ты же так с бэкапами собирался делать?)

а что там за вариант "по принципу Vbulleten Import"? к какой стандартной форме приводятся данные?
 

clevel

Новичок
Автор оригинала: x-yuri
кстати, по поводу журнала в бд - не обязательно каждое изменение отслеживать, можно делать периодические снэпшоты (ты же так с бэкапами собирался делать?)
а что там за вариант "по принципу Vbulleten Import"? к какой стандартной форме приводятся данные?
В данном случае - просто идет речь о конфиге: основная таблица -- такая-то. Первичный ключ - из таких то полей состоит. Зависимая таблица - такая-то, по такому то ключу выбираем данные. Плюс -- принци обновления данных для главной и зависимой таблице.
И доп. строка - название функции - для персональной отработки данных.
Например, для проверки зависимых данных и вставки нужных - если удалили необходимые данные.

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

Насчет снапшотов: все таки думаю схему с моими бекапами оставить как есть.
Так как схема сложнее, но и универсальнее и выгоднее (сделал бекап любыми средствами, потом закачал в панельку -- выбрал что восстановить - точечно восстановилось что надо).

Я банально смогу ее потом на цмс, биллинг и кучу своих других софтовых решений внедрить.
Так что игра стоит свечь :)
 
Сверху