在Linux环境下用Rust操作数据库,是很多后端开发者绕不开的实战场景。这篇文章会带你从零开始,走完环境准备、库选型、同步/异步操作、ORM使用,再到最佳实践和常见坑的完整流程。顺便说一句,文中所有代码都在Ubuntu 22.04上验证过,但其他发行版也基本通用。

一、环境准备
1. Rust工具链安装
在Ubuntu或Debian这类Linux发行版上,安装Rust工具链(包含rustc编译器、cargo包管理器)以及必要的编译依赖,只需几条命令:
sudo apt update && sudo apt install -y build-essential curl git
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env
验证安装是否成功:
rustc --version # 查看Rust编译器版本
cargo --version # 查看包管理器版本
2. 数据库安装(以PostgreSQL为例)
sudo apt install -y postgresql postgresql-contrib # 安装PostgreSQL及扩展
sudo -u postgres createuser --interactive # 交互式创建数据库用户(需输入用户名/密码)
sudo -u postgres createdb testdb # 为用户创建数据库
如果选用SQLite这种嵌入式数据库,则无需额外安装服务,后面直接用Rust库操作即可,非常方便。
二、常用数据库操作库选择
Rust生态里成熟的数据库库不少,怎么选?看你的场景:
- 同步操作:
rusqlite(SQLite)、diesel(PostgreSQL/MySQL/SQLite,自带查询构建器)、mysql(MySQL)。 - 异步操作:
tokio-postgres(PostgreSQL,配合tokio运行时)、sqlx(PostgreSQL/MySQL/SQLite,异步+编译时SQL检查)、mongodb(NoSQL数据库)。 - ORM框架:
Diesel(类型安全查询构建)、SQLx(异步ORM,支持编译时SQL验证)。
简单说,追求轻量选rusqlite,要异步和高性能选sqlx或tokio-postgres,喜欢ORM风格选Diesel。
三、同步数据库操作示例(以SQLite为例)
1. 添加依赖
在项目根目录的Cargo.toml中添加rusqlite(支持SQLite)和可选的serde(用于数据序列化):
[dependencies]
rusqlite = { version = "0.26", features = ["bundled"] } # bundled 包含SQLite源码,无需系统安装
serde = { version = "1.0", features = ["derive"] } # 可选,用于结构体序列化
2. 基础CRUD操作
use rusqlite::{params, Connection, Result};
use serde::{Serialize, Deserialize};
// 定义用户结构体(可选,用于数据映射)
#[derive(Debug, Serialize, Deserialize)]
struct User {
id: i32,
name: String,
age: i32,
}
fn main() -> Result<()> {
// 1. 连接数据库(若文件不存在则自动创建)
let conn = Connection::open("test.db")?;
// 2. 创建表(IF NOT EXISTS避免重复创建)
conn.execute(
"CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
age INTEGER
)",
[],
)?;
// 3. 插入数据(参数化查询,防止SQL注入)
conn.execute(
"INSERT INTO users (name, age) VALUES (?1, ?2)",
params![ "Alice", 30 ],
)?;
// 4. 查询数据(使用query_map映射到结构体)
let mut stmt = conn.prepare("SELECT id, name, age FROM users")?;
let user_iter = stmt.query_map([], |row| {
Ok(User {
id: row.get(0)?,
name: row.get(1)?,
age: row.get(2)?,
})
})?;
for user in user_iter {
println!("User: {:?}", user?); // 输出:User { id: 1, name: "Alice", age: 30 }
}
Ok(())
}
这里有几个关键点值得注意:
- 参数化查询(
params![...])是防止SQL注入的核心手段,任何时候都不要拼字符串; ?操作符用于错误传播,让代码简洁很多;bundled特性避免依赖系统SQLite库,可移植性更好。
四、异步数据库操作示例(以PostgreSQL为例)
1. 添加依赖
在Cargo.toml中添加tokio(异步运行时)和tokio-postgres(PostgreSQL客户端):
[dependencies]
tokio = { version = "1.0", features = ["full"] } # 异步运行时
tokio-postgres = "0.7" # PostgreSQL异步客户端
2. 异步CRUD操作
use tokio_postgres::{NoTls, Error};
#[tokio::main] // 标记异步主函数
async fn main() -> Result<(), Error> {
// 1. 连接数据库(异步)
let (client, connection) = tokio_postgres::connect(
"host=localhost user=postgres dbname=testdb", // 连接字符串
NoTls, // 无TLS加密(生产环境建议启用)
).await?;
// 2. 后台运行连接任务(处理连接错误)
tokio::spawn(async move {
if let Err(e) = connection.await {
eprintln!("Connection error: {}", e);
}
});
// 3. 执行查询(异步)
let rows = client
.query("SELECT id, name, age FROM users", &[])
.await?;
// 4. 处理结果
for row in rows {
let id: i32 = row.get(0);
let name: String = row.get(1);
let age: i32 = row.get(2);
println!("ID: {}, Name: {}, Age: {}", id, name, age);
}
Ok(())
}
几个要点:
#[tokio::main]宏会把主函数变成异步函数,依赖tokio运行时;- 异步操作必须用
.await,否则会阻塞线程; - 生产环境强烈建议使用TLS加密,把
NoTls换成native_tls或rustls。
五、ORM框架使用示例(以Diesel为例)
1. 添加依赖
在Cargo.toml中添加diesel(支持PostgreSQL/MySQL/SQLite)和dotenv(环境变量管理):
[dependencies]
diesel = { version = "1.4", features = ["postgres"] } # 替换为mysql或sqlite适配对应数据库
dotenv = "0.15" # 管理环境变量
2. 配置数据库连接
在项目根目录创建.env文件:
DATABASE_URL=postgres://postgres:your_password@localhost/testdb
3. 定义Schema和模型
运行diesel setup初始化数据库迁移目录,然后创建迁移:
diesel migration generate create_users
编辑迁移文件(migrations/xxxx_create_users/up.sql):
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
age INTEGER
);
运行迁移:
diesel migration run
4. 基础操作
#[macro_use] extern crate diesel;
use diesel::prelude::*;
use dotenv::dotenv;
use std::env;
// 定义模型
#[derive(Queryable, Insertable)]
#[table_name = "users"]
struct User {
name: String,
age: i32,
}
fn main() {
// 加载环境变量
dotenv().ok();
// 建立数据库连接
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
let connection = PgConnection::establish(&database_url)
.expect(&format!("Error connecting to {}", database_url));
// 插入数据
let new_user = User { name: "Bob".to_string(), age: 25 };
diesel::insert_into(users::table)
.values(&new_user)
.execute(&connection)
.expect("Error sa ving new user");
// 查询数据
let results = users::table
.filter(users::name.like("%B%"))
.limit(5)
.load::(&connection)
.expect("Error loading users");
println!("Found {} users", results.len());
for user in results {
println!("Name: {}, Age: {}", user.name, user.age);
}
}
Diesel的亮点:
- 通过
schema.rs自动生成表结构绑定,不用硬写SQL; - 类型安全的查询构建(如
filter、limit),编译时就能检查SQL合法性; - 数据库密码等敏感信息通过环境变量管理,安全性更高。
六、开发最佳实践
1. 安全策略
- 参数化查询:始终使用
params![...]或bind方法,这是避免SQL注入的底线。 - 错误处理:善用
Result类型和?操作符,或者用thiserror库封装自定义错误类型。
2. 性能优化
- 连接池:用
r2d2库管理数据库连接(如r2d2_sqlite、r2d2_postgres),避免频繁创建/销毁连接带来的开销。 - 批量操作:使用事务(
transaction)批量插入或更新数据,减少数据库往返次数。 - 索引优化:为高频查询字段添加索引,比如
CREATE INDEX idx_name ON users(name),能显著提升查询速度。
七、常见问题解决
- 连接失败:先检查数据库服务是否启动(
sudo systemctl status postgresql),再确认连接字符串中的主机、端口、用户名、密码、数据库名是否都正确。 - 依赖冲突:用
cargo tree查看依赖树,排查版本冲突,比如diesel和tokio的版本兼容性。 - 字符编码问题:确保数据库和Rust程序都使用UTF-8编码。PostgreSQL默认支持,SQLite也无需额外配置。