Django PostgreSQL后端,对数据库锁应用迁移

django-pg-zero-downtime-migrations的Python项目详细描述


构建状态

django pg零停机迁移

Django PostgreSQL后端,对数据库锁应用迁移。

安装

pip install django-pg-zero-downtime-migrations
< Buff行情>

注意:此软件包适用于django 2.0+。

用法

要为Postgres启用零停机迁移,只需设置此软件包提供的django后端并添加最安全的设置:

DATABASES = {
    'default': {
        'ENGINE': 'django_zero_downtime_migrations.backends.postgres',
        #'ENGINE': 'django_zero_downtime_migrations.backends.postgis',
        ...
    }
}
ZERO_DOWNTIME_MIGRATIONS_LOCK_TIMEOUT = '2s'
ZERO_DOWNTIME_MIGRATIONS_STATEMENT_TIMEOUT = '2s'
ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT = True
ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE = True
ZERO_DOWNTIME_MIGRATIONS_USE_NOT_NULL = False
< Buff行情>

注意:此后端只为迁移(schema和runsql操作,而不是runpython操作)带来了零停机时间改进,其他方面它与标准django后端工作相同。

< Buff行情>

注意:此软件包是beta版的,请在应用于生产之前检查迁移SQL,并提交任何问题。

与标准django后端的区别

此后端提供相同的结果状态(而不是not nullconstraint replacement),但提供了不同的方式和额外的保证,以避免表被锁定。

此后端不使用事务进行迁移(除了runpython操作),因为并非所有固定sql都可以在事务中运行,而且它允许避免复杂迁移的死锁。因此,当您的迁移在事务处理过程中停止时,您需要手动修复它(而不是潜在的停机时间)。

部署流程

