精简您的 Rails 8 应用程序:从 Postgres 迁移到 SQLite
在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上平稳运行。