CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
Ruby on Rails 实战入门/课时/数据库适配
RawBlame
课时目录12 节

Ruby on Rails 实战入门

  1. 01Rails 概览与项目结构
  2. 02路由设计
  3. 03控制器
  4. 04Active Record 模型
  5. 05查询与优化
  6. 06数据库适配
  7. 07视图与组件
  8. 08表单
  9. 09认证与授权
  10. 10资产与后台任务
  11. 11测试与调试
  12. 12部署与运维

数据库适配

PostgreSQL/MySQL/SQLite。

30 分钟

数据库适配

这一课你将能做到

  1. 用 database.yml 配置三套环境,并把敏感信息挪到环境变量
  2. 把应用从 SQLite 切换到 MySQL/PostgreSQL,理解适配器差异
  3. 配置连接池、读写分离与多数据库,说清楚各自的适用场景

为什么要学

Rails 默认用 SQLite 起步,零配置很爽,但真实项目几乎必然上 MySQL 或 PostgreSQL(本站就是 MySQL)。数据库适配层的知识决定了你能否顺利完成「本地开发 → 团队 staging → 生产」的迁移,也决定了流量翻倍时你的应用是平滑扩容还是半夜宕机。连接池配置错误是生产事故的高发区。

先建立图像

Rails 连接数据库的层次:

你的代码 Article.find(1)
      │
      ▼
Active Record(生成 SQL)
      │
      ▼
适配器 Adapter(mysql2 / pg / sqlite3 gem)
      │
      ▼
连接池(pool: 5,复用 TCP 连接)
      │
      ▼
数据库服务器(MySQL 8 / PostgreSQL 16 / SQLite 文件)

三套环境各自独立:

环境用途数据
development本地开发真实感种子数据
test跑测试每次测试前清空,事务回滚
production线上真实用户数据

跟着做

步骤 1:读懂默认 database.yml

