Когда на проекте ближе к концу разработки становится понятно, что без версионирования данных не обойтись, ведь любой пользователь может зайти и удалить что-то, что создавалось в течение длительного периода времени десятками других людей, приходится искать подходящее решение.
решение, требующее минимальных усилий.
Не вдаваясь в подробности конкретного проекта, в котором возникла такая необходимость, представим себе табличный документ Google Docs, редактировать который может любой посетитель сайта.
Предположим, что инженеры Google используют базу данных MySQL следующей структуры: документы --идентификатор --имя --creator_id листы --идентификатор --document_id --имя ряды --число --sheet_id --высота столбцы --число --sheet_id --ширина клетки --идентификатор --sheet_id --цвет --содержание --row_number --col_number --creator_id Как уже было написано выше, доступ к документу имеет любой человек, и, например, изменение значения ячейки на матерное слово навсегда уничтожит предыдущее значение.
Требования к структуре
Обязательный
- Возможность сохранять предыдущие значения ячеек.
- Возможность просмотра предыдущих версий онлайн-документа.
- Возможность отката к предыдущей версии
- Некоторые параметры могут не иметь разных версий (id,sheet_id)
- Внедрение в используемый ORM
Желательный
- Минимально возможные трудозатраты на перенос данных в новую структуру, выбор, изменение и добавление данных
- Ээкономия дискового пространства
- Выделение измененных ячеек при просмотре старых редакций
Выполнение
После долгих размышлений была выведена «формула».Что, если мы создадим дополнительную таблицу и объединим основную таблицу и вновь созданную таблицу, содержащую разные версии значений.
Прежде всего нам нужна таблица с ревизиями: редакции --идентификатор --parent_id --document_id --Дата создания Мы разделили таблицу ячеек на 2, чтобы ячейки содержали информацию, не требующую управления версиями, а ячейки_данные содержали информацию, которая может быть изменена пользователями.
Кроме того, в таблицу cell_data добавляем поля созданный_in_revision_id, delete_in_revision_id и идентификатор пользователя, внесшего изменения.
клетки --идентификатор --sheet_id --row_number --col_number --creator_id ячейки_данные --cell_id --цвет --содержание --data_creator_id --created_in_revision_id --deleted_in_revision_id (первичный ключ для cell_id + созданный_in_revision_id) В нашем коде к объекту Document (если мы программируем на основе объекта) мы добавляем метод getRevisionCondition($revisionId=false), который должен возвращать префикс к SQL-запросу, например «created_in_revision_id in (0,100,300,301) и delete_in_revision_id не в (0,100,300,301)».
Те.
содержать текущую ревизию и всех ее родителей в конструкции «в (.
)» и «не в (.
)»
Образец
Дальше карьер, который раньше выглядел так:превращается в:select * from cells where row_number=3 and col_number=2
select c.*,cd.* from cells c,cells_data cd where row_number=3 and col_number=2 and id=cell_id and $revisionCondition
Разумеется, имена полей этих таблиц не должны дублироваться.
Вставка новой записи
Здесь все так же просто, как и с семплированием.Проверяем, не просрочена ли ревизия и берем самую последнюю.
Например вот так: $revision=$document->updateRevisionIfExpired()
Сначала вставляем в основную таблицу (cells), затем в таблицу с версионными данными (cells_data).
В поле созданный_in_revision_id записываем ID последней ревизии.
Удаление записи
Здесь мы попробуем сэкономить дисковое пространство.Если, например, мы установили время жизни ревизии 30 минут, то мы сравниваем ревизию удаленной записи с текущей ревизией и:
- если они совпадают, мы просто удаляем их из базы данных
- если ревизия уже изменилась, то просто пропишите ID новой ревизии в поле таблицы cell_data — delete_in_revision_id
if(cellRevisionId==currentRevision.getId()){
db.online.query("delete from cells_data where cell_id="+cellId+" and created_in_revision_id="+cellRevisionId)
}else{
db.online.update('cells_data',{'deleted_in_revision_id':currentRevision.getId()},{'cell_id': cellId, 'created_in_revision_id' : cellRevisionId})
}
Обновление записи
Изменение данных чем-то похоже на их удаление.
Если ревизия не изменилась, данные просто обновляются, а если изменились, то поле delete_in_revision_id обновляется и в таблицу cell_data вставляется новая запись с идентификатором новой ревизии в созданном_in_revision_id.
Вместо заключения
Т.к.
большая часть таблиц, нуждающихся в версионировании, уже перенесена в новую структуру - могу выделить плюсы и минусы:
Преимущества
- Все выделения и вставки (другими словами селекты и вставки) были переделаны максимально быстро и без каких-либо осложнений.
- Делает все что требовалось и почти все что хотелось
Недостатки
- Удаление и обновление записи по-прежнему немного сложны, и было бы неплохо использовать более автоматизированный скрипт.
- Возможно, вам нужно что-то сделать с запросами типа in (.
), а не in (.
), поскольку они перечисляют все версии документа.
-
Где Я Могу Получить Эмулятор Unix?
19 Oct, 24 -
Мошенники: Skype-Mobile.net
19 Oct, 24 -
Как Образуются Пробки
19 Oct, 24