Вот обходные пути и альтернатива

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

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

Поддержка мягкого удаления

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

id: string
username: string
password: string
created_date: Date
deleted: boolean

В приведенном выше примере любой пользователь, для которого в поле deleted установлено значение true, считается удаленным.

Плюсы мягкого удаления

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

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

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

Попытки мягкого удаления

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

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

SELECT * from users where deleted=false;

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

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

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

Добавление многостолбцового индекса с первым столбцом в качестве поля флага deleted и его разумное использование в запросах должно сделать это быстрее. Индекс для ускорения выбора можно добавить следующим образом:

CREATE INDEX "users_username_unique" ON users(deleted, username);

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

SELECT * FROM users WHERE deleted=false AND username="foobar";

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

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

Представьте, что у нас есть уникальное ограничение для поля email в таблице users. У нас возникнет проблема, если есть пользователь с адресом электронной почты [email protected], который удаляет свою учетную запись, а затем создает новую с тем же адресом электронной почты.

Для решения этой проблемы нам нужно добавить частичный индекс вместо обычного индекса. Частичные индексы позволяют нам создать индекс только для подмножества полей, используя предложение WHERE при создании индекса следующим образом:

CREATE UNIQUE INDEX "users_username_unique" ON users(deleted, email) WHERE deleted=false;

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

Тем не менее, я встречал множество приложений, которые не позволяют повторно использовать значения уникальных полей, таких как username и email, даже если они уже удалены. Так что такая практика кажется приемлемой.

Наконец, при мягком удалении часто возникает соблазн просто установить поле флага и покончить с ним. Однако некоторые таблицы и коллекции могут содержать конфиденциальные данные, которые на самом деле следует удалить. Таким образом, нам нужно обрабатывать конфиденциальные данные отдельно при выполнении мягкого удаления. Это показано в примере ниже.

UPDATE users set deleted=true, deleted_date=NOW(), credit_card_number=NULL where id='someId';

Альтернатива: жесткое удаление с копированием

Теперь давайте посмотрим на альтернативу мягкому удалению, которая использует жесткое удаление.

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

Самый простой способ добиться этого - использовать триггер базы данных. Ниже приведен пример использования PostgreSQL.

Плюсы жесткого удаления с копированием

Основное преимущество использования этого подхода заключается в том, что удаленные и текущие данные четко разделены. Таким образом, вероятность утечки удаленных данных отсутствует.

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

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

Проблемы жесткого удаления с копированием

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

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

Другой недостаток состоит в том, что DELETE операций обычно медленнее, чем UPDATE операций. Вдобавок к этому добавляем несколько миллисекунд с триггером и вставляем в архивную таблицу. Однако мы бы уже использовали DELETE запросы в таблицах, которые не поддерживают какие-либо формы мягкого удаления или архивирования. Кроме того, фактическое удаление записей гарантирует, что наши индексы будут содержать только живые данные, а не смесь удаленных и живых данных. Таким образом, использование DELETE запросов не должно быть проблемой.

Последний недостаток состоит в том, что может быть трудно поддерживать отношения в архивных таблицах. Например, если таблица users имеет отношение «многие к одному» с таблицей companies, то компания, на которую указывает запись в users_archive, может существовать либо в companies (для действующих компаний), либо в companies_archive (для удаленных компаний).

Заключение

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

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

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