Django 老项目如何从 SQLite 迁到 PostgreSQL?

2022-09-1711:21:33后端程序开发Comments1,145 views字数 1756阅读模式

Django 项目配置 SQLite 非常简单,而且考虑到个人博客访问量不会很大,所以线上环境直接使用了 SQLite 数据库。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27690.html

博客上线初期一切运行良好,直到最近频繁收到 database is locked 的异常告警。经过一番调研,大致确定了导致异常的原因:并发情况下,如果线程等待数据库锁的时间超过指定时间(默认为 5 秒)则会抛出 database is locked 异常。Stackoverflow 上有人问了类似的问题,解决方案大致可总结为这几种:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27690.html

  1. 更换数据库引擎。
  2. 优化应用程序,减少并发。
  3. 增加锁超时时间。

方案 3 治标不治本,方案 2 比较麻烦,而且一直使用 SQLite 也不是长远之计,所以长痛不如短痛,决定线上环境彻底换掉 SQLite,投入 PostgreSQL 的怀抱。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27690.html

数据迁移方案

一开始想到的方案是将 SQLite 中的数据导出为 SQL 语句,再导入 PostgreSQL 数据库中,但毕竟分属 2 个不同的数据库引擎,很容易出现不兼容的 SQL 语句,所以这个方案被否掉了。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27690.html

最终决定采用的方案是将 SQLite 中的数据导出为 JSON 格式的数据,再导入的 PostgreSQL 数据库中,Django 提供了导出和导入的命令,实施起来非常方便。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27690.html

以下是整个迁移流程:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27690.html

  1. 导出 JSON 格式的数据:python manage.py dumpdata > db.json
  2. 修改 Django 项目的数据库引擎配置;
  3. 生成新的数据库表:python manage.py migrate
  4. 删除新表中自动生成的 ContentType 有关的数据:
$ python manage.py shell
>>> from django.contrib.contenttypes.models import ContentType
>>> ContentType.objects.all().delete()
  1. 导入 JSON 数据:python manage.py loaddata db.json

第 4 步需要特别注意,新生成的数据库表,Django 会自动写入 ContentType 有关的数据,需要先删除掉,否则会和导入的数据的冲突。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27690.html

遇到的坑

尽管迁移步骤非常的简单,但是也存在不少的坑,以下就是我遇到的一些坑和填坑方法。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27690.html

坑1:不兼容的数据格式

SQLite 数据库字段的类型比较简单,而 PostgreSQL 字段类型更加丰富,有些能够存入 SQLite 的数据导入 PostgreSQL 时无法通过格式校验。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27690.html

例如我的博客评论有一个 ip 字段,某些空记录的值为 b''。SQLite 中字段类型是 char,而 PostgreSQL 中是 inet,inet 施加的校验更强,值 b'' 无法通过校验,因此导入时会报错。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27690.html

解决方案就是导出数据前,先将 ip 字段的值修改为和 PostgreSQL inet 字段类型兼容的值。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27690.html

坑2:PostgreSQL 不允许 join 不同类型的字段

PostgreSQL 不同类型的字段不允许 join 操作。因此在 SQLite 和 MySQL 中执行没问题的 SQL 语句在 PostgreSQL 中就会报错。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27690.html

没有太好的解决方案,只能把各个表中需要 join 的字段改为相同类型。如果无法修改,建议更换数据库为 MySQL。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27690.html

坑3:当心应用程序自动生成的数据

应用程序很可能会有当某条记录存入数据库时,自动生成某些关联数据的逻辑,导入数据时就会产生冲突。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27690.html

例如我的博客应用中,当 Users 表中存入一条用户记录后,就会在 token 表中生成一条关联的记录,那么导入数据时,原数据中的 token 就会和新生成的 token 冲突。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27690.html

解决方案是,找出自动生成的数据,将他们从导出的数据中排除。dumpdata 命令支持指定排除的数据,例如:python manage.py dumpdata > db.json --exclude=authtoken 将排除 authtoken 应用中的数据。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27690.html

总结

  1. 更换数据库引擎是一件异常痛苦的事,所以在项目初期就要选好数据库引擎,减少数据迁移的麻烦。
  2. 尽管 Django ORM 非常强大,在一定程度上提供了数据库引擎的抽象,但仍然无法避免在某个数据库工作良好的代码,换到另一个数据库就无法使用的情况,所以无论是开发、测试还是线上,尽量使用相同的数据库引擎。
  3. 这种数据迁移方式效率非常低,我博客 200M 的数据导入就花了 30 多分钟,如果数据量比较大,最好还是换一种更加高效的方式。
文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27690.html
  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/bc/27690.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定