会员登录 - 用户注册 - 设为首页 - 加入收藏 - 网站地图 Sentry 开发者贡献指南-数据库迁移!

Sentry 开发者贡献指南-数据库迁移

时间:2025-11-04 21:14:36 来源:益强数据堂 作者:域名 阅读:747次

Django 迁移是南数我们处理 Sentry 中数据库更改的方式。

Django 迁移官方文档:https://docs.djangoproject.com/en/2.2/topics/migrations/。据库

这些将涵盖了解迁移正在执行的迁移操作所需的大部分内容。

命令

请注意,南数对于所有这些命令,据库如果在 getsentry 存储库中,迁移您可以将 getsentry 替换为 sentry。南数

将您的据库数据库升级到最新

sentry upgrade 会自动更新你的迁移。您也可以运行 sentry django migrate 来直接访问迁移命令。迁移

将您的南数数据库移动到特定的迁移

当您要测试迁移时,这会很有帮助。据库

sentry django migrate - 请注意,迁移migration_name 可以是南数部分匹配,通常数字就是据库你所需要的。

例如:sentry django migrate sentry 0005

这也可用于回滚迁移。迁移如果你犯了错误,在开发中很有用。

为迁移生成 SQL

这对审查您的代码的人很有帮助,因为并不总是清楚 Django 迁移实际要做什么。

sentry django sqlmigrate

例如 sentry django sqlmigrate sentry 0003

生成迁移

这会根据您对模型所做的IT技术网更改自动为您生成迁移。

sentry django makemigrations

或者

sentry django makemigrations 用于一个指定的 app。

例如 sentry django makemigrations sentry

当您在 pr 中包含迁移时,还要为迁移生成 sql 并将其作为注释包含在内,以便您的审阅者可以更轻松地了解 Django 正在做什么。

您还可以使用 sentry django makemigrations --empty 生成空迁移。这对于数据迁移和其他自定义工作很有用。

将迁移合并到 master

合并到 master 时,您可能会注意到与 migrations_lockfile.txt 的冲突。这个文件是为了帮助我们避免将具有相同迁移编号的两个迁移合并到 master,如果您与它发生冲突,那么很可能有人在您之前提交了迁移。

指南

在运行迁移时,我们需要注意一些事项。

过滤器

如果(数据)迁移涉及大表或未索引的列,最好迭代整个表而不是使用 filter。例如:

复制EnvironmentProject.objects.filter(environment__name="none")  1.

因为 EnvironmentProject 行太多,这会一次将太多行带入内存。相反,我们应该使用 RangeQuerySetWrapperWithProgressBar 遍历所有 EnvironmentProject 行,因为它会分块进行。例如:

复制for env in RangeQuerySetWrapperWithProgressBar(EnvironmentProject.objects.all()):      if env.name == none:          # Do what you need  1.2.3.

我们通常更喜欢避免将 .filter 与 RangeQuerySetWrapperWithProgressBar 一起使用。由于它已经通过 id 对表进行排序,因此我们无法利用字段上的任何索引,并且可能会为每个块扫描大量行。b2b信息网这会运行得更慢,但我们通常更喜欢这样,因为它在更长的时间内平均负载,并使每个查询获取每个块的成本相当低。

索引

我们更喜欢使用 CREATE INDEX CONCURRENTLY 在现有的大型表上创建索引。当我们这样做时,我们无法在事务中运行迁移,因此使用 atomic = False 来运行这些很重要。

删除列/表

由于我们的部署过程,这很复杂。当我们部署时,我们运行迁移,然后推出应用程序代码,这需要一段时间。这意味着如果我们只是删除一个列或模型,那么 sentry 中的代码将查找这些列/表并在部署完成之前出错。在某些情况下,这可能意味着 Sentry 在部署完成之前很难停机。

为避免这种情况,请执行以下步骤:

如果列不是空的,则将其标记为空,并创建一个迁移。 部署。 从模型中删除列,但在迁移中确保我们只将状态标记为已删除(removed)。免费信息发布网 部署。 最后,创建一个删除列的迁移。

这是删除已经可以为空的列的示例。首先我们从模型中删除列,然后修改迁移以仅更新状态而不进行数据库操作。

复制operations = [          migrations.SeparateDatabaseAndState(              database_operations=[],              state_operations=[                  migrations.RemoveField(model_name="alertrule", name="alert_threshold"),                  migrations.RemoveField(model_name="alertrule", name="resolve_threshold"),                  migrations.RemoveField(model_name="alertrule", name="threshold_type"),              ],          )      ]  1.2.3.4.5.6.7.8.9.10.

