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. 技术优缺点分析
优势:
- 原子性配置管理:所有设置集中管理,像遥控器一样方便
- 连接池智能调度:自动复用连接,省资源
- 多环境支持:开发/测试/生产环境轻松切换
- 扩展性强:支持主从复制、读写分离等高级配置
需要注意的:
- 密码明文问题:生产环境建议使用环境变量或加密存储
- 连接泄露风险:注意及时释放查询结果
- 版本兼容性:注意Ecto和Postgrex版本匹配
- 超时设置:根据业务场景调整合理数值
7. 避坑指南
连接超时问题:如果遇到
DBConnection.ConnectionError
,可以尝试:- 检查网络防火墙设置
- 适当增大timeout值
- 验证数据库服务是否正常运行
编码问题:在config中增加:
charset: "utf8mb4",
collation: "utf8mb4_unicode_ci"
避免出现中文乱码
- 连接池耗尽:可以通过监控工具查看连接使用情况:
MyApp.Repo.config()[:pool_size]
MyApp.Repo.checkout(fn -> :ok end)
- 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数据库连接的各个角落都摸了个遍。从基础配置到高级技巧,从单数据库到多租户方案,这些知识足够应对日常开发中的大多数场景了。
未来可以继续探索的方向:
- 结合Phoenix框架的实时数据推送
- 使用Ecto的查询DSL构建复杂查询
- 实现数据库的读写分离架构
- 整合监控系统进行性能分析
记住,好的数据库配置就像呼吸一样自然——用户感受不到它的存在,但系统一刻都离不开它。希望这篇指南能成为你的Elixir数据库配置手册,遇到问题时随时回来查查。