1. 先唠唠为什么要配置数据库

在咱们搞Elixir开发的时候,数据库就像是你家小区的快递柜——得有个正确的取件码才能拿到数据。不管是Web应用还是微服务,90%的项目都躲不开要和数据库打交道。今天咱们就用最常见的PostgreSQL数据库搭配Ecto库,手把手教你配置这个"快递柜"的取件码。

先说说典型应用场景:

  • 用户管理系统要存取用户资料
  • 电商平台要记录订单信息
  • IoT设备要存储传感器数据
  • 实时聊天要保存消息记录

这些场景都像不同形状的快递包裹,需要合适的数据库柜子来存放。接下来咱们就进入实战环节。

2. 环境准备与Ecto安装

2.1 创建新项目

mix new my_app --sup
cd my_app

这个--sup参数会生成监督树结构,数据库连接池需要这个架构来管理生命周期。

2.2 添加依赖

打开mix.exs文件,在deps函数里添加:

defp deps do
  [
    {:ecto_sql, "~> 3.10"},
    {:postgrex, ">= 0.0.0"}
  ]
end

运行mix deps.get安装依赖,就像去超市买好了做菜的原料。

3. 基础配置四步曲

3.1 生成配置文件

创建config/config.exs并添加:

import Config

# 数据库连接配置
config :my_app, MyApp.Repo,
  username: "postgres",
  password: "postgres",
  database: "my_app_dev",
  hostname: "localhost",
  pool_size: 10,
  show_sensitive_data_on_connection_error: true

# 不同环境的差异化配置
import_config "#{config_env()}.exs"

这相当于给你的数据库连接办了张身份证,各个参数的含义就像身份证号码的不同字段。

3.2 创建Repo模块

在lib/my_app/repo.ex创建:

defmodule MyApp.Repo do
  use Ecto.Repo,
    otp_app: :my_app,
    adapter: Ecto.Adapters.Postgres
end

这个模块就像数据库操作的接线员,负责把我们的请求转接给PostgreSQL。

3.3 监督树配置

修改lib/my_app/application.ex:

def start(_type, _args) do
  children = [
    MyApp.Repo  # 加入数据库连接池监管
  ]

  Supervisor.start_link(children, strategy: :one_for_one)
end

这相当于给数据库连接池安排了个监工,保证连接随时可用。

3.4 创建数据库

运行命令:

mix ecto.create

看到绿色的提示信息,说明你的数据库快递柜已经建好了!

4. 高级配置技巧

4.1 动态配置

在需要根据环境变量配置的场景下,可以这样写:

config :my_app, MyApp.Repo,
  username: System.get_env("DB_USER") || "postgres",
  password: System.get_env("DB_PASS") || "postgres",
  database: System.get_env("DB_NAME") || "my_app_prod",
  hostname: System.get_env("DB_HOST") || "localhost"

这就像给配置装了个智能开关,能自动适应不同环境。

4.2 SSL连接配置

生产环境必备的安全措施:

config :my_app, MyApp.Repo,
  ssl: true,
  ssl_opts: [
    cacertfile: "/path/to/ca-certificate",
    verify: :verify_peer
  ]

相当于给你的数据库连接加了把指纹锁。

4.3 连接池优化

config :my_app, MyApp.Repo,
  pool_size: 20,  # 最大连接数
  queue_target: 5000,  # 等待队列上限
  timeout: 30000  # 超时时间(ms)

这就像调整快递柜的格子数量和取件时间限制。

5. 关联技术:迁移文件的使用

创建一个用户表的迁移示例:

mix ecto.gen.migration create_users

打开生成的迁移文件:

defmodule MyApp.Repo.Migrations.CreateUsers do
  use Ecto.Migration

  def change do
    create table(:users) do
      add :username, :string, size: 50, null: false
      add :email, :string, size: 150
      add :age, :integer
      add :preferences, :map

      timestamps()
    end

    create unique_index(:users, [:email])
  end
end

运行mix ecto.migrate应用迁移,就像给数据库快递柜添加了新的储物格。

6. 技术优缺点分析

优势:

  1. 原子性配置管理:所有设置集中管理,像遥控器一样方便
  2. 连接池智能调度:自动复用连接,省资源
  3. 多环境支持:开发/测试/生产环境轻松切换
  4. 扩展性强:支持主从复制、读写分离等高级配置

需要注意的:

  1. 密码明文问题:生产环境建议使用环境变量或加密存储
  2. 连接泄露风险:注意及时释放查询结果
  3. 版本兼容性:注意Ecto和Postgrex版本匹配
  4. 超时设置:根据业务场景调整合理数值

7. 避坑指南

  1. 连接超时问题:如果遇到DBConnection.ConnectionError,可以尝试:

    • 检查网络防火墙设置
    • 适当增大timeout值
    • 验证数据库服务是否正常运行
  2. 编码问题:在config中增加:

charset: "utf8mb4",
collation: "utf8mb4_unicode_ci"

避免出现中文乱码

  1. 连接池耗尽:可以通过监控工具查看连接使用情况:
MyApp.Repo.config()[:pool_size]
MyApp.Repo.checkout(fn -> :ok end)
  1. SSL证书验证:测试环境可以临时设置ssl: false,但生产环境必须启用

8. 实战场景演示

8.1 多数据库配置

适用于需要同时连接多个数据库的场景:

# 主数据库配置
config :my_app, MyApp.Repo,
  username: "main_user",
  database: "main_db"

# 日志数据库配置
config :my_app, MyApp.LogRepo,
  username: "log_user",
  database: "log_db"

然后创建对应的Repo模块:

defmodule MyApp.LogRepo do
  use Ecto.Repo,
    otp_app: :my_app,
    adapter: Ecto.Adapters.Postgres
end

这就像给应用装了个双卡双待的手机,能同时使用两个数据库。

8.2 动态数据库切换

在租户系统的场景下,可以根据请求动态切换数据库:

defmodule MultiTenantRepo do
  def put_tenant(tenant_id) do
    config = MyApp.Repo.config()
    new_config = Keyword.put(config, :database, "tenant_#{tenant_id}")
    MyApp.Repo.put_dynamic_repo(new_config)
  end
end

这相当于给每个租户分配了独立的快递柜,数据完全隔离。

9. 总结与展望

经过这一趟配置之旅,咱们已经把Ecto数据库连接的各个角落都摸了个遍。从基础配置到高级技巧,从单数据库到多租户方案,这些知识足够应对日常开发中的大多数场景了。

未来可以继续探索的方向:

  1. 结合Phoenix框架的实时数据推送
  2. 使用Ecto的查询DSL构建复杂查询
  3. 实现数据库的读写分离架构
  4. 整合监控系统进行性能分析

记住,好的数据库配置就像呼吸一样自然——用户感受不到它的存在,但系统一刻都离不开它。希望这篇指南能成为你的Elixir数据库配置手册,遇到问题时随时回来查查。