一旦部署完成,我们就可以部署实际的列删除。这个 pr 只会有一个迁移,因为 Django 不再知道这些字段。请注意,反向 SQL 仅适用于开发人员,因此可以不分配默认值或进行任何类型的回填:

复制operations = [          migrations.SeparateDatabaseAndState(              database_operations=[                  migrations.RunSQL(                      """                      ALTERTABLE"sentry_alertrule"DROPCOLUMN"alert_threshold";                      ALTERTABLE"sentry_alertrule"DROPCOLUMN"resolve_threshold";                      ALTERTABLE"sentry_alertrule"DROPCOLUMN"threshold_type";                      """,                      reverse_sql="""                      ALTERTABLE"sentry_alertrule"ADDCOLUMN"alert_threshold"smallintNULL;                      ALTERTABLE"sentry_alertrule"ADDCOLUMN"resolve_threshold"intNULL;                      ALTERTABLE"sentry_alertrule"ADDCOLUMN"threshold_type"intNULL;                      """,                  )              ],              state_operations=[],          )      ]  1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.

如果该表在其他表中被引用为外键,则需要格外小心。在这种情况下,首先删除其他表中的外键列,然后返回到此步骤。

通过在列上设置 db_constraint=False,删除此表到其他表的任何数据库级外键约束。 部署 从 sentry 代码库中删除模型和所有引用。确保迁移仅将状态标记为已删除。 部署。 创建一个删除表的迁移。 部署

这是删除此模型的示例:

复制class AlertRuleTriggerAction(Model):      alert_rule_trigger = FlexibleForeignKey("sentry.AlertRuleTrigger")      integration = FlexibleForeignKey("sentry.Integration", null=True)      type = models.SmallIntegerField()      target_type = models.SmallIntegerField()      # Identifier used to perform the actionon a given target      target_identifier = models.TextField(null=True)      # Human readable nameto display in the UI      target_display = models.TextField(null=True)      date_added = models.DateTimeField(default=timezone.now)      class Meta:          app_label = "sentry"         db_table = "sentry_alertruletriggeraction" 1.2.3.4.5.6.7.8.9.10.11.12.13.14.

首先,我们检查了它没有被任何其他模型引用,它没有。接下来,我们需要删除和 db 级外键约束。为此,我们改变这两列并生成一个迁移:

复制alert_rule_trigger = FlexibleForeignKey("sentry.AlertRuleTrigger", db_constraint=False)  integration = FlexibleForeignKey("sentry.Integration", null=True, db_constraint=False)  1.2.

迁移中的操作看起来像

复制operations = [         migrations.AlterField(             model_name=alertruletriggeraction,             name=alert_rule_trigger,             field=sentry.db.models.fields.foreignkey.FlexibleForeignKey(db_constraint=False, on_delete=django.db.models.deletion.CASCADE, to=sentry.AlertRuleTrigger),         ),         migrations.AlterField(             model_name=alertruletriggeraction,             name=integration,             field=sentry.db.models.fields.foreignkey.FlexibleForeignKey(db_constraint=False, null=True, on_delete=django.db.models.deletion.CASCADE, to=sentry.Integration),         ),     ]  1.2.3.4.5.6.7.8.9.10.11.12.

我们可以看到它生成的 sql 只是删除了 FK 约束

复制BEGIN;  SET CONSTRAINTS "a875987ae7debe6be88869cb2eebcdc5" IMMEDIATE; ALTERTABLE"sentry_alertruletriggeraction"DROPCONSTRAINT"a875987ae7debe6be88869cb2eebcdc5";  SET CONSTRAINTS "sentry_integration_id_14286d876e86361c_fk_sentry_integration_id" IMMEDIATE; ALTERTABLE"sentry_alertruletriggeraction"DROPCONSTRAINT"sentry_integration_id_14286d876e86361c_fk_sentry_integration_id";  COMMIT;  1.2.3.4.

所以现在我们部署它并进入下一阶段。

下一阶段涉及从代码库中删除对模型的所有引用。所以我们这样做,然后我们生成一个迁移,从迁移状态中删除模型,而不是数据库。此迁移中的操作如下所示

复制operations = [          migrations.SeparateDatabaseAndState(              state_operations=[migrations.DeleteModel(name="AlertRuleTriggerAction")],              database_operations=[],          )      ]  1.2.3.4.5.6.

并且生成的 SQL 显示没有发生数据库更改。所以现在我们部署它并进入最后一步。

在这最后一步中,我们只想手动编写 DDL 来删除表。所以我们使用 sentry django makemigrations --empty 来产生一个空的迁移,然后修改操作如下:

