Миграции базы данных на django production

От кого-то, у кого есть приложение django в нетривиальной производственной среде, как вы справляетесь с миграцией базы данных? Я знаю, что есть south, но мне кажется, что здесь будет много упущений, если будет задействовано что-нибудь существенное.

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

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


person David542    schedule 31.05.2012    source источник
comment
Большинство знакомых мне разработчиков Django используют South. Я сам использую Юг. Как вы думаете, что будет «очень много упускать»? У меня были проблемы с Югом, но по большей части он работает.   -  person super9    schedule 31.05.2012
comment
Каков примерный объем вашей заявки?   -  person David542    schedule 31.05.2012


Ответы (5)


Я думаю, что эта проблема состоит из двух частей.

Во-первых, это управление схемой базы данных и ее изменениями. Мы делаем это с помощью South, сохраняя как рабочие модели, так и файлы миграции в нашем репозитории SCM. В целях безопасности (или паранойи) мы делаем дамп базы данных перед (и, если нам действительно страшно, после) выполнения каких-либо миграций. До сих пор Юг отвечал всем нашим требованиям.

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


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

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

Будьте осторожны, если вы вносите изменения в копию своей основной базы данных. Основная проблема здесь в том, что ваш сайт все еще активен и предположительно принимает записи в базу данных. Что происходит с данными, записанными в основную базу данных, пока вы заняты переносом клона для дальнейшего использования? Ваш сайт должен быть либо отключен все время, либо временно переведен в состояние только для чтения, иначе вы потеряете их.

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

Я видел / слышал несколько других способов, которые работают хорошо.

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

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

Но я думаю, что в этот момент Google станет вашим другом, потому что, как я уже сказал, решение очень зависит от контекста, и я беспокоюсь, что этот ответ станет бессмысленным ... Найдите что-то вроде «развертывание с нулевым временем простоя», и вы ' Я получу такие результаты, как это с куча идей ...

person Mark Streatfield    schedule 02.06.2012
comment
Спасибо за этот ответ. Не могли бы вы вкратце объяснить, как вы справляетесь со второй частью этого? - person David542; 02.06.2012
comment
Я добавил несколько деталей для второй части проблемы - надеюсь, это поможет. Это также зависит от вашего масштаба - Facebook должен решать эту проблему совсем иначе, чем блог, который вы, возможно, размещаете в своей спальне! - person Mark Streatfield; 03.06.2012
comment
Для всех, кто только что пришел сюда, ссылка внизу сообщения на exortech.com мертва. Вот ссылка Wayback Machine на самую последнюю версию без перенаправления: https://web.archive.org/web/20140822052754/http://www.exortech.com/blog/2009/02/01/weekly-release-blog-11-zero-downtime-database-deployment/ - person Joe C.; 23.05.2017
comment
Юг, похоже, устарел stackoverflow.com/a/52259197/1897935 - person Srinath Ganesh; 28.02.2020

Я использую South для производственного сервера с кодовой базой ~ 40 000 строк, и пока у нас не было никаких проблем. Мы также прошли через несколько серьезных рефакторингов для некоторых наших моделей, и у нас не было никаких проблем.

Одна вещь, которая у нас также есть, - это контроль версий наших моделей, который помогает нам отменить любые изменения, которые мы вносим в модели на стороне программного обеспечения, причем South больше подходит для фактических данных. Мы используем Django Reversion.

person beowulfburritos    schedule 31.05.2012
comment
Я только кратко прочитал документацию по Django Reversion, но мне было непонятно, почему вы должны использовать его вместо `` правильного '' SCM (такого как SVN или git) для управления моделями вместе (и таким же образом, как ) остальной код вашего сайта. Что я пропустил? - person Mark Streatfield; 02.06.2012
comment
@MarkStreatfield: реверсия предназначена для управления содержимым моделей с течением времени, а не структурой. - person Matthew Schinckel; 05.06.2012
comment
Ах, понятно, спасибо за пояснение - я как-то полностью это пропустил. Поэтому было бы полезно сделать что-то похожее на то, что описано в этом вопросе stackoverflow.com/questions/39281/database-design-for-revisions/? - person Mark Streatfield; 06.06.2012

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

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

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

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

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

Фактический код Python - я поместил его в конец файла settings.py, чтобы проверить концепцию, но вы, вероятно, захотите сохранить его в отдельном модуле:

from django.db.models.sql.compiler import SQLCompiler
from MySQLdb import OperationalError

orig_exec = SQLCompiler.execute_sql
def new_exec(self, *args, **kw):
    try:
        return orig_exec(self, *args, **kw)
    except OperationalError, e:
        if e[0] != 1054: # unknown column
            raise
        upgradeSchema(self.connection)
        return orig_exec(self, *args, **kw)
SQLCompiler.execute_sql = new_exec

def upgradeSchema(conn):
    cursor = conn.cursor()
    try:
        cursor.execute("alter table users add phone varchar(255)")
    except OperationalError, e:
        if e[0] != 1060: # duplicate column name
            raise

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

Вам нужно будет адаптировать класс исключения (MySQLdb.OperationalError в моем случае) и числа (1054 «неизвестный столбец» / 1060 «повторяющийся столбец» в моем случае) для механизма вашей базы данных и изменения схемы, но это должно быть легко.

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

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

person Julian    schedule 08.06.2012

Если ваша база данных нетривиальна и Postgresql, у вас есть целый ряд отличных SQL-опций, в том числе:

  • снимок и откат
  • репликация в реальном времени на резервный сервер
  • пробное обновление, затем живи

Вариант пробного обновления хорош (но лучше всего делать это вместе со снимком)

su postgres
pg_dump <db> > $(date "+%Y%m%d_%H%M").sql
psql template1
# create database upgrade_test template current_db
# \c upgradetest
# \i upgrade_file.sql
...assuming all well...
# \q
pg_dump <db> > $(date "+%Y%m%d_%H%M").sql # we're paranoid
psql <db>
# \i upgrade_file.sql

Если вам нравится описанная выше схема, но вас беспокоит время, которое потребуется на запуск обновления дважды, вы можете заблокировать db для записи, а затем, если обновление до upgradetest пройдет успешно затем вы можете переименовать db в dbold и upgradetest в db. Есть много вариантов.

Если у вас есть файл SQL, в котором перечислены все изменения, которые вы хотите внести, очень удобная команда psql \set ON_ERROR_STOP 1. Это останавливает сценарий обновления в тот момент, когда что-то пойдет не так. И, проведя множество тестов, вы можете убедиться, что ничего не работает.

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

pg_dump --schema-only production_db > production_schema.sql
pg_dump --schema-only upgraded_db > upgrade_schema.sql
vimdiff production_schema.sql upgrade_schema.sql
or
diff -Naur production_schema.sql upgrade_schema.sql > changes.patch
vim changes.patch (to check/edit)
person rorycl    schedule 07.06.2012

Юг не везде используется. Как и в моей организации, у нас есть 3 уровня тестирования кода. Один - это локальная среда разработки, один - среда разработки, а третий - производственная среда.

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

person iamkhush    schedule 03.06.2012