精简您的 Rails 8 应用程序:从 Postgres 迁移到 SQLite

发布:2024-10-20 10:23 阅读:13 点赞:0

在Rails 8中,SQLite成为了默认数据库,为开发者提供了简化应用栈的机会,同时仍然保持强大和灵活性。无论是小型个人项目还是规模扩展,SQLite的改进使其成为生产使用的优秀选择。

一. 创建Postgres数据库的备份

在将Rails应用从Postgres迁移到SQLite之前,创建Postgres数据库的备份至关重要。这可以确保在迁移过程中出现问题时,数据仍然安全。

首先,确保你能够访问运行Postgres的Docker容器,使用以下命令:

export POSTGRES_CONTAINER_ID=$(docker ps | grep postgres | awk '{print $1}' | head -n 1)
docker exec -it $POSTGRES_CONTAINER_ID bash

这条命令会获取Postgres容器的ID并进入容器。

接下来,使用pg_dump命令创建Postgres数据库的备份:

pg_dump -U postgres -d myapp_production -f database.dump

这条命令将创建一个名为database.dump的文件,作为数据库的备份。完成后,退出容器:

exit

然后,你可以将该备份文件复制到主机上:

docker cp $POSTGRES_CONTAINER_ID:/database.dump ./database.dump

如果你需要将备份下载到本地机器,可以使用scp命令:

scp user@server:/path/to/database.dump ~/Downloads

二. 更新Rails配置

确保你的应用程序已升级到Rails 8。为了将Rails应用程序指向SQLite数据库,需要在config/database.yml文件中更新数据库配置。以下是原Postgres的配置示例:

default: &default
  adapter: postgresql
  encoding: unicode
  port: 5432
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  timeout: 5000

development:
  <<: *default
  host: localhost
  database: myapp_development
  user: postgres
  password: password

test:
  <<: *default
  host: localhost
  database: myapp_test
  user: postgres
  password: password

production:
  <<: *default
  host: <%= ENV.fetch("POSTGRES_HOST") { "host" } %>
  database: myapp_production
  user: <%= ENV.fetch("POSTGRES_USER") { "user" } %>
  password: <%= ENV.fetch("POSTGRES_PASSWORD") { "password" } %>

将其更新为SQLite配置如下:

default: &default
  adapter: sqlite3
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  timeout: 5000

development:
  <<: *default
  database: storage/development.sqlite3

test:
  <<: *default
  database: storage/test.sqlite3

production:
  primary:
    <<: *default
    database: storage/production.sqlite3

三. 部署新配置

更新Rails应用程序后,可以部署新的Rails应用。在我的应用中,我使用Kamal。如果你使用其他工具,确保重新部署你的应用以加载新配置。

kamal deploy -d production

在服务器上验证SQLite数据库是否已创建,并检查应用程序是否正常运行:

docker exec -it $RAILS_CONTAINER_ID ls storage

这条命令会列出名为production.sqlite3的SQLite数据库文件。如果没有显示,可能存在部署问题,检查日志以识别问题:

kamal app logs -d production

四. 导入Postgres数据到SQLite

现在Rails应用正在运行(但SQLite数据库为空),可以开始从Postgres导入数据。不幸的是,没有直接将Postgres转储导入SQLite的方法。

在研究中,我发现像pg2sqlite这样的工具,但它们无法正确准备SQLite数据库所需的列数据类型和约束。最好的方法是从“干净的状态”开始,并在Rails设置好SQLite数据库的正确模式后分别导入数据。

可以创建一个脚本来连接Postgres和SQLite数据库,并将数据传输到新创建的SQLite数据库。

下面是一个转移数据的示例脚本,可以将其放在script/migrate_data.rb中。对于简单应用,这可能足够;对于复杂场景,可能需要添加额外逻辑来处理数据转换或关系。

require "active_record"
require "pg"
require "sqlite3"

# PostgreSQL数据库配置
postgres_config = {
  adapter: "postgresql",
  host: ENV["POSTGRES_HOST"],
  username: ENV["POSTGRES_USER"],
  password: ENV["POSTGRES_PASSWORD"],
  database: "myapp_production"
}

# SQLite数据库配置
sqlite_config = {
  adapter: "sqlite3",
  database: "storage/production.sqlite3"
}

# 建立连接
ActiveRecord::Base.establish_connection(postgres_config)
postgres_connection = ActiveRecord::Base.connection
ActiveRecord::Base.establish_connection(sqlite_config)
sqlite_conn = ActiveRecord::Base.connection

# 定义迁移的表顺序
tables_to_migrate = [
  "posts",
  "comments",
  # 添加所有表的正确顺序(由于外键约束)
]

tables_to_migrate.each do |table_name|
  puts "正在迁移表:#{table_name}"

  # 从PostgreSQL获取数据
  data = postgres_connection.select_all("SELECT * FROM #{table_name}")

  # 将数据插入SQLite
  data.rows.each do |row|
    attributes = data.columns.zip(row).to_h
    begin
      sqlite_conn.insert_fixture(attributes, table_name)
    rescue => e
      puts "插入#{table_name}时出错:#{e.message}"
    end
  end

  puts "表#{table_name}迁移成功。"
end

puts "迁移成功完成!"

要再次运行导入脚本(例如,在进行更改后),可以重置SQLite数据库并重新运行脚本:

docker exec -it $RAILS_CONTAINER_ID bin/rails db:reset
docker exec -it $RAILS_CONTAINER_ID bin/rails runner script/migrate_data.rb

导入数据后,Rails应用程序现在应该能够像以前一样运行,数据源来自SQLite数据库。可以通过在应用中检查数据或对SQLite数据库运行查询来验证这一点。

确保应用现在正在与SQLite数据库通信,可以停止Postgres容器并查看Rails应用程序是否仍按预期运行:

docker stop $POSTGRES_CONTAINER_ID

检查日志以确认一切正常。使用以下命令来监控日志:

kamal app logs -d production -f

五. 使用Litestream备份

为确保SQLite数据库定期备份,可以依赖Litestream。这是一种工具,可以将SQLite数据库持续备份到云存储提供商,如AWS S3。可以使用方便的litestream-ruby gem来实现。

六. 停止辅助服务并清理

在确认Rails应用程序正常运行SQLite数据库后,可以停止Postgres容器并清理卷。步骤如下:

docker stop $POSTGRES_CONTAINER_ID
docker rm $POSTGRES_CONTAINER_ID

要识别卷名称,可以使用以下命令:

docker volume ls

然后使用以下命令删除该卷:

docker volume rm <volume_name>

或者,使用Kamal移除Postgres辅助服务:

kamal accessory remove postgres -d production

此外,请确保:

  • 从Gemfile中移除pg gem。
  • 从Dockerfile中移除libpq-dev和其他不再需要的Postgres相关依赖。
  • 从Kamal的deploy.yml文件中移除Postgres辅助服务(如适用)。
  • 从Kamal的密钥中移除任何与Postgres相关的密钥(如适用)。
  • 移除迁移脚本和Postgres转储文件。

七. 结论

通过将Rails 8应用程序从Postgres迁移到SQLite,您简化了设置,同时保持性能和可靠性。SQLite与Rails 8的集成为许多用例提供了直接、高效的解决方案。

这次迁移使您能够减少栈的复杂性,并通过适当的备份策略,您可以自信地在生产环境中依赖SQLite。按照本指南中的步骤,您的Rails应用程序现在已经在SQLite上平稳运行。