config/database.yml(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:
  <<: *default
  database: storage/production.sqlite3

要点:default: &default 定义锚点,<<: *default 在各环境复用——DRY 的 YAML 技巧。pool 是连接池大小,<%= ENV.fetch(...) %> 说明 yml 里可以嵌 ERB 读环境变量。

步骤 2:切换到 MySQL

新项目直接指定:

rails new blog --database=mysql

已有项目改造——Gemfile 换驱动:

gem "mysql2", "~> 0.5"     # 删掉 sqlite3

config/database.yml:

default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS", 5) %>
  username: <%= ENV.fetch("DB_USERNAME", "root") %>
  password: <%= ENV.fetch("DB_PASSWORD", "") %>
  host: <%= ENV.fetch("DB_HOST", "localhost") %>

development:
  <<: *default
  database: blog_development

test:
  <<: *default
  database: blog_test

production:
  <<: *default
  database: blog_production

然后:

bundle install
bin/rails db:create      # 建库
bin/rails db:migrate     # 建表
bin/rails db:seed        # 灌种子数据

注意 utf8mb4:MySQL 的老 utf8 只支持 3 字节字符,存 emoji 会报错,utf8mb4 才是真 UTF-8。这是本站(Spring Boot + MySQL)同样踩过的坑。

步骤 3:生产环境用环境变量保密码

永远不要把生产密码提交进 Git。Rails 7 推荐两种方式:

方式一,环境变量直读(上面 yml 已演示),部署时在服务器上设:

export DB_PASSWORD="真实密码"
export RAILS_MASTER_KEY="config/master.key 的内容"

方式二,Rails 加密凭据(credentials):

bin/rails credentials:edit
# 编辑器里写入,保存后自动加密存到 config/credentials.yml.enc
db:
  password: 真实密码

代码里读取:

password: <%= Rails.application.credentials.dig(:db, :password) %>

credentials.yml.enc 可以安全提交 Git,解密钥匙 config/master.key 绝不提交。

步骤 4:连接池与 Puma 线程的关系

Puma 配置(config/puma.rb):

threads_count = ENV.fetch("RAILS_MAX_THREADS", 5)
threads threads_count, threads_count
workers ENV.fetch("WEB_CONCURRENCY", 2)

规则:连接池大小 ≥ 每个 worker 的线程数。每个线程处理请求时要独占一条数据库连接,池子比线程小,请求就得排队等连接,表现为「偶发超时」。总连接数 = workers × pool,还要给数据库 max_connections 留余量。算式:2 workers × 5 pool = 10 条连接,MySQL 默认 151 上限,绰绰有余。

步骤 5:多数据库与读写分离(了解即可)

Rails 6+ 原生支持多数据库:

production:
  primary:
    database: blog_production
    host: master.db.internal
  primary_replica:
    database: blog_production
    host: replica.db.internal
    replica: true
class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
  connects_to database: { writing: :primary, reading: :primary_replica }
end

读多写少的场景(内容站、报表),从库扛查询流量,主库专心写。中小项目用不到,但面试常考。

讲清楚要点

  1. 适配器是 gem 不是框架内置:mysql2、pg、sqlite3 都是独立 gem,Active Record 通过它们翻译 SQL 方言。换库 = 换 gem + 改 yml + 迁数据。
  2. test 环境必须独立库:测试框架每次运行清空数据(事务回滚),共用开发库会删掉你的调试数据。
  3. 密码不进 Git:用 ENV 或 credentials。检查仓库历史,git log -p config/database.yml 确认从没提交过明文密码;提交过就立刻改密码。
  4. 池大小跟着线程走:Puma 线程数、Sidekiq 并发数都要 ≤ 对应进程的连接池,否则并发一高就 ConnectionTimeoutError。
  5. utf8mb4 是 MySQL 唯一正确的 UTF-8:建库时指定,Rails 的 mysql2 默认已帮你配好,但接手老库时要检查。

你可能会懵的地方

  • MySQL 和 PostgreSQL 怎么选? 都能打。PostgreSQL 功能更强(JSONB、全文搜索、地理 PostGIS、更严的 SQL 标准);MySQL 生态广、运维熟人多、托管服务便宜。团队已有 MySQL 经验(如本站)就用 MySQL,别为技术新鲜感换库。

  • SQLite 真的不能上生产吗? Rails 官方近年态度转变:单服务器、读多写少的中小应用,SQLite 上生产完全可行(Litestack 方案),省掉运维成本。多机部署、高并发写入才必须上 MySQL/PG。

  • db:create 和 db:migrate 区别? db:create 只建空库(CREATE DATABASE);db:migrate 在库里按迁移文件建表改表。新环境一把梭:bin/rails db:setup(create + schema 加载 + seed)。

  • schema.rb 和 structure.sql 区别? 默认 schema.rb 是 Ruby 方言的表结构快照,跨数据库通用。用到数据库特有功能(PG 的物化视图、触发器)时 schema.rb 表达不了,需切 config.active_record.schema_format = :sql,生成 structure.sql。

  • 开发环境连远程数据库好吗? 不好。延迟高、误操作伤真实数据。开发用本地库,需要真实数据时做脱敏导出再导入。

错法对照

错法问题正解
生产密码明文写进 yml 并提交密码随仓库泄露ENV 或 credentials,历史泄露立刻换密
pool 默认 5 但 Puma 开 16 线程高并发下连接等待超时pool ≥ 每进程线程数
test 和 development 共用库跑测试清空开发数据三套环境三个库
MySQL 用 utf8 编码emoji 写入报错utf8mb4
在 yml 里硬编码生产 host换服务器要改代码ENV.fetch("DB_HOST")

卡点急救

  1. Access denied for user:用户名密码错,或该用户没有对应库的权限。MySQL 里执行 GRANT ALL ON blog_development.* TO 'rails'@'localhost'; 并 FLUSH PRIVILEGES;。
  2. Can't connect to MySQL server:MySQL 服务没启动;或 host 写了 localhost 但走了 socket 而 socket 路径不对——把 host 改成 127.0.0.1 强制走 TCP 试试。
  3. 测试报 database is locked(SQLite):测试并行/事务冲突,确认 test 库独立;MySQL 下常见原因是连接池耗尽,检查 pool 配置。

面试常问

  • Rails 三套环境的数据库如何隔离?为什么? 答:database.yml 里 development/test/production 各配独立 database。隔离原因:测试每次清空数据,混用会毁开发数据;生产更是物理隔离的真实数据。这是「环境即数据边界」的基本工程纪律。

  • 连接池是干什么的?怎么定大小? 答:TCP 连接建立昂贵,池子预先建好一批连接复用。大小定式:≥ 单进程最大并发线程数(Puma 线程 / Sidekiq 并发),同时所有进程总连接数 < 数据库 max_connections。过大浪费数据库内存,过小请求排队。

  • 明文密码提交过 Git 怎么办? 答:立刻改密码(改密是唯一能补救的动作);用 git filter-repo 或 BFG 清洗历史;通知团队强制更新仓库。预防:凭据一律走 ENV/credentials,code review 把 yml 变更列为重点审查项。

自测三问

  1. pool: 5 和 Puma threads 16 同时存在会发生什么?怎么改?
  2. 为什么 credentials.yml.enc 能提交而 master.key 不能?
  3. MySQL 的 utf8 和 utf8mb4 差异是什么?

小结一句话

数据库适配的核心纪律:三套环境三个库、密码永不进 Git、连接池跟着线程走、MySQL 认准 utf8mb4。

延伸

  • Active Record 多数据库指南
  • 下一课:视图与组件——ERB 模板、布局、局部视图与 ViewComponent。
加载本节练习…
← 上一课查询与优化下一课 →视图与组件