复制operations = [          migrations.RunSQL(              """              DROPTABLE"sentry_alertruletriggeraction";              """,              reverse_sql="CREATE TABLE sentry_alertruletriggeraction (fake_col int)", # We just create a fake table here so that the DROP will work if we roll back the migration.          )      ]  1.2.3.4.5.6.7.8.

然后我们部署它,我们就完成了。

外键

创建外键大多没问题,但是对于像 Project、Group 这样的大/繁忙的表,由于获取锁的困难,它可能会导致问题。您仍然可以创建 Django 级别的外键,而无需创建数据库约束。为此,请在定义键时设置 db_constraint=False。

重命名表

重命名表很危险,会导致停机。发生这种情况的原因是在部署期间将运行旧/新代码的混合。因此,一旦我们在 Postgres 中重命名该表,如果旧代码尝试访问它,它就会立即开始出错。有两种方法可以处理重命名表:

不要在 Postgres 中重命名表。相反,只需在 Django 中重命名模型,并确保将 Meta.db_table 设置为当前表名,这样不会有任何中断。这是首选方法。 如果你真的想重命名表,那么步骤将是: 使用新名称创建一个表 开始对旧表和新表进行双重写入,最好是在事务中。 将旧行回填到新表中。 将 model 更改为从新表开始读取。 停止写入旧表并从代码中删除引用。 丢弃旧表。 一般来说,这是不值得做的,与回报相比,这需要冒很多风险/付出很多努力。

添加列

创建新列时,它们应始终创建为可为空的。这是出于两个原因:

如果存在现有行,添加非空列需要设置默认值,添加默认值需要完全重写表。这是危险的,很可能会导致停机 在部署期间,新旧代码混合运行。如果旧代码尝试向表中插入一行,则插入将失败,因为旧代码不知道新列存在,因此无法为该列提供值。

向列添加 NOT NULL

将 not null 添加到列可能很危险,即使该列的表的每一行都有数据。这是因为 Postgres 仍然需要对所有行执行非空检查,然后才能添加约束。在小表上这可能没问题,因为检查会很快,但在大表上这可能会导致停机。这里有几个选项可以确保安全:

ALTER TABLE tbl ADD CONSTRAINT cnstr CHECK (col IS NOT NULL) NOT VALID; ALTER TABLE tbl VALIDATE CONSTRAINT cnstr;. 首先,我们将约束创建为无效。然后我们之后验证它。我们仍然需要扫描整个表来验证,但我们只需要持有一个 SHARE UPDATE EXCLUSIVE 锁,它只会阻止其他 ALTER TABLE 命令,但允许读/写继续。这很有效,但会有 0.5-1% 的轻微性能损失。在 Postgres 12 之后,我们可以扩展这个方法来添加一个真正的 NOT NULL 约束。 如果表足够小并且体积足够小,那么创建一个普通的 NOT NULL 约束应该是安全的。小是几百万行或更少。

添加具有默认值的列

向现有表添加具有默认值的列是危险的。这需要 Postgres 锁定表并重写它。相反,更好的选择是:

在 Postgres 中添加没有默认值的列,但在 Django 中添加默认值。这使我们能够确保所有新行都具有默认值。这是通过修改迁移文件以包含 migrations.SeperateDatabaseAndState 来完成的

复制operations = [      migrations.SeparateDatabaseAndState(          database_operations=[              migrations.AddField(                  model_name="mymodel",                  name="new_field",                  # Dont use a defaultin Postgres, a data migration can be used afterward to backfill                  field=models.PositiveSmallIntegerField(null=True),              ),          ],          state_operations=[              migrations.AddField(                  model_name="mymodel",                  name="new_field",                  # Use the defaultin Django, new rows will use the specified default                 field=models.PositiveSmallIntegerField(null=True, default=1),              ),          ],      )      ]  1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20. 通过数据迁移使用默认值回填预先存在的行。

改变列类型

改变列的类型通常是危险的,因为它需要重写整个表。有一些例外:

将 varchar() 更改为更大尺寸的 varchar。 将任何 varchar 更改为 text 将 numeric 更改为 numeric,其中 precision 更高但 scale 相同。

对于任何其他类型,最好的前进路径通常是:

创建具有新类型的列。 开始对新旧列进行双重写入。 回填并将旧列值转换为新列。 更改代码以使用新字段。 停止写入旧列并从代码中删除引用。 从数据库中删除旧列。 通常,这值得在 #discuss-backend 中讨论。

重命名列

重命名列是危险的,会导致停机。发生这种情况的原因是在部署期间将运行旧/新代码的混合。因此,一旦我们在 Postgres 中重命名该列,如果旧代码尝试访问它,它就会立即开始出错。有两种方法可以处理重命名列:

不要重命名 Postgres 中的列。相反,只需在 Django 中重命名字段,并在定义中使用 db_column 将其设置为现有的列名,这样就不会中断。这是首选方法。 如果你真的想重命名列,那么步骤将是: 创建具有新名称的列 开始对新旧列进行双重写入。 将旧列值回填到新列中。 将字段更改为从新列开始读取。 停止写入旧列并从代码中删除引用。 从数据库中删除旧列。

一般来说,这是不值得做的,与回报相比,这需要冒很多风险/付出很多努力。

(责任编辑:数据库)

最新内容
推荐内容
  • 主板接线安装教程(详细步骤指南让你轻松安装主板,打造个性化电脑体验)
  • 先来介绍一下硬盘分区的基本知识:物理介质 The physical media逻辑卷管理说明不幸的是,该磁盘工具不支持LVM的大多数强大的特性,没有管理卷组、扩展分区,或者创建快照等选项。对于这些操作,你可以通过终端来实现,但是没有那个必要。相反,你可以打开Ubuntu软件中心,搜索关键字LVM,然后安装逻辑卷管理工具,你可以在终端窗口中运行sudo apt-get install system-config-lvm命令来安装它。安装完之后,你就可以从dash上打开逻辑卷管理工具了。这个图形化配置工具是由红帽公司开发的,虽然有点陈旧了,但却是唯一的图形化方式,你可以通过它来完成上述操作,将那些终端命令抛诸脑后了。比如说,你想要添加一个新的物理卷到卷组中。你可以打开该工具,选择未初始化条目下的新磁盘,然后点击“初始化条目”按钮。然后,你就可以在未分配卷下找到新的物理卷了,你可以使用“添加到现存卷组”按钮来将它添加到“ubuntu-vg”卷组,这是Ubuntu在安装过程中创建的卷组。要扩展逻辑分区到物理空间,你可以在逻辑视图下选择它,点击编辑属性,然后修改大小来扩大分区。你也可以在这里缩小分区。system-config-lvm的其它选项允许你设置快照和镜像。对于传统桌面而言,你或许不需要这些特性,但是在这里也可以通过图形化处理。记住,你也可以使用终端命令完成这一切。
  • 电脑密码错误锁定的解决方法(忘记密码或被盗的情况下,如何解除电脑锁定)
  • 修改方案如下: 1、命令行中输入gconf-editor,打开配置编辑器。 PS:ubuntu 12.10 直接在终端输入:gsettings set org.gnome.desktop.wm.preferences button-layout :minimize,maximize,close 在ubuntu13.04上就不完全起作用了,只修改了小部分窗口的按钮到右上角了。
  • 台式电脑内置喇叭安装教程(一步步教你如何给台式电脑安装内置喇叭)
  • VMware,虚拟系统之王,以前介绍过,见博文:诡异的虚拟机大师——VMware,VMware的强悍之处很多,其中之一就是可以轻松在虚拟和现实中穿梭,当然要实现这一愿望,就得安装VMware Tools了,呵呵,有些人可以不知道怎么安装呢,尤其LINUX下的安装,今天偶就把偶的安装过程和大家分享一下,呵呵,有不当之处,还请斧正 WIN系统操作系统一向追求易用为上,所以呀,安装VMware Tools也是一件非常轻松简单的事情,看截图吧(以最新推出的WIN2008测试版为例,其他像XP、VISTA都一样)  弹出对话框,确认是否安装,这不是废话嘛,当然要安装了  一会,VMware Tools的安装文件就被加载到系统的光驱中并自动运行安装程序了 WIN系统操作系统的一大毛病,需要重启,重启后状态栏显示为安装成功就一切OK了   和WIN2008时的虚拟机状态栏显示的一样 和WIN2008仍然是一样  也可以加载到光驱中,可不能自动安装了,这就是LINUX的一大缺点,易用性太差 UBUNTU自带的新立得安装管理器也不好使,看来只能手动命令行安装了  UBUNTU的命令行窗口就是“附件”里的“终端”,好奇怪的名字 13.jpg: 这是比关键的步骤啦,依次输入以下命令: --------------------------该行不输入 $sudo apt-get install build-essential --------------------------该行不输入 后面一路回答Y或回车就OK了 好了,安装到此结束,可以享受穿梭于真实与虚拟的快感了
热点内容