尊重数据库锁的Django postgresql后端,用于应用迁移。
项目描述
django-pg-zero-downtime-migrations
Django postgresql后端,在数据库锁的约束下应用迁移。
安装
pip install django-pg-zero-downtime-migrations
用法
要为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
注意:此后端只为迁移(模式和相关
RunSQL
操作)提供零停机改进,但不包括RunPython
操作,在其他用途上与标准django后端工作相同。
注意:此包处于测试版,请在生产环境中应用迁移之前检查您的迁移SQL,并提交问题以解决任何疑问。
与标准django后端的差异
此后端提供相同的结果状态(除django < 5.0使用ZERO_DOWNTIME_MIGRATIONS_KEEP_DEFAULT=True
外),但采用不同的方式和额外的保证来避免挂起的表锁定。
此后端在迁移(除RunPython
操作外)不使用事务,因为并非所有SQL修复都可以在事务中运行,并且这有助于避免复杂迁移的死锁。因此,当您的迁移在迁移文件操作中途失败时,您需要手动修复数据库状态(而不是潜在的停机时间)。因此,最佳实践是将迁移模块尽可能保持小巧。此外,ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL=True
允许自动化手动数据库状态的修复。
部署流程
存在零停机时间部署的要求
- 我们有一个数据库;
- 我们有多个实例与应用程序关联 - 即使您重启其中一个实例,应用程序也应始终可用;
- 在实例之前有均衡器;
- 我们的应用程序在迁移之前、期间和之后都运行良好 - 旧应用程序与旧的和新的数据库模式版本兼容;
- 我们的应用程序在实例更新之前、期间和之后都运行良好 - 旧的和新的应用程序版本与新的数据库模式版本兼容。
流程
- 应用迁移
- 从均衡器断开实例,重启它并重新连接到均衡器 - 逐个对所有实例执行此操作
如果我们的部署不符合零停机时间部署规则,则将其拆分为更小的部署。
设置
ZERO_DOWNTIME_MIGRATIONS_LOCK_TIMEOUT
为需要ACCESS EXCLUSIVE
锁的SQL语句应用lock_timeout
,默认为None
ZERO_DOWNTIME_MIGRATIONS_LOCK_TIMEOUT = '2s'
允许的值
None
- 使用当前的PostgreSQL设置- 其他 - 应用超时,
0
及其等效值表示将禁用超时
ZERO_DOWNTIME_MIGRATIONS_STATEMENT_TIMEOUT
为需要ACCESS EXCLUSIVE
锁的SQL语句应用statement_timeout
,默认为None
ZERO_DOWNTIME_MIGRATIONS_STATEMENT_TIMEOUT = '2s'
允许的值
None
- 使用当前的PostgreSQL设置- 其他 - 应用超时,
0
及其等效值表示将禁用超时
ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT
将statement_timeout
设置为0ms
,适用于需要SHARE UPDATE EXCLUSIVE
锁的SQL语句,这在全局启用statement_timeout
且尝试运行长时间运行的操作(如索引创建或约束验证)时很有用,默认为False
ZERO_DOWNTIME_MIGRATIONS_FLEXIBLE_STATEMENT_TIMEOUT = True
ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE
启用的选项不允许运行可能不安全的迁移,默认为False
ZERO_DOWNTIME_MIGRATIONS_RAISE_FOR_UNSAFE = True
ZERO_DOWNTIME_DEFERRED_SQL
定义应用延迟SQL的方式,默认为True
ZERO_DOWNTIME_DEFERRED_SQL = True
允许的值
True
- 以类似Django默认方式运行延迟SQLFalse
- 尽可能快地运行延迟SQL
ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL
定义幂等模式,默认为False
ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL = False
允许的值
True
- 跳过已应用的SQL迁移False
- 标准非原子Django行为
由于此后端不使用事务进行迁移,任何失败的迁移都可能导致中间状态中进程停止。为了避免手动模式操作,幂等模式允许在修复问题(例如数据问题或长时间运行的CRUD查询)后重新运行失败的迁移。
注意:幂等模式检查仅依赖于名称和索引以及约束的有效状态,因此它可以忽略名称冲突,并建议不要用于CI检查。
ZERO_DOWNTIME_MIGRATIONS_EXPLICIT_CONSTRAINTS_DROP
定义在删除表或列之前删除外键、唯一约束和索引的方式,默认为True
ZERO_DOWNTIME_MIGRATIONS_EXPLICIT_CONSTRAINTS_DROP = True
允许的值
True
- 在删除表之前删除与此表相关的外键,在删除列之前删除与此列相关的外键、此列上的唯一约束和此列使用的索引。False
- 标准Django行为,将使用CASCADE
模式删除约束(也可以显式删除某些约束)。
在删除表或列之前显式删除约束和索引,可以在ACCESS EXCLUSIVE
锁的情况下分割仅更改模式的操作,并删除物理文件,这可能需要很长时间并导致停机。
ZERO_DOWNTIME_MIGRATIONS_KEEP_DEFAULT
定义在添加新列时如何在数据库级别保留或删除默认值,默认值为False
ZERO_DOWNTIME_MIGRATIONS_KEEP_DEFAULT = False
允许的值
True
- 在添加具有代码默认值的列之后,此默认值不会被删除,此选项允许使用ALTER TABLE ADD COLUMN SET DEFAULT NOT NULL
作为更简单、更高效的安全操作,这比在数据库级别创建无默认值的列然后填充列要简单得多。False
- 在添加具有代码默认值的列之后,此默认值将被删除,这是Django的标准行为。
注意:此选项仅适用于Django < 5.0,在Django 5.0+中应使用显式的
db_default
。
PgBouncer和超时
如果您使用PgBouncer并希望超时按预期工作,您需要确保使用session pool_mode运行迁移或使用直接数据库连接。
它如何工作
Postgres表级别锁
Postgres在表级别有不同的锁,可能会相互冲突https://postgresql.ac.cn/docs/current/static/explicit-locking.html#LOCKING-TABLES
ACCESS SHARE |
ROW SHARE |
ROW EXCLUSIVE |
SHARE UPDATE EXCLUSIVE |
SHARE |
SHARE ROW EXCLUSIVE |
EXCLUSIVE |
ACCESS EXCLUSIVE |
|
---|---|---|---|---|---|---|---|---|
ACCESS SHARE |
X | |||||||
ROW SHARE |
X | X | ||||||
ROW EXCLUSIVE |
X | X | X | X | ||||
SHARE UPDATE EXCLUSIVE |
X | X | X | X | X | |||
SHARE |
X | X | X | X | X | |||
SHARE ROW EXCLUSIVE |
X | X | X | X | X | X | ||
EXCLUSIVE |
X | X | X | X | X | X | X | |
ACCESS EXCLUSIVE |
X | X | X | X | X | X | X | X |
迁移和业务逻辑锁
让我们将此锁分割为迁移和业务逻辑操作。
- 迁移操作在单个线程中同步工作,并涵盖模式迁移(数据迁移与业务逻辑操作冲突,就像业务逻辑并发冲突一样)。
- 业务逻辑操作是并发的。
迁移锁
锁 | 操作 |
---|---|
ACCESS EXCLUSIVE |
CREATE SEQUENCE 、DROP SEQUENCE 、CREATE TABLE 、DROP TABLE 、ALTER TABLE **、DROP INDEX |
SHARE |
CREATE INDEX |
SHARE UPDATE EXCLUSIVE |
CREATE INDEX CONCURRENTLY 、DROP INDEX CONCURRENTLY 、ALTER TABLE VALIDATE CONSTRAINT *** |
**: CREATE SEQUENCE
、DROP SEQUENCE
、CREATE TABLE
、DROP TABLE
不应该有冲突,因为您的业务逻辑还不应该操作已创建的表,也不应该操作已删除的表。
**: 不是所有的ALTER TABLE
操作都使用ACCESS EXCLUSIVE
锁,但所有当前的Django迁移都使用它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.py和https://postgresql.ac.cn/docs/current/static/sql-altertable.html。
***: Django没有VALIDATE CONSTRAINT
逻辑,但我们将使用它来处理某些情况。
业务逻辑锁
锁 | 操作 | 与锁冲突 | 与操作冲突 |
---|---|---|---|
ACCESS SHARE |
SELECT |
ACCESS EXCLUSIVE |
ALTER TABLE 、DROP INDEX |
ROW SHARE |
SELECT FOR UPDATE |
ACCESS EXCLUSIVE 、EXCLUSIVE |
ALTER TABLE 、DROP INDEX |
ROW EXCLUSIVE |
INSERT 、UPDATE 、DELETE |
ACCESS EXCLUSIVE 、EXCLUSIVE 、SHARE ROW EXCLUSIVE 、SHARE |
ALTER TABLE 、DROP INDEX 、CREATE INDEX |
因此,您可以发现所有Django对现有表的架构更改都与业务逻辑冲突,但幸运的是,它们在通常情况下都是安全的或具有安全的替代方案。
Postgres行级别锁
由于业务逻辑主要与表行交互,因此了解行级别的锁定冲突也很重要https://postgresql.ac.cn/docs/current/static/explicit-locking.html#LOCKING-ROWS
锁 | 用于密钥共享 |
用于共享 |
用于无密钥更新 |
用于更新 |
---|---|---|---|---|
用于密钥共享 |
X | |||
用于共享 |
X | X | ||
用于无密钥更新 |
X | X | X | |
用于更新 |
X | X | X | X |
主要观点是,如果你有两个事务更新一行,则第二个事务将等待第一个事务完成。因此,为了业务逻辑和数据迁移,最好避免更新整个表,而使用批量操作。
注意:批量操作也可以运行得更快,因为PostgreSQL可以使用更优的执行计划来处理小数据范围,并使用索引。
事务FIFO等待
在有趣的文章中找到了相同的图表 http://pankrat.github.io/2015/django-migrations-without-downtimes/。
在这个图表中,我们可以提取几个指标
- 操作时间 - 改变模式所花费的时间,在涉及大量行表的长时间运行操作,如
CREATE INDEX
或ALTER TABLE ADD CONSTRAINT
的情况下,所以你需要一个安全等效的。 - 等待时间 - 你的迁移将等待直到所有事务完成,因此对于长时间运行的操作/事务,如分析,存在问题,因此你需要避免它或在迁移期间禁用它。
- 每秒查询次数 + 执行时间 + 连接池 - 如果执行许多查询,特别是长时间运行的查询,它们可以消耗所有可用的数据库连接,直到锁被释放,所以你需要不同的优化:在不太忙的时候运行迁移,减少查询数量和执行时间,分割数据。
- 一个事务中操作过多 - 你在所有前面的点上都有一个问题,所以如果你在一个事务中有很多操作,那么你更有可能遇到这个问题,所以你需要避免在单个事务中同时进行太多操作(甚至根本不在事务中运行,但要注意操作失败时)。
处理超时
PostgreSQL有两个设置来处理图中的等待时间
和操作时间
:lock_timeout
和statement_timeout
。
SET lock_timeout TO '2s'
允许你在运行迁移之前有长时间运行的查询/事务时避免停机时间(https://postgresql.ac.cn/docs/current/static/runtime-config-client.html#GUC-LOCK-TIMEOUT)。
SET statement_timeout TO '2s'
允许你在有长时间运行的迁移查询时避免停机时间(https://postgresql.ac.cn/docs/current/static/runtime-config-client.html#GUC-STATEMENT-TIMEOUT)。
死锁
死锁没有停机问题,但一个事务中的操作过多会获取大多数冲突的锁,并在事务提交或回滚后才能释放。所以避免ACCESS EXCLUSIVE
锁操作和单事务中的长时间操作是个好主意。死锁也会在部署生产部署时使你的迁移卡住,因为不同的表将被锁定,例如,对于使用ACCESS EXCLUSIVE
锁的两个表的外键。
行和值存储
PostgreSQL以不同的方式存储不同类型的数据。如果你尝试将一种类型转换为另一种类型,并且它以不同的方式存储,那么PostgreSQL将重写所有值。幸运的是,某些类型以相同的方式存储,PostgreSQL不需要做任何事情来更改类型,但在某些情况下,PostgreSQL需要检查所有值是否符合新类型的限制,例如字符串长度。
多版本并发控制
有关文档 https://postgresql.ac.cn/docs/current/static/mvcc-intro.html,PostgreSQL使用多版本模型来维护数据一致性。这意味着每个SQL语句都看到了数据的快照。它在添加和删除列时无需任何索引具有优势,约束和默认值不会更改现有数据,新版本的数据将在INSERT
和UPDATE
时创建,删除只是标记记录为已过期。所有垃圾都会在稍后通过VACUUM
或AUTO VACUUM
收集。
Django迁移技巧
任何模式更改都可以通过创建新表并将数据复制到其中来处理,但这可能需要很长时间。
# | 名称 | 安全 | 安全替代方案 | 描述 |
---|---|---|---|---|
1 | 创建序列 |
X | 安全操作,因为您的业务逻辑在迁移时间不应与新序列一起操作 * | |
2 | 删除序列 |
X | 安全操作,因为您的业务逻辑在迁移时间不应与此序列一起操作 * | |
3 | 创建表 |
X | 安全操作,因为您的业务逻辑在迁移时间不应与新表一起操作 * | |
4 | 删除表 |
X | 安全操作,因为您的业务逻辑在迁移时间不应与此表一起操作 * | |
5 | 修改表重命名为 |
使用可更新的视图 | 不安全操作,因为同时操作两个表的业务逻辑编写太难,因此建议使用临时可更新视图并在事务中切换名称 * | |
6 | 修改表设置表空间 |
添加新表并复制数据 | 不安全操作,但可能您根本不需要或很少需要 * | |
7 | 修改表添加列 |
X | 如果没有SET NOT NULL 、PRIMARY KEY 、UNIQUE ,则为安全操作 * |
|
8 | 修改表添加列设置默认值 |
X | 安全操作,然而如果代码默认值用于NOT NULL 、db_default 或NULL 则没有问题 * |
|
9 | 修改表添加列设置非空 |
+/- | 不安全操作,因为没有SET DEFAULT 将无法工作,或者在迁移后旧代码可以插入行而不创建新列并引发异常,因此建议使用ALTER TABLE ADD COLUMN SET DEFAULT 与db_default 或ALTER TABLE ADD COLUMN 然后填充列然后ALTER TABLE ALTER COLUMN SET NOT NULL * 和 ** |
|
10 | 修改表添加列为主键 |
添加索引和添加约束 | 不安全操作,因为您在迁移到CREATE INDEX 上花费时间,所以建议先使用ALTER TABLE ADD COLUMN 然后CREATE INDEX CONCURRENTLY 然后ALTER TABLE ADD CONSTRAINT PRIMARY KEY USING INDEX *** |
|
11 | 修改表添加列设置唯一 |
添加索引和添加约束 | 不安全操作,因为您在迁移到CREATE INDEX 上花费时间,所以建议先使用ALTER TABLE ADD COLUMN 然后CREATE INDEX CONCURRENTLY 然后ALTER TABLE ADD CONSTRAINT UNIQUE USING INDEX *** |
|
12 | 修改表修改列类型 |
+/- | 不安全操作,因为您在迁移时花费时间检查列中的所有项是否有效或更改类型,但某些操作可能是安全的 **** | |
13 | 修改表修改列设置非空 |
在之前添加检查约束 | 不安全操作,因为您在迁移时花费时间检查列中的所有项是否为NOT NULL ,因此建议先使用ALTER TABLE ADD CONSTRAINT CHECK 然后ALTER TABLE VALIDATE CONSTRAINT 然后ALTER TABLE ALTER COLUMN SET NOT NULL ** |
|
14 | 修改表修改列删除非空 |
X | 安全操作 | |
15 | 修改表修改列设置默认值 |
X | 安全操作 | |
16 | 修改表修改列删除默认值 |
X | 安全操作 | |
17 | 修改表删除列 |
X | 安全操作,因为您的业务逻辑在迁移时间不应与此列一起操作,然而更好的做法是在* 和 *****之前使用ALTER TABLE ALTER COLUMN DROP NOT NULL 、ALTER TABLE DROP CONSTRAINT 和DROP INDEX * |
|
18 | 修改表重命名列 |
使用可更新的视图 | 不安全操作,因为同时操作两个列的业务逻辑编写太难,因此建议使用临时可更新视图并在事务中切换名称 * | |
19 | 修改表添加约束检查 |
添加为无效并验证 | 不安全操作,因为您在迁移时花费时间检查约束 * | |
20 | ALTER TABLE DROP CONSTRAINT (CHECK ) |
X | 安全操作 | |
21 | 修改表添加外键约束 |
添加为无效并验证 | 不安全操作,因为您在迁移时花费时间检查约束,锁定两个表 * | |
22 | ALTER TABLE DROP CONSTRAINT (FOREIGN KEY ) |
X | 安全操作,锁定两个表 | |
23 | 修改表添加主键约束 |
添加索引和添加约束 | 不安全操作,因为你在迁移过程中创建索引 *** | |
24 | ALTER TABLE DROP CONSTRAINT (PRIMARY KEY ) |
X | 安全操作 *** | |
25 | ALTER TABLE ADD CONSTRAINT UNIQUE |
添加索引和添加约束 | 不安全操作,因为你在迁移过程中创建索引 *** | |
26 | ALTER TABLE DROP CONSTRAINT (UNIQUE ) |
X | 安全操作 *** | |
27 | ALTER TABLE ADD CONSTRAINT EXCLUDE |
添加新表并复制数据 | ||
28 | ALTER TABLE DROP CONSTRAINT (EXCLUDE) |
X | ||
29 | CREATE INDEX |
CREATE INDEX CONCURRENTLY |
不安全操作,因为你在迁移过程中创建索引 | |
30 | DROP INDEX |
X | DROP INDEX CONCURRENTLY |
安全操作 *** |
31 | CREATE INDEX CONCURRENTLY |
X | 安全操作 | |
32 | DROP INDEX CONCURRENTLY |
X | 安全操作 *** |
*: 在生产环境中进行迁移且不停机的情况下,你的新旧代码应该在迁移前后正确运行,让我们在处理迁移前后的逻辑部分中仔细研究这一点。
**: postgres将检查所有在NOT NULL
列中耗时较多的项,让我们在处理NOT NULL
约束部分中仔细研究这一点。
***: 当你跳过ALTER TABLE ADD CONSTRAINT UNIQUE USING INDEX
时,postgres将表现出相同的操作,而且与CONCURRENTLY
的差异仍然不清楚,除了锁定方面的差异,让我们在处理UNIQUE
约束部分中仔细研究这一点。
****: 让我们在处理ALTER TABLE ALTER COLUMN TYPE
部分中仔细研究这一点。
*****: 如果你使用python manage.py makemigrations --check
在CI上检查迁移,你无法在不创建迁移的情况下删除列,所以在这种情况下,你可以使用有用的回滚迁移流程:在所有实例上应用代码,然后迁移数据库
处理迁移前后的逻辑
添加和删除模型和列
迁移:CREATE SEQUENCE
,DROP SEQUENCE
,CREATE TABLE
,DROP TABLE
,ALTER TABLE ADD COLUMN
,ALTER TABLE DROP COLUMN
。
这些迁移相当安全,因为你的逻辑在迁移之前不与这些数据一起工作
重命名模型
迁移:ALTER TABLE RENAME TO
。
标准的django方法不允许同时使用旧代码和新代码以及旧表名和新表名进行操作,希望以下方法可以通过将迁移拆分为几个步骤来重命名表
- 提供代码更改,但用SeparateDatabaseAndState sql操作替换标准的迁移,该操作在
transaction
中重命名表并创建具有旧表名的可更新视图。- 旧代码可以通过旧名称使用可更新视图
- 新代码可以通过新名称使用表
- 在新的代码部署后,旧代码不再使用,因此我们可以删除视图
- 新代码可以与重命名的表一起使用
重命名列
迁移:ALTER TABLE RENAME COLUMN
。
标准的django方法不允许同时使用旧代码和新代码以及旧列名和新列名进行操作,希望以下方法可以通过将迁移拆分为几个步骤来重命名列
- 提供代码更改,但用SeparateDatabaseAndState sql操作替换标准的迁移,该操作在
transaction
中重命名列,重命名表为临时表,并创建具有旧表名和旧列及新列的可更新视图。- 旧代码可以与新的可更新视图一起使用并使用旧列
- 新代码可以与新的可更新视图一起工作并使用新列
- 新代码部署后,旧代码不再使用,因此在事务中我们可以删除视图并将表重命名回来
- 新代码可以与重命名的列一起工作
工作逻辑的更改
迁移:`ALTER TABLE SET TABLESPACE`,`ALTER TABLE ADD CONSTRAINT EXCLUDE`。
对于这次迁移,实现对所有实例都正确的逻辑太难,因此有两种处理方式
- 创建新表,复制现有数据,删除旧表
- 停机时间
创建非空列
迁移:`ALTER TABLE ADD COLUMN NOT NULL`。
Postgres不允许在表不为空且未提供`DEFAULT`的情况下创建带有`NOT NULL`的列。因此,您想使用`ALTER TABLE ADD COLUMN DEFAULT NOT NULL`。Django有两种创建列默认值的方法:代码中的`default`和针对Django 5.0+的`db_default`。它们之间对我们来说的主要区别在于它们在迁移和迁移后旧代码插入处理方面的操作
`default`迁移和业务逻辑SQL
-- migration
ALTER TABLE tbl ADD COLUMN new_col integer DEFAULT 0 NOT NULL;
ALTER TABLE tbl ALTER COLUMN new_col DROP DEFAULT;
-- business logic
INSERT INTO tbl (old_col) VALUES (1); -- old code inserts fail
INSERT INTO tbl (old_col, new_col) VALUES (1, 1); -- new code inserts work fine
`db_default`迁移和业务逻辑SQL
-- migration
ALTER TABLE tbl ADD COLUMN new_col integer DEFAULT 0 NOT NULL;
-- business logic
INSERT INTO tbl (old_col) VALUES (1); -- old code inserts work fine with default
INSERT INTO tbl (old_col, new_col) VALUES (1, 1); -- new code inserts work fine
`db_default`是应用默认值最稳健的方法,并且它与`NOT NULL`约束也配合得很好。在django<5.0中,您可以使用`ZERO_DOWNTIME_MIGRATIONS_KEEP_DEFAULT=True`来模拟`default`字段的`db_default`行为。
处理`NOT NULL`列约束
当您应用`ALTER TABLE ALTER COLUMN SET NOT NULL`时,Postgres会检查所有列值是否为`NOT NULL`(全表扫描),如果存在适当的有效的`CHECK CONSTRAINT`,则此检查将被跳过。因此,要安全地将现有列设置为`NOT NULL`,您可以遵循以下步骤
- `ALTER TABLE ADD CONSTRAINT CHECK (column IS NOT NULL) NOT VALID` - 为列创建无效的检查约束,此操作仅对表元数据更新采取`ACCESS EXCLUSIVE`锁
- `ALTER TABLE VALIDATE CONSTRAINT` - 验证约束,此时所有列值应为`NOT NULL`,此操作在完成全表扫描之前采取`SHARE UPDATE EXCLUSIVE`锁
- `ALTER TABLE ALTER COLUMN SET NOT NULL` - 设置列`NOT NULL`,如果存在适当的有效的`CHECK CONSTRAINT`,则不检查列值,在这种情况下,此操作仅对表元数据更新采取`ACCESS EXCLUSIVE`锁
- `ALTER TABLE DROP CONSTRAINT` - 清理重复列`NOT NULL`的`CHECK CONSTRAINT`,此操作仅对表元数据更新采取`ACCESS EXCLUSIVE`锁
处理`UNIQUE`约束
Postgres有两种处理唯一性的方法:《CREATE UNIQUE INDEX》和《ALTER TABLE ADD CONSTRAINT UNIQUE》 - 都使用唯一索引。我们可以找到的不同之处在于我们不能为约束应用`DROP INDEX CONCURRENTLY`。然而,关于`DROP INDEX`和`DROP INDEX CONCURRENTLY`的差异仍然不清楚,除了锁的差异之外,但正如我们之前看到的,两者都被标记为安全 - 我们不花费时间在`DROP INDEX`上,只是等待锁。因此,由于Django使用约束来处理唯一性,我们也有一些技巧来安全地使用约束。
处理`ALTER TABLE ALTER COLUMN TYPE`
以下操作是安全的
- `varchar(LESS)`到`varchar(MORE)`,其中LESS < MORE
- `varchar(ANY)`到`text`
- `numeric(LESS, SAME)`到`numeric(MORE, SAME)`,其中LESS < MORE和SAME == SAME
对于其他操作,建议创建新列并将数据复制到其中。例如,某些类型也可以安全,但您应该自行检查。
django-pg-zero-downtime-migrations 更新日志
0.16
- 将`ADD COLUMN DEFAULT NULL`更改为代码默认的安全操作
- 将`ADD COLUMN DEFAULT NOT NULL`更改为Django 5.0+中`db_default`的安全操作
- 添加了
ZERO_DOWNTIME_MIGRATIONS_KEEP_DEFAULT
设置,并将使用此设置的ADD COLUMN DEFAULT NOT NULL
改为安全操作,以支持django < 5.0 - 添加了
ZERO_DOWNTIME_MIGRATIONS_EXPLICIT_CONSTRAINTS_DROP
设置,并在删除列或表之前添加了删除约束和索引 - 修复了idempotent模式下的sqlmigrate
- 修复了带有include参数的创建唯一约束的问题
- 修复了idempotent模式测试
- 更新了不安全的迁移链接到文档
- 将修复的代码更新到最新的django版本
- 更新测试镜像为ubuntu 24.04
- 改进了README
0.15
- 添加了idempotent模式和
ZERO_DOWNTIME_MIGRATIONS_IDEMPOTENT_SQL
设置 - 修复了django 3.2的退化问题,缺少
skip_default_on_alter
方法 - 改进了README
- 更新了发布github action
0.14
- 修复了延迟的sql错误
- 添加了对django 5.0的支持
- 添加了对python 3.12的支持
- 添加了对postgres 16的支持
- 删除了对postgres 11的支持
- 删除了
ZERO_DOWNTIME_MIGRATIONS_USE_NOT_NULL
设置 - 标记
migrate_isnotnull_check_constraints
命令已弃用
0.13
- 添加了对django 4.2的支持
- 标记django 3.2支持已弃用
- 标记django 4.0支持已弃用
- 标记django 4.1支持已弃用
- 标记postgres 11支持已弃用
- 删除了对postgres 10的支持
- 更新测试docker镜像为ubuntu 22.04
0.12
- 添加了
serial
和integer
、bigserial
和bigint
、smallserial
和smallint
,与安全迁移相同的类型更改 - 修复了
AutoField
类型更改和并发插入问题,适用于django < 4.1
- 添加了序列删除和创建的超时,因为它们可以与
CASCADE
关键字一起使用并影响其他表 - 添加了对django 4.1的支持
- 添加了对python 3.11的支持
- 添加了对postgres 15的支持
- 标记postgres 10支持已弃用
- 删除了对django 2.2的支持
- 删除了对django 3.0的支持
- 删除了对django 3.1的支持
- 删除了对postgres 9.5的支持
- 删除了对postgres 9.6的支持
- 为pull请求添加了github actions检查
0.11
- 修复了在保留
db_table
时重命名模型引发ALTER_TABLE_RENAME
错误的问题 #26 - 添加了对django 3.2的支持
- 添加了对django 4.0的支持
- 添加了对python 3.9的支持
- 添加了对python 3.10的支持
- 添加了对postgres 14的支持
- 标记django 2.2支持已弃用
- 标记django 3.0支持已弃用
- 标记django 3.1支持已弃用
- 标记python 3.6支持已弃用
- 标记python 3.7支持已弃用
- 标记postgres 9.5支持已弃用
- 标记postgres 9.6支持已弃用
- 迁移到github actions进行测试
0.10
- 添加了对django 3.1的支持
- 添加了对postgres 13的支持
- 删除了对python 3.5的支持
- 更新了测试环境
0.9
- 修复了十进制到浮点迁移错误
- 修复了django 3.0.2+测试
0.8
- 添加了对django 3.0的支持
- 添加了并发索引创建和删除操作
- 添加了对排除约束的支持,作为不安全操作
- 删除了对postgres 9.4的支持
- 删除了对django 2.0的支持
- 删除了对django 2.1的支持
- 删除了已弃用的
django_zero_downtime_migrations_postgres_backend
模块
0.7
- 添加了对python 3.8的支持
- 添加了对postgres特定索引的支持
- 提高了测试清晰度
- 修复了管理命令的regexp转义警告
- 修复了样式检查
- 改进了README
- 标记python 3.5支持已弃用
- 标记postgres 9.4支持已弃用
- 标记django 2.0支持已弃用
- 标记django 2.1支持已弃用
0.6
- 标记
ZERO_DOWNTIME_MIGRATIONS_USE_NOT_NULL
选项对postgres 12+已弃用 - 添加了将迁移到实际
NOT NULL
从CHECK IS NOT NULL
约束的管理命令 - 添加了对pg 12、pg 11 root、pg 11兼容not null约束、pg 11标准not null约束和pg 10、9.6、9.5、9.4、postgis数据库的集成测试
- 修复了通过pg_attribute删除和创建兼容not null约束的bug
- 最小化了在迁移模块中的操作之间的延迟sql执行带来的副作用
- 添加了对postgres 12安全
NOT NULL
约束创建的支持 - 添加了创建安全
NOT NULL
约束以增加对pg_catalog.pg_attribute
额外权限的支持,当启用ZERO_DOWNTIME_MIGRATIONS_USE_NOT_NULL=USE_PG_ATTRIBUTE_UPDATE_FOR_SUPERUSER
选项时 - 将带有
null=False
参数和兼容CHECK IS NOT NULL
约束选项的AddField
标记为不安全操作,并避免在这种情况下使用ZERO_DOWNTIME_MIGRATIONS_USE_NOT_NULL
值 - 向包中添加了版本
- 修复了pypi README中的图片链接
- 改进了README
0.5
- 将zero-downtime-schema提取为mixin,以允许与其他后端一起使用此逻辑
- 将模块从
django_zero_downtime_migrations_postgres_backend
移动到django_zero_downtime_migrations.backends.postgres
- 将
django_zero_downtime_migrations_postgres_backend
模块标记为已弃用 - 添加了对postgis后端的支持
- 改进了README
0.4
- 将
ZERO_DOWNTIME_MIGRATIONS_LOCK_TIMEOUT
和ZERO_DOWNTIME_MIGRATIONS_STATEMENT_TIMEOUT
的默认值从0ms
更改为None
,以与尊重默认postgres超时的默认django行为保持一致 - 添加了包含选项默认值的文档更新
- 添加了包含最佳选项使用方法的文档更新
- 修复了添加具有默认值的可空字段时没有错误和警告问题
- 添加了包含错误和警告的描述以及安全替代使用方法的文档链接
- 添加了包含类型转换解决方案的文档更新
0.3
- 添加了对django 2.2的支持,包括
Meta.indexes
和Meta.constraints
属性 - 修复了regexp的python弃用警告
- 删除了未使用的
TimeoutException
- 改进了README和PYPI描述
0.2
- 添加了允许在全局设置
statement_timeout
时禁用长操作(如约束验证时的索引创建)的statement_timeout
选项
0.1.1
- 添加了长描述内容类型
0.1
- 用更安全的查询替换了默认的sql查询
- 添加了
statement_timeout
和lock_timeout
的选项 - 添加了
NOT NULL
约束行为的选项 - 添加了限制不安全操作的选项
项目详情
哈希值 for django_pg_zero_downtime_migrations-0.16.tar.gz
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 47105e5a9bc677560a31ee6df5def66596b63e9dc063fa5ec152c42316e3c280 |
|
MD5 | a03bcdcbbe25211abade29fc394f96a6 |
|
BLAKE2b-256 | 3797032494ffc65811c154e062ab2820323f11f7be3600eee7cb3b91caa0fd4e |
哈希值 for django_pg_zero_downtime_migrations-0.16-py3-none-any.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 6b1dc75814514c651a8e3fc1b3d9eb4d9ea7b5d7f02267cb04d47c719d59c9ad |
|
MD5 | 7b6365f91e6aa47ded28bc61173d5b5e |
|
BLAKE2b-256 | ecb74882635a67cb9f0ea275ac042837e9802c9f048bb758a51199e62a44f7b8 |