如何将旧的 Django 项目从 SQLite 迁移到 PostgreSQL?
Django项目中SQLite的配置非常简单,而且由于个人博客的流量不会很大,所以线上环境直接使用SQLite数据库。
博客刚上线的时候一切都还好,直到最近经常收到异常报警数据库被锁定
。经过一番研究,大致确定了该异常的原因:并发情况下,如果某个线程等待数据库锁的时间超过指定时间(默认5秒),就会出现数据库锁定
异常抛出。有人在 Stackoverflow 上提出了类似的问题,解决方案大致可以总结为:
- 更换数据库引擎。
- 优化应用程序并减少并发性。
- 增加锁定时间限制。
选项 3 治标不治本。选项 2 更困难,并且一直使用 SQLite 并不是一个长期的解决方案。所以,长期的痛苦不如短期的痛苦。我决定在线上环境彻底取代SQLite,投入PostgreSQL的怀抱。
数据迁移方案
我首先想到的方案是将SQLite中的数据作为SQL语句导出,然后导入到PostgreSQL数据库中。毕竟它们属于两个不同的数据库引擎,很容易出现不兼容的SQL语句。 ,所以这个方案被否决了。
我们最终决定采用的解决方案是将SQLite中的数据导出为JSON格式数据,然后导入到PostgreSQL数据库中。 Django提供了export和import命令,实现起来非常方便。
以下是整个迁移过程:
- 以JSON格式导出数据:
python manage.py dumpdata > db.json
; - 编辑Django数据库引擎配置;
- 生成新的数据库表:
python manage.py migrate
; - 删除新表中自动生成的ContentType相关数据:
$ python manage.py shell
>>> from django.contrib.contenttypes.models import ContentType
>>> ContentType.objects.all().delete()
- 导入JSON数据:
python manage.py loaddata db.json
。
第4步要特别注意,Django会自动将ContentType相关的数据写入到新生成的数据库表中,必须先删除该表,否则会与导入的数据冲突。
找坑
虽然迁移步骤很简单,但是陷阱也不少。以下是我遇到的一些陷阱以及如何应对它们。
坑一:数据格式不兼容
SQLite数据库字段类型比较简单,而PostgreSQL字段类型比较丰富。一些可以存储在 SQLite 中的数据在导入 PostgreSQL 时无法通过格式验证。
比如我的博客评论有一个ip字段,一些空记录的值为b''
。 SQLite 中的字段类型是 char,而 PostgreSQL 中的字段类型是 inet。 inet 施加的身份验证更强。值b''
无法通过验证,导入时会报错。
解决办法是在导出数据之前将ip字段的值更改为与PostgreSQL inet字段类型兼容的值。
坑2:PostgreSQL不允许不同类型的字段串联
PostgreSQL不允许不同类型的字段串联。因此,在SQLite和MySQL中正确执行的SQL语句在PostgreSQL中会报错。
没有什么好的解决办法。我们可以只将每个表中需要连接的字段更改为相同类型。如果无法修改,建议将数据库改为MySQL。
坑3:小心应用程序自动生成的数据
应用程序可能存在记录存入数据库时自动生成一些相关数据的逻辑,在导入数据时会发生冲突。
例如,在我的博客应用中,当Users表中存储一条用户记录时,token表中会生成一条相关记录。那么当你导入数据时,原始数据中的token就会和新生成的token一样。冲突。
解决方案是识别自动生成的数据并将其从导出的数据中排除。命令 dumpdata
支持指定排除的数据,例如: python manage.py dumpdata > db.json --exclude=authtoken
将排除 authtoken 中的应用程序数据
Summary
- 更换数据库引擎是一件极其痛苦的事情,因此必须在项目开始时选择数据库引擎,以降低数据迁移的难度。
- 虽然Django ORM非常强大,并且提供了数据库引擎一定程度的抽象,但是在一个数据库中运行良好的代码无法在另一个数据库中使用的情况仍然不可避免,所以无论是开发还是测试还是线上,尝试一下使用相同的数据库引擎。
- 这种数据迁移方式效率很低。我的博客导入2亿条数据花了30多分钟。如果数据量比较大,最好采用更高效的方法。
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。