iT邦幫忙

2025 iThome 鐵人賽

DAY 15
0
Rust

Rust 後端入門系列 第 15

Day15 Rust 專案連接資料庫(PostgreSQL)

  • 分享至 

  • xImage
  •  

sqlx 與 diesel 各自的優點

  • 設計理念與使用情境
    • diesel
      • 以「型別安全的 ORM/Query DSL」為核心。透過 Rust 的型別系統與 DSL(diesel::prelude::*),在編譯時就能檢查欄位與型別的一致性,減少 runtime 錯誤。
      • 適合喜歡在程式碼內用 DSL 組 query、並且偏好編譯時期保證的專案。對於標準 CRUD 與關聯操作,diesel 的 DSL 可以讓程式碼更「Rust 化」。
    • sqlx
      • 原生 async、以 raw SQL 為主的非阻塞 client。你可以直接寫標準 SQL,並能選擇在 build 時以 macro(sqlx::query!)做 SQL 與欄位的編譯時檢查。
      • 更適合需要複雜查詢(CTE、視窗函數、原生 SQL 優化)的場景,以及直接在 async handler 內使用資料庫的 web 應用。
  • 型別安全與開發流程
    • diesel 的優勢:在 compile-time 提供強烈保證,減少 runtime 型別與欄位不一致的錯誤。但在某些複雜查詢或動態 query 時,DSL 可能較難以表達。
    • sqlx 的平衡:允許寫 raw SQL 的彈性,同時可啟用 compile-time 檢查(需在 build 時連接到真實 DB 或使用 offline 準備),否則為 runtime 驗證。對於開發流程來說,sqlx 提供較接近 DB 的操作感受。
  • 非同步支援與效能考量
    • diesel 傳統上是同步 API(有 async 支援方案),在 async web 框架中通常要把工作丟到 blocking thread pool,會增加一些配置與上下文切換成本。
    • sqlx 原生支援 tokio/async-std,與 axum、tower 等現代 async 生態整合順暢,避免額外的 blocking 轉換。
  • 可維護性與學習成本
    • diesel 的 DSL 對習慣完全以型別驅動開發的人友好,但學習曲線與某些查詢的限制值得考量。
    • sqlx 更接近 SQL 原生語法,對熟悉 SQL 的開發者更直覺,且在團隊中如果 DB 團隊直接給 SQL,接手更簡單。

為什麼 axum 適合使用 sqlx

  • axum 是基於 tokio 的輕量化、模組化 web 框架,handler 通常是 async 函式;axum 的設計鼓勵把共享資源(例如 DB 連線池)注入到 request context(透過 Extension)。
  • sqlx 的優勢在於:
    • 原生 async 支援:能在 axum handler 內直接 await DB 操作,不需 spawn_blocking,降低複雜度與延遲風險。
    • PgPool(或其他 DB pool)可安全跨 handler 分享,並容易注入 axum 的 Extension,實務上常見範例也都採用這種模式。
    • 可直接使用 raw SQL:對於需要微調 SQL 或用原生 SQL 做效能優化的 API,sqlx 提供直接控制權。

axum + sqlx 是現代 Rust web 工程常見組合,利於快速開發與效能優化。


在 Windows 與 macOS 安裝 PostgreSQL

以下分別說明 Windows 與 macOS 的安裝方式,包含 GUI 與終端機選項,並提供常見問題的處理方法。

Windows

  • 方法 A:Postgres 官方安裝器(EnterpriseDB)
    1. 下載:到 https://www.postgresql.org/download/windows/ ,點選 EnterpriseDB 連結。
    2. 執行安裝程式(.exe),安裝步驟通常包含:
      • 安裝路徑
      • 選擇要安裝的 components(預設包含 server、pgAdmin、StackBuilder)
      • 設定 PostgreSQL superuser(postgres)密碼(請記住)
      • 選擇 port(預設 5432)
      • 選擇 locale
    3. 完成後會啟動 PostgreSQL 服務(作為 Windows service)。
    4. 驗證安裝是否成功:
      • 在命令提示字元(或 PowerShell)執行:psql -U postgres -h 127.0.0.1 -p 5432
        系統會要求輸入密碼,輸入安裝時設定的 postgres 密碼。
      • 或開啟 pgAdmin(若安裝)用 GUI 連線。
    5. 常見問題:
      • psql 找不到:將 PostgreSQL 的 bin 路徑(例如 C:\Program Files\PostgreSQL\18\bin)加到 PATH,或使用完整路徑執行 psql.exe。
      • Service 無法啟動:檢查 Windows 事件檢視器或 PostgreSQL 的 log(在 data 目錄下)。
  • 方法 B:使用 Chocolatey(命令提示字元)
    1. 安裝 Chocolatey(若尚未安裝):以系統管理員權限在 PowerShell 執行 Chocolatey 官方指令。

    2. 安裝 PostgreSQL:

      choco install postgresql
      
    3. 完成後同樣需要確認 service 與 PATH。Chocolatey 會提示安裝位置與初始密碼設定方式。

    4. 驗證方式同上。