零停机部署有一个主要规则:

  1. 我们有一个数据库;
  2. 我们有几个应用程序实例-应用程序应该始终可用,即使您重新启动其中一个实例;
  3. 我们在实例之前有平衡器;
  4. 我们的应用程序在迁移前、迁移时和迁移后运行良好-旧应用程序在新旧数据库架构版本下运行良好;
  5. 我们的应用程序在实例更新之前、之后和之后都可以正常工作-旧的和新的应用程序版本可以与新的数据库架构版本一起正常工作。
  6. < > >

    流量:

    1. 应用迁移
    2. 断开实例窗体Balancer的连接,重新启动它并返回到Balancer-对所有实例逐个重复此操作
    3. < > >

      如果我们的部署不满足零停机部署规则,则将其拆分为较小的部署。

      附加设置

      零停机时间迁移锁定超时

      对于需要access exclusivelock的sql语句,应用statement-timeout,默认为none

      ZERO_DOWNTIME_MIGRATIONS_LOCK_TIMEOUT = '2s'
      

      允许值:

      • -当前使用的Postgres设置
      • 其他-将应用超时,0和等效值表示将禁用超时

      零停机时间迁移语句超时

      对于需要access exclusivelock、默认none

      ZERO_DOWNTIME_MIGRATIONS_STATEMENT_TIMEOUT = '2s'
      

      允许值:

      • -当前使用的Postgres设置
      • 其他-将应用超时,0和等效值表示将禁用超时

      零停机时间迁移灵活语句超时

      将SQL语句的语句超时设置为0ms需要share update exclusive锁定,在全局启用语句超时并尝试运行索引创建或约束验证等长期运行操作时非常有用,默认值为false

      ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT = True
      

      零停机时间迁移提高不安全性

      启用选项不允许运行潜在的不安全迁移,默认值false

      ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE = True
      

      零停机时间迁移使用非空

      设置避免不为空的策略约束创建长锁,默认为

      ZERO_DOWNTIME_MIGRATIONS_USE_NOT_NULL = 10 ** 7
      

      允许值:

      处理部分索引

      < Buff行情>

      注意:django 2.2支持本机部分索引机制:https://docs.djangoproject.com/en/2.2/ref/models/indexes/conditionhttps://docs.djangoproject.com/en/2.2/ref/models/constraints/condition

      如果您在postgres中使用了部分索引包https://github.com/mattiaslinnap/django-partial-index" rel="nofollow">https://github.com/mattiaslinnap/django-partial-index,那么您可以很容易地使这个包对于迁移也是安全的:

      from django_zero_downtime_migrations_postgres_backend.schema import PGShareUpdateExclusive
      from partial_index import PartialIndex
      
      PartialIndex.sql_create_index['postgresql'] = PGShareUpdateExclusive(
          'CREATE%(unique)s INDEX CONCURRENTLY %(name)s ON %(table)s%(using)s (%(columns)s)%(extra)s WHERE %(where)s',
          disable_statement_timeout=True
      )
      

      工作原理

      Postgres表级锁

      postgres在表级别上有不同的锁,这些锁可能相互冲突https://www.postgresql.org/docs/current/static/explicit locking.html locking-tables

      <表><广告>< T/>访问共享行共享排他行独占共享更新共享独占共享行独占独占访问< /广告><正文>访问共享x行共享xx排他行xxxx独家共享更新xxxxx共享x 阿里gn="center">xxxx共享行独占xxxxxx独家xxxxxxx独家访问xxxxxxxx

      迁移和业务逻辑锁

      让我们将此锁拆分为迁移和业务逻辑操作。

      • 迁移操作在一个线程中同步工作并覆盖架构迁移(数据迁移与业务逻辑操作冲突,与业务逻辑冲突同时发生)。
      • 业务逻辑操作同时工作。

      迁移锁

      <表><广告>锁定 操作 < /广告><正文>独家访问创建序列删除序列创建表删除表*,更改表**,删除索引共享创建索引独家共享更新并发创建索引并发删除索引***,更改表验证约束***

      *:创建序列删除序列创建表删除表不应该有冲突,因为逻辑不应该使用它操作

      **:不是所有的alter table操作都采用access exclusivelock,但目前所有的django迁移都是通过https://github.com/django/django/blob/master/django/db/backends/base/schema.py" rel="nofollow">https://github.com/django/django/blob/master/django/db/backends/base/schema.py,https://github.com/django/django/blob/master/django/db/backends/postgresql/schema.pyhttps://www.postgresql.org/docs/current/static/sql altertable.html

      ***:django当前不支持并发操作操作

      ***:django没有validate constraint逻辑,但我们将在某些情况下使用它

      业务逻辑锁 <表><广告>锁定 操作 与锁冲突 与操作冲突 < /广告><正文>访问共享选择独家访问更改表删除索引行共享选择进行更新访问独占独占更改表删除索引排他行插入更新删除访问独占独占共享行独占共享更改表删除索引创建索引

      因此,您可以发现exist表的所有django模式更改都与业务逻辑冲突,但幸运的是,它们是安全的,或者在gen中有安全的替代方案。艾莱依.

      Postgres行级锁

      由于业务逻辑主要与表行一起工作,因此理解行级的锁冲突也很重要https://www.postgresql.org/docs/current/static/explicit locking.html;锁定行

      <表><广告>锁定 用于密钥共享共享 不更新密钥更新 < /广告><正文>用于密钥共享x共享xx表示无钥匙更新xxx用于更新xxxx

      要点是,如果有两个事务更新一行,那么第二个事务将等到第一个事务完成。因此,对于业务逻辑和数据迁移,最好避免对整个表进行更新,而是使用批处理操作。

      < Buff行情>

      注意:批处理操作也可以更快地工作,因为它有助于Postgres制定更优化的执行计划。

      事务FIFO等待

      postgres fifo

      在一篇有趣的文章中找到了相同的图表http://pankrat.github.io/2015/django-migrations-without-downtimes/" rel="nofollow">http://pankrat.github.io/2015/django migrations without downtimes/

      在这个图表中,我们可以提取几个指标:

      1. 操作时间-花在架构更改上的时间,因此对许多行表(如create indexalter table add column set default)进行长时间运行操作时会出现问题,因此您需要使用更多的存储等效项。
      2. 等待时间-您的迁移将等待所有事务完成,因此对于像Analytic这样长时间运行的操作/事务存在问题,因此您需要避免它或禁用迁移时间。
      3. 每秒查询数+执行时间和连接池-如果要表的查询太多,而此查询花费的时间太长,则此查询只能将所有可用的连接带到数据库,直到等待释放锁为止,因此看起来您需要在那里进行不同的优化:在加载最少,减少查询计数和执行时间,分割数据。
      4. 一个事务中的操作太多-对于一个操作,前面的所有点都有问题,因此如果一个事务中有多个操作,则有更多的机会遇到此问题,因此,您应该避免在一个事务中执行许多操作(或者事件根本不在事务中运行它,但当某些操作失败时,您应该更加小心)。
      5. < > >

        处理超时

        Postgres有两个处理图中显示的等待时间和操作时间的设置:锁定超时语句超时

        将锁定超时设置为"2s"允许您在运行迁移之前长时间运行查询/事务时避免停机(https://www.postgresql.org/docs/current/static/runtime config client.html guc-lock-timeout)。

        将statement\u timeout设置为"2s"允许您在长时间运行迁移查询时避免停机(https://www.postgresql.org/docs/current/static/runtime config client.html;guc-statement-timeout)。

        死锁

        死锁不存在停机问题,但是一个事务中的太多操作将获取最具冲突性的锁,并仅在事务提交或回滚后释放它。因此,在一个事务中避免access exclusive锁定操作和长时间操作是一个好主意。当不同的表将被锁定时,死锁也会使您在生产部署时无法进行迁移,例如,对于使用access exclusive锁定两个表的外键。

        存储的行和值

        Postgres以不同的方式存储不同类型的值https://www.postgresql.org/docs/current/static/storage toast.html storage-toast-ondisk。当您试图将一种类型转换为另一种类型,并且它以不同的方式存储时,postgres将重写所有值。幸运的是,某些类型以相同的方式存储,Postgres不需要做任何事情来更改类型,但在某些情况下,Postgres需要检查所有值是否具有相同的新类型限制。

        多版本并发控制

        关于文档https://www.postgresql.org/docs/current/static/mvcc intro.html使用多版本模型维护Postgres中的数据一致性。这意味着每个sql语句都能看到数据的快照。它的优点是添加和删除不带任何索引、约束和默认值的列不会更改现有数据,新版本的数据将在insertupdate上创建,删除只是将您的记录标记为过期。所有垃圾稍后将通过vacuumauto vacuum收集。

        Django Migrations破解

        任何模式更改都可以通过创建新表并将数据复制到表中来处理,因此只要将没有停机时间的安全方法的不安全操作标记为no

        <表><广告>名称安全安全选项说明 < /广告><正文>1创建序列x安全操作,因为您的业务逻辑不应该在迁移时间上使用新序列操作*2放置顺序x安全操作,因为您的业务逻辑不应该在迁移时间上使用此序列操作*3创建表x安全操作,因为您的业务逻辑不应该在迁移时间上使用新表*4放置表格x安全操作,因为您的业务逻辑不应该在迁移时间上使用此表操作*5将表重命名更改为不安全操作,同时操作两个表的业务逻辑太难编写,因此建议先创建表
        ,然后将所有数据复制到新表*6更改表集表空间不安全操作,但您可能根本不需要或经常不需要它*7更改表添加列x个如果没有设置为非空设置默认值主键唯一*8更改表添加列设置默认值添加列并设置默认值 不安全操作,因为您要花时间在迁移中填充表中的所有值,因此建议alter table add column然后填充column,然后设置默认值*9alter table add column set不为空+/-不安全操作,因为没有设置默认值就无法工作,所以建议先设置alter table add column,然后填充column,再设置alter table alter column set not null*和**10alter table add column主键 添加索引并添加约束不安全操作,因为您要花时间迁移到创建索引,所以建议使用alter table add column然后并发创建索引然后使用索引
        alter table add constraint primary key 11alter table add column unique添加索引并添加约束不安全操作,因为您要花时间迁移到创建索引,所以建议使用alter table add column然后并发创建索引,然后使用索引alter table add constraint unique 12alter table alter column type+/-不安全操作,因为您需要花时间迁移以检查列中的所有项是否有效或更改类型,但某些操作可能是安全的****13alter table alter column set不为空+/-不安全操作,因为您要花时间在迁移过程中检查列中的所有项是否不为空**14更改表alter column drop不为空x安全操作15更改表格更改列设置默认值x安全操作16更改表alter column drop defaultx安全操作17alter table drop columnx安全操作,因为您的业务逻辑不应该在迁移时对该列进行操作,但是最好alter table alter column drop not nullalter table drop constraintdrop indexbefore*和*****18更改表重命名列新建列并复制 不安全操作,同时操作两列的业务逻辑太难编写,因此建议alter table create column然后将所有数据复制到新列*19更改表添加约束检查添加为无效并验证不安全操作,因为您要花时间在迁移中检查约束20更改表格删除约束检查)x安全操作21alter table add constraint外键 添加为无效并验证不安全操作,因为您要花时间在迁移中检查约束,请锁定两个表22alter table drop constraint外键)x安全操作,锁定两张桌子23alter table add constraint主键 添加索引并添加约束不安全的操作,因为您要花时间在迁移中创建索引***24 alter table drop constraint主键)x安全操作***25alter table add constraint unique添加索引并添加约束不安全的操作,因为您要花时间在迁移中创建索引***26alter table drop constraintunique)x安全操作***27创建索引同时创建索引不安全操作,因为您要花时间在迁移中创建索引28删除索引x同时删除索引安全操作***

        *:在没有停机的情况下进行生产迁移的要点是,您的代码应该在迁移前后正常工作,让我们仔细看一下下面这一点

        **:Postgres将检查列中所有需要时间的项是否为空,让我们仔细查看下面的这一点

        ***:当您跳过alter table add constraint unique using index并且仍然不清楚与concurrent的区别时,postgres将有相同的行为,除了锁的区别,让我们仔细看下面这一点

        ***:让我们仔细看下面这一点

        *****:如果使用python manage.py makemigrations--check检查ci上的迁移,则在不创建迁移的情况下不能删除代码中的列,因此在这种情况下,可以使用后迁移流:在所有实例上应用代码,然后迁移数据库

        处理迁移前后应该工作的逻辑
        新的和正在删除的型号和列

        迁移:创建序列删除序列创建表删除表更改表添加列更改表删除列

        这种迁移非常安全,因为在迁移之前,您的逻辑无法处理这些数据

        工作逻辑的变化

        迁移:将表重命名更改为更改表集表空间更改表重命名列

        对于这种迁移,实现逻辑太难了,所有实例都能正常工作,因此有两种方法来处理它:

        1. 创建新表/列、复制现有数据、删除旧表/列
        2. 停机时间
        3. < > >
          使用默认值创建列

          迁移:alter table add column set default

          使用默认值创建列的标准django行为是使用默认值填充所有值。django不会永久使用数据库默认值,因此当您添加具有默认值的新列时,django将创建具有默认值的列并立即删除此默认值,例如,新的默认值将来自django代码。在这种情况下,并非所有实例应用的迁移都已更新,此时表中的新行将没有默认值,可能需要在此之后更新可为空的值。因此,要避免这种情况,最好的方法是避免使用默认的创建列和拆分列创建(默认为新行)以及将数据填充到两个迁移(使用部署)。

          处理非空约束

          postgres在应用非空约束时检查所有列项非空,很遗憾,不能将此检查推迟到无效时。但我们有一些技巧和选择。

          1. 在负载最小时运行迁移,以避免锁定的负面影响。
          2. 设置语句超时并尝试为小表设置不为空约束。
          3. 使用check(column is not null)约束,而不是使用nextvalidate constraint的支持无效的选项,有关详细信息,请参阅文章https://medium.com/doctorib engineering/adding-a-not-null-constraint-on-pg-faster-with-minimal-locking-38b2c00c4d1c
          4. < > >

            处理唯一性约束

            Postgres有两种唯一性方法:创建唯一索引更改表添加约束唯一-都在内部使用唯一索引。区别在于,我发现不能同时对约束应用删除索引。但是仍然不清楚除了锁中的差异之外,删除索引同时删除索引有什么区别,但是正如您之前看到的,两者都标记为安全-您不会花时间在删除索引中,只是等待锁定。因此,当django使用约束来获得唯一性时,我们也有安全使用约束的技巧。

            处理alter table alter column type

            接下来的操作是安全的:

            1. varchar(较少)varchar(较多)其中,较少<;较多
            2. varchar(任意)文本
            3. 数值(更少,相同)数值(更多,相同)其中less<;more and same==相同
            4. < > >

              对于其他操作,建议创建新列并将数据复制到其中。有些类型也可以是安全的,但你应该自己检查。

              django pg零停机迁移更改日志

              0.5

              • 提取零停机模式到mixin,以允许将此逻辑与其他后端一起使用
              • 将模块从django_zero_down_migrations_postgres_backend移动到django_zero_down_migrations.backends.postgres
              • 标记django_zero_dowtime_migrations_postgres_backend模块为已弃用
              • 添加Postgis后端支持
              • 自述文件改进

              0.4

              • Zero_Downtime_Migrations_Lock_TimeoutZero_Downtime_Migrations_Statement_Timeout的默认值从0ms更改为None以获得与默认Postgres超时相同的默认Django行为
              • 使用默认选项向文档添加更新
              • 使用最佳选项为文档添加更新
              • 修复了在默认情况下添加可为空字段没有错误和警告问题
              • 添加指向文档的链接,其中包含问题描述和安全的替代方案错误和警告的用法
              • 使用类型转换解决方案为文档添加更新

              0.3

              • 使用meta.indexmeta.constraints属性添加django 2.2支持
              • 修复regexp的python弃用警告
              • 删除未使用的TimeoutException
              • 改进自述文件和pyPI说明

              0.2

              • 当语句超时设置为全局时,添加允许对长操作(如在约束验证时创建索引)禁用语句超时的选项

              0.1.1

              • 添加详细说明内容类型

              0.1

              • 首次发布:
                • 将默认SQL查询替换为更安全的查询
                • 添加语句超时和锁定超时的选项
                • 添加不为空的选项约束行为
                • 添加不安全操作限制选项

              欢迎加入QQ群-->: 979659372 Python中文网_新手群

              推荐PyPI第三方库


热门话题
java无法使用JAXB配置Moxy   java如何让我的简单Swing telnet客户端正确显示字符?   java中从可运行线程调用主线程的多线程处理   java数据源。EBJ3会话bean中的getConnection()   使用java和正则表达式从xml文件提取值时出现问题   java定制Jersy胡须Mvc   在Java中,“限制并发”是什么意思?   java有没有更干净的方法可以在这里使用Optional,而不在三个地方返回“NA”?   java Tomcat启动,然后崩溃,除非我打电话   java理解客户机和服务器   java时间戳将在视图对象>实体转换期间丢失   如何在java中返回布尔值(基元)?   java使用spring mvc设置日志记录,希望仅对我的代码进行跟踪/调试   用Jackson解析嵌套对象