macOS

  • 方法 A:Homebrew(推薦給習慣命令列的開發者)
    1. 安裝 Homebrew(若尚未):在 Terminal 執行官方安裝指令。

    2. 安裝 PostgreSQL:

      brew install postgresql
      
    3. 啟動 PostgreSQL:

      • 開發使用(用 LaunchAgent):
      brew services start postgresql
      
      • 或暫時啟動(路徑依系統而異):
      pg_ctl -D /usr/local/var/postgres start 
      
    4. 建立初始資料庫(homebrew 安裝後通常會建立 current user 的資料庫):

      createdb $(whoami)
      
    5. 常見問題:

      • 若遇到權限或路徑問題,檢查 Homebrew 的安裝路徑與環境變數。
      • 若使用 Apple Silicon(M系列晶片),Homebrew 安裝路徑可能是 /opt/homebrew,注意 PATH 設定。
  • 方法 B:Postgres.app(圖形化,一鍵啟動)
    1. 下載 Postgres.app:https://postgresapp.com/
    2. 將 Postgres.app 拖到 Applications,啟動之後會在 menu bar 顯示 PostgreSQL 已啟動。
    3. psql 可透過應用程式內的 shell 或手動將 Postgres.app 的 bin 加到 PATH:
      export PATH="/Applications/Postgres.app/Contents/Versions/latest/bin:$PATH"
    4. 驗證:psql -h 127.0.0.1 -U postgres
    5. Postgres.app 的好處是簡單易用,適合快速開發環境設定。

基本安全與管理建議

  • 初始 superuser(通常是 postgres)請設強密碼,將開發環境的 creds 加入 .env 並加入 .gitignore。
  • 若本機只做開發,可允許本機連線(127.0.0.1),但在開發以外環境慎防開放到公共網路。
  • 如果需要同時執行多個 PostgreSQL 版本,避免埠號衝突(預設 5432)。

在 Rust 專案用 sqlx 連接 PostgreSQL

提供一個最小可運作的 Rust 範例,程式啟動後嘗試連到 PostgreSQL,並回報「連線成功」或「連線失敗」。不包含任何 CRUD 操作。

前置條件

  • 已安裝 Rust
  • PostgreSQL 已在本機運作
  • 建議在專案目錄放置 .env 存 DATABASE_URL(並加入 .gitignore)

Cargo.toml

請建立一個新的專案

cargo new sqlx_connect_demo

然後在 Cargo.toml 中加入:

[package]
name = "sqlx_connect_demo"
version = "0.1.0"
edition = "2024"

[dependencies]
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "postgres", "macros"] }
dotenvy = "0.15"

說明:

  • runtime-tokio-rustls:使用 tokio runtime 與 rustls,避免系統 openssl 的相依問題。
  • postgres:啟用 PostgreSQL 支援。
  • macros:若未在 build 時做 compile-time SQL 驗證,也能使用其它 macro 功能。這裡我們主要用 runtime 建立 pool。
  • dotenvy:用來從 .env 載入 DATABASE_URL(選用,方便 local 開發)。

.env (放在專案根目錄,切勿推上版本控制)

DATABASE_URL=postgres://myuser:mypassword@127.0.0.1:5432/mydb

調整 myuser、mypassword、host、port、mydb 為你系統上的設定。

main.rs

請把 src/main.rs 改成下列內容:

use sqlx::postgres::PgPoolOptions;
use std::time::Duration;
use dotenvy::dotenv;
use std::env;
use tokio::time;

#[tokio::main]
async fn main() {
    dotenv().ok();

    let database_url = match env::var("DATABASE_URL") {
        Ok(v) => v,
        Err(_) => {
            eprintln!("錯誤:找不到 DATABASE_URL");
            std::process::exit(1);
        }
    };

    // 例如 5 秒超時
    let connect_future = PgPoolOptions::new()
        .max_connections(5)
        .connect(&database_url);

    let pool = match time::timeout(Duration::from_secs(5), connect_future).await {
        Ok(Ok(p)) => {
            println!("成功建立 PgPool");
            p
        }
        Ok(Err(e)) => {
            eprintln!("建立 PgPool 失敗: {}", e);
            std::process::exit(1);
        }
        Err(_) => {
            eprintln!("建立 PgPool 超時");
            std::process::exit(1);
        }
    };

    // ping
    match sqlx::query_scalar::<_, i32>("SELECT 1")
        .fetch_one(&pool)
        .await
    {
        Ok(v) => println!("Ping 成功(SELECT 1 回傳 {})", v),
        Err(e) => {
            eprintln!("Ping 失敗: {}", e);
            std::process::exit(1);
        }
    }
}

說明:

  • 第一階段在建立 pool 時就可能失敗(例如無法連到 DB、認證錯誤),程式會印錯誤並結束。
  • 第二階段透過簡單 query 做 ping,確保連線能成功執行 SQL。

執行

  • 在專案根目錄,確認 .env 有設定 DATABASE_URL,然後執行:
cargo run
  • 成功輸出範例:
成功建立 PgPool
Ping 成功(SELECT 1 回傳 1)
  • 失敗輸出範例(連線失敗或認證錯誤):
建立 PgPool 失敗: error returned from database: password authentication failed for user "myuser"
或
Ping 失敗: error returned from database: connection refused

常見問題與除錯建議

  • 找不到 psql / 指令無法執行
    • Windows:確認 PostgreSQL 的 bin 路徑加入 PATH,或使用完整路徑執行。
    • macOS(Homebrew):確定 /opt/homebrew/bin(Apple Silicon)或 /usr/local/bin 在 PATH。
  • 連線被拒(connection refused)
    • 檢查 PostgreSQL 服務是否已啟動。
    • 確認 host 與 port 是否正確(localhost vs 127.0.0.1),以及防火牆規則。
  • 認證錯誤(password authentication failed)
    • 確認 DATABASE_URL 中的帳號密碼正確,使用 psql 測試同樣的帳密。
  • 權限問題
    • 若使用自訂 user,確認該 user 有權限存取指定資料庫(GRANT 或建立時指定)。
  • sqlx 連線超時
    • 可以調整 connect_timeout 與 pool 設定,或檢查 network/pg_hba.conf 等設定。
  • 若在 macOS 用 Postgres.app,記得把 bin 路徑加入 PATH,否則 psql 可能找不到。

上一篇
Day 14 Axum 專案:簡單 RESTful API
下一篇
Day 16 Axum 撰寫 migration
系列文
Rust 後端入門16
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言