Files
udmin/docs/DATABASE.md
ayou a3f2f99a68 docs: 添加项目文档包括总览、架构、流程引擎和服务层
新增以下文档文件:
- PROJECT_OVERVIEW.md 项目总览文档
- BACKEND_ARCHITECTURE.md 后端架构文档
- FRONTEND_ARCHITECTURE.md 前端架构文档
- FLOW_ENGINE.md 流程引擎文档
- SERVICES.md 服务层文档
- ERROR_HANDLING.md 错误处理模块文档

文档内容涵盖项目整体介绍、技术架构、核心模块设计和实现细节
2025-09-24 20:21:45 +08:00

1056 lines
31 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# UdminAI 数据库模块文档
## 概述
UdminAI 项目的数据库模块基于 SeaORM 框架构建,提供了完整的数据库抽象层和 ORM 功能。该模块负责数据库连接管理、事务处理、查询构建、迁移管理等核心功能,为整个系统提供可靠的数据持久化支持。
## 技术架构
### 核心组件
- **SeaORM**: 现代化的 Rust ORM 框架
- **SQLx**: 异步 SQL 工具包
- **PostgreSQL**: 主数据库(支持 MySQL、SQLite
- **Redis**: 缓存和会话存储
- **Migration**: 数据库版本管理
### 设计原则
- **类型安全**: 编译时查询验证
- **异步优先**: 全异步数据库操作
- **连接池**: 高效的连接管理
- **事务支持**: ACID 事务保证
- **迁移管理**: 版本化数据库结构
## 模块结构
```
backend/src/
├── db.rs # 数据库连接和配置
├── redis.rs # Redis 连接和操作
└── models/ # 数据模型定义
├── mod.rs
├── user.rs
├── role.rs
├── permission.rs
├── flow.rs
├── schedule_job.rs
└── ...
```
## 数据库连接 (db.rs)
### 功能特性
- 数据库连接池管理
- 多数据库支持
- 连接健康检查
- 自动重连机制
- 性能监控
### 实现代码
```rust
use sea_orm::{
ConnectOptions, Database, DatabaseConnection, DbErr, TransactionTrait,
};
use std::time::Duration;
use tracing::{error, info, warn};
/// 数据库配置
#[derive(Debug, Clone)]
pub struct DatabaseConfig {
pub url: String,
pub max_connections: u32,
pub min_connections: u32,
pub connect_timeout: Duration,
pub idle_timeout: Duration,
pub max_lifetime: Duration,
pub sqlx_logging: bool,
pub sqlx_logging_level: tracing::Level,
}
impl Default for DatabaseConfig {
fn default() -> Self {
Self {
url: "postgresql://localhost/udmin_ai".to_string(),
max_connections: 100,
min_connections: 5,
connect_timeout: Duration::from_secs(8),
idle_timeout: Duration::from_secs(600),
max_lifetime: Duration::from_secs(3600),
sqlx_logging: true,
sqlx_logging_level: tracing::Level::INFO,
}
}
}
/// 数据库连接管理器
#[derive(Debug, Clone)]
pub struct DatabaseManager {
connection: DatabaseConnection,
config: DatabaseConfig,
}
impl DatabaseManager {
/// 创建新的数据库管理器
pub async fn new(config: DatabaseConfig) -> Result<Self, DbErr> {
info!(target = "udmin", url = %config.url, "database.connect.starting");
let mut opt = ConnectOptions::new(&config.url);
opt.max_connections(config.max_connections)
.min_connections(config.min_connections)
.connect_timeout(config.connect_timeout)
.idle_timeout(config.idle_timeout)
.max_lifetime(config.max_lifetime)
.sqlx_logging(config.sqlx_logging)
.sqlx_logging_level(config.sqlx_logging_level);
let connection = Database::connect(opt).await?;
info!(target = "udmin", "database.connect.success");
Ok(Self { connection, config })
}
/// 从环境变量创建数据库管理器
pub async fn from_env() -> Result<Self, DbErr> {
let config = DatabaseConfig {
url: std::env::var("DATABASE_URL")
.unwrap_or_else(|_| "postgresql://localhost/udmin_ai".to_string()),
max_connections: std::env::var("DB_MAX_CONNECTIONS")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(100),
min_connections: std::env::var("DB_MIN_CONNECTIONS")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(5),
connect_timeout: Duration::from_secs(
std::env::var("DB_CONNECT_TIMEOUT")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(8),
),
idle_timeout: Duration::from_secs(
std::env::var("DB_IDLE_TIMEOUT")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(600),
),
max_lifetime: Duration::from_secs(
std::env::var("DB_MAX_LIFETIME")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(3600),
),
sqlx_logging: std::env::var("DB_SQLX_LOGGING")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(true),
sqlx_logging_level: match std::env::var("DB_SQLX_LOGGING_LEVEL")
.unwrap_or_else(|_| "info".to_string())
.to_lowercase()
.as_str()
{
"trace" => tracing::Level::TRACE,
"debug" => tracing::Level::DEBUG,
"info" => tracing::Level::INFO,
"warn" => tracing::Level::WARN,
"error" => tracing::Level::ERROR,
_ => tracing::Level::INFO,
},
};
Self::new(config).await
}
/// 获取数据库连接
pub fn connection(&self) -> &DatabaseConnection {
&self.connection
}
/// 检查数据库连接健康状态
pub async fn health_check(&self) -> Result<(), DbErr> {
use sea_orm::Statement;
let backend = self.connection.get_database_backend();
let stmt = Statement::from_string(backend, "SELECT 1".to_string());
match self.connection.execute(stmt).await {
Ok(_) => {
info!(target = "udmin", "database.health_check.success");
Ok(())
}
Err(e) => {
error!(target = "udmin", error = %e, "database.health_check.failed");
Err(e)
}
}
}
/// 获取连接池统计信息
pub async fn pool_stats(&self) -> DatabasePoolStats {
// 注意SeaORM 目前不直接暴露连接池统计信息
// 这里提供一个接口,实际实现可能需要通过其他方式获取
DatabasePoolStats {
active_connections: 0, // 需要通过底层 SQLx 获取
idle_connections: 0,
total_connections: 0,
max_connections: self.config.max_connections,
}
}
/// 执行事务
pub async fn transaction<F, R, E>(&self, f: F) -> Result<R, E>
where
F: for<'c> FnOnce(&'c DatabaseConnection) -> futures::future::BoxFuture<'c, Result<R, E>>
+ Send,
E: From<DbErr>,
R: Send,
{
let txn = self.connection.begin().await.map_err(E::from)?;
match f(&txn).await {
Ok(result) => {
txn.commit().await.map_err(E::from)?;
info!(target = "udmin", "database.transaction.committed");
Ok(result)
}
Err(e) => {
if let Err(rollback_err) = txn.rollback().await {
error!(target = "udmin", error = %rollback_err, "database.transaction.rollback_failed");
}
warn!(target = "udmin", "database.transaction.rolled_back");
Err(e)
}
}
}
/// 关闭数据库连接
pub async fn close(&self) -> Result<(), DbErr> {
info!(target = "udmin", "database.connection.closing");
self.connection.close().await?;
info!(target = "udmin", "database.connection.closed");
Ok(())
}
}
/// 数据库连接池统计信息
#[derive(Debug, Clone)]
pub struct DatabasePoolStats {
pub active_connections: u32,
pub idle_connections: u32,
pub total_connections: u32,
pub max_connections: u32,
}
/// 数据库迁移管理
pub struct MigrationManager {
connection: DatabaseConnection,
}
impl MigrationManager {
pub fn new(connection: DatabaseConnection) -> Self {
Self { connection }
}
/// 运行所有待执行的迁移
pub async fn migrate_up(&self) -> Result<(), DbErr> {
info!(target = "udmin", "database.migration.starting");
// 这里需要根据实际的迁移框架实现
// 例如使用 sea-orm-migration
info!(target = "udmin", "database.migration.completed");
Ok(())
}
/// 回滚迁移
pub async fn migrate_down(&self, steps: Option<u32>) -> Result<(), DbErr> {
let steps = steps.unwrap_or(1);
info!(target = "udmin", steps = %steps, "database.migration.rollback.starting");
// 实现迁移回滚逻辑
info!(target = "udmin", steps = %steps, "database.migration.rollback.completed");
Ok(())
}
/// 获取迁移状态
pub async fn migration_status(&self) -> Result<Vec<MigrationInfo>, DbErr> {
// 返回迁移状态信息
Ok(vec![])
}
}
/// 迁移信息
#[derive(Debug, Clone)]
pub struct MigrationInfo {
pub version: String,
pub name: String,
pub applied_at: Option<chrono::DateTime<chrono::Utc>>,
pub is_applied: bool,
}
/// 数据库查询构建器辅助函数
pub mod query_builder {
use sea_orm::{
ColumnTrait, Condition, EntityTrait, PaginatorTrait, QueryFilter, QueryOrder, QuerySelect,
Select,
};
/// 分页查询构建器
pub struct PaginationBuilder<E: EntityTrait> {
select: Select<E>,
page: u64,
page_size: u64,
}
impl<E: EntityTrait> PaginationBuilder<E> {
pub fn new(select: Select<E>) -> Self {
Self {
select,
page: 1,
page_size: 20,
}
}
pub fn page(mut self, page: u64) -> Self {
self.page = page.max(1);
self
}
pub fn page_size(mut self, page_size: u64) -> Self {
self.page_size = page_size.clamp(1, 100);
self
}
pub fn build(self) -> Select<E> {
let offset = (self.page - 1) * self.page_size;
self.select.limit(self.page_size).offset(offset)
}
}
/// 条件构建器
pub struct ConditionBuilder {
condition: Condition,
}
impl ConditionBuilder {
pub fn new() -> Self {
Self {
condition: Condition::all(),
}
}
pub fn add<C>(mut self, column_condition: C) -> Self
where
C: Into<Condition>,
{
self.condition = self.condition.add(column_condition);
self
}
pub fn add_option<C>(mut self, condition: Option<C>) -> Self
where
C: Into<Condition>,
{
if let Some(cond) = condition {
self.condition = self.condition.add(cond);
}
self
}
pub fn build(self) -> Condition {
self.condition
}
}
impl Default for ConditionBuilder {
fn default() -> Self {
Self::new()
}
}
}
/// 数据库错误处理
pub mod error_handler {
use sea_orm::DbErr;
use crate::error::AppError;
/// 将数据库错误转换为应用错误
pub fn handle_db_error(err: DbErr) -> AppError {
match err {
DbErr::RecordNotFound(_) => AppError::NotFound("记录不存在".to_string()),
DbErr::Custom(msg) => AppError::DatabaseError(msg),
DbErr::Conn(msg) => AppError::DatabaseError(format!("连接错误: {}", msg)),
DbErr::Exec(msg) => AppError::DatabaseError(format!("执行错误: {}", msg)),
DbErr::Query(msg) => AppError::DatabaseError(format!("查询错误: {}", msg)),
_ => AppError::DatabaseError("未知数据库错误".to_string()),
}
}
/// 检查是否为唯一约束违反错误
pub fn is_unique_violation(err: &DbErr) -> bool {
match err {
DbErr::Exec(msg) | DbErr::Query(msg) => {
msg.contains("unique constraint") || msg.contains("duplicate key")
}
_ => false,
}
}
/// 检查是否为外键约束违反错误
pub fn is_foreign_key_violation(err: &DbErr) -> bool {
match err {
DbErr::Exec(msg) | DbErr::Query(msg) => {
msg.contains("foreign key constraint")
}
_ => false,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use tokio_test;
#[tokio::test]
async fn test_database_config() {
let config = DatabaseConfig::default();
assert_eq!(config.max_connections, 100);
assert_eq!(config.min_connections, 5);
}
#[tokio::test]
async fn test_condition_builder() {
use query_builder::ConditionBuilder;
use sea_orm::{ColumnTrait, Condition};
let condition = ConditionBuilder::new()
.add_option(Some(Condition::all()))
.build();
// 验证条件构建
assert!(!format!("{:?}", condition).is_empty());
}
#[test]
fn test_error_handling() {
use error_handler::*;
let db_err = DbErr::RecordNotFound("test".to_string());
let app_err = handle_db_error(db_err);
match app_err {
AppError::NotFound(_) => assert!(true),
_ => assert!(false, "Expected NotFound error"),
}
}
}
```
## Redis 连接 (redis.rs)
### 功能特性
- Redis 连接池管理
- 缓存操作封装
- 会话存储
- 分布式锁
- 发布订阅
### 实现代码
```rust
use redis::{
aio::ConnectionManager, Client, RedisError, RedisResult, AsyncCommands,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use tracing::{error, info, warn};
use tokio::time::timeout;
/// Redis 配置
#[derive(Debug, Clone)]
pub struct RedisConfig {
pub url: String,
pub max_connections: u32,
pub connect_timeout: Duration,
pub command_timeout: Duration,
pub retry_attempts: u32,
pub retry_delay: Duration,
}
impl Default for RedisConfig {
fn default() -> Self {
Self {
url: "redis://localhost:6379".to_string(),
max_connections: 50,
connect_timeout: Duration::from_secs(5),
command_timeout: Duration::from_secs(10),
retry_attempts: 3,
retry_delay: Duration::from_millis(100),
}
}
}
/// Redis 连接管理器
#[derive(Debug, Clone)]
pub struct RedisManager {
connection: ConnectionManager,
config: RedisConfig,
}
impl RedisManager {
/// 创建新的 Redis 管理器
pub async fn new(config: RedisConfig) -> RedisResult<Self> {
info!(target = "udmin", url = %config.url, "redis.connect.starting");
let client = Client::open(config.url.clone())?;
let connection = timeout(
config.connect_timeout,
ConnectionManager::new(client),
)
.await
.map_err(|_| RedisError::from((redis::ErrorKind::IoError, "连接超时")))??
;
info!(target = "udmin", "redis.connect.success");
Ok(Self { connection, config })
}
/// 从环境变量创建 Redis 管理器
pub async fn from_env() -> RedisResult<Self> {
let config = RedisConfig {
url: std::env::var("REDIS_URL")
.unwrap_or_else(|_| "redis://localhost:6379".to_string()),
max_connections: std::env::var("REDIS_MAX_CONNECTIONS")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(50),
connect_timeout: Duration::from_secs(
std::env::var("REDIS_CONNECT_TIMEOUT")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(5),
),
command_timeout: Duration::from_secs(
std::env::var("REDIS_COMMAND_TIMEOUT")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(10),
),
retry_attempts: std::env::var("REDIS_RETRY_ATTEMPTS")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(3),
retry_delay: Duration::from_millis(
std::env::var("REDIS_RETRY_DELAY_MS")
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(100),
),
};
Self::new(config).await
}
/// 执行带超时的 Redis 命令
async fn execute_with_timeout<F, R>(&self, f: F) -> RedisResult<R>
where
F: futures::Future<Output = RedisResult<R>>,
{
timeout(self.config.command_timeout, f)
.await
.map_err(|_| RedisError::from((redis::ErrorKind::IoError, "命令执行超时")))??
}
/// 设置键值对
pub async fn set<K, V>(&self, key: K, value: V) -> RedisResult<()>
where
K: redis::ToRedisArgs + Send + Sync,
V: redis::ToRedisArgs + Send + Sync,
{
let mut conn = self.connection.clone();
self.execute_with_timeout(async move {
conn.set(key, value).await
}).await
}
/// 设置键值对(带过期时间)
pub async fn setex<K, V>(&self, key: K, value: V, seconds: usize) -> RedisResult<()>
where
K: redis::ToRedisArgs + Send + Sync,
V: redis::ToRedisArgs + Send + Sync,
{
let mut conn = self.connection.clone();
self.execute_with_timeout(async move {
conn.setex(key, seconds, value).await
}).await
}
/// 获取键值
pub async fn get<K, V>(&self, key: K) -> RedisResult<Option<V>>
where
K: redis::ToRedisArgs + Send + Sync,
V: redis::FromRedisValue,
{
let mut conn = self.connection.clone();
self.execute_with_timeout(async move {
conn.get(key).await
}).await
}
/// 删除键
pub async fn del<K>(&self, key: K) -> RedisResult<bool>
where
K: redis::ToRedisArgs + Send + Sync,
{
let mut conn = self.connection.clone();
let result: i32 = self.execute_with_timeout(async move {
conn.del(key).await
}).await?;
Ok(result > 0)
}
/// 检查键是否存在
pub async fn exists<K>(&self, key: K) -> RedisResult<bool>
where
K: redis::ToRedisArgs + Send + Sync,
{
let mut conn = self.connection.clone();
self.execute_with_timeout(async move {
conn.exists(key).await
}).await
}
/// 设置键的过期时间
pub async fn expire<K>(&self, key: K, seconds: usize) -> RedisResult<bool>
where
K: redis::ToRedisArgs + Send + Sync,
{
let mut conn = self.connection.clone();
self.execute_with_timeout(async move {
conn.expire(key, seconds).await
}).await
}
/// 获取键的剩余过期时间
pub async fn ttl<K>(&self, key: K) -> RedisResult<i32>
where
K: redis::ToRedisArgs + Send + Sync,
{
let mut conn = self.connection.clone();
self.execute_with_timeout(async move {
conn.ttl(key).await
}).await
}
/// 原子递增
pub async fn incr<K>(&self, key: K, delta: i64) -> RedisResult<i64>
where
K: redis::ToRedisArgs + Send + Sync,
{
let mut conn = self.connection.clone();
self.execute_with_timeout(async move {
conn.incr(key, delta).await
}).await
}
/// 原子递减
pub async fn decr<K>(&self, key: K, delta: i64) -> RedisResult<i64>
where
K: redis::ToRedisArgs + Send + Sync,
{
let mut conn = self.connection.clone();
self.execute_with_timeout(async move {
conn.decr(key, delta).await
}).await
}
/// 列表左推
pub async fn lpush<K, V>(&self, key: K, value: V) -> RedisResult<i32>
where
K: redis::ToRedisArgs + Send + Sync,
V: redis::ToRedisArgs + Send + Sync,
{
let mut conn = self.connection.clone();
self.execute_with_timeout(async move {
conn.lpush(key, value).await
}).await
}
/// 列表右弹
pub async fn rpop<K, V>(&self, key: K) -> RedisResult<Option<V>>
where
K: redis::ToRedisArgs + Send + Sync,
V: redis::FromRedisValue,
{
let mut conn = self.connection.clone();
self.execute_with_timeout(async move {
conn.rpop(key, None).await
}).await
}
/// 获取列表长度
pub async fn llen<K>(&self, key: K) -> RedisResult<i32>
where
K: redis::ToRedisArgs + Send + Sync,
{
let mut conn = self.connection.clone();
self.execute_with_timeout(async move {
conn.llen(key).await
}).await
}
/// 哈希设置字段
pub async fn hset<K, F, V>(&self, key: K, field: F, value: V) -> RedisResult<bool>
where
K: redis::ToRedisArgs + Send + Sync,
F: redis::ToRedisArgs + Send + Sync,
V: redis::ToRedisArgs + Send + Sync,
{
let mut conn = self.connection.clone();
self.execute_with_timeout(async move {
conn.hset(key, field, value).await
}).await
}
/// 哈希获取字段
pub async fn hget<K, F, V>(&self, key: K, field: F) -> RedisResult<Option<V>>
where
K: redis::ToRedisArgs + Send + Sync,
F: redis::ToRedisArgs + Send + Sync,
V: redis::FromRedisValue,
{
let mut conn = self.connection.clone();
self.execute_with_timeout(async move {
conn.hget(key, field).await
}).await
}
/// 哈希删除字段
pub async fn hdel<K, F>(&self, key: K, field: F) -> RedisResult<bool>
where
K: redis::ToRedisArgs + Send + Sync,
F: redis::ToRedisArgs + Send + Sync,
{
let mut conn = self.connection.clone();
let result: i32 = self.execute_with_timeout(async move {
conn.hdel(key, field).await
}).await?;
Ok(result > 0)
}
/// 检查 Redis 连接健康状态
pub async fn health_check(&self) -> RedisResult<()> {
let mut conn = self.connection.clone();
match self.execute_with_timeout(async move {
redis::cmd("PING").query_async(&mut conn).await
}).await {
Ok(redis::Value::Status(status)) if status == "PONG" => {
info!(target = "udmin", "redis.health_check.success");
Ok(())
}
Ok(_) => {
error!(target = "udmin", "redis.health_check.unexpected_response");
Err(RedisError::from((redis::ErrorKind::ResponseError, "意外的响应")))
}
Err(e) => {
error!(target = "udmin", error = %e, "redis.health_check.failed");
Err(e)
}
}
}
}
/// Redis 缓存操作封装
pub struct RedisCache {
manager: RedisManager,
key_prefix: String,
default_ttl: Duration,
}
impl RedisCache {
pub fn new(manager: RedisManager, key_prefix: String, default_ttl: Duration) -> Self {
Self {
manager,
key_prefix,
default_ttl,
}
}
/// 构建完整的缓存键
fn build_key(&self, key: &str) -> String {
format!("{}:{}", self.key_prefix, key)
}
/// 设置缓存JSON 序列化)
pub async fn set_json<T>(&self, key: &str, value: &T) -> RedisResult<()>
where
T: Serialize,
{
let json_value = serde_json::to_string(value)
.map_err(|e| RedisError::from((redis::ErrorKind::TypeError, e.to_string())))?;
let full_key = self.build_key(key);
self.manager.setex(full_key, json_value, self.default_ttl.as_secs() as usize).await
}
/// 获取缓存JSON 反序列化)
pub async fn get_json<T>(&self, key: &str) -> RedisResult<Option<T>>
where
T: for<'de> Deserialize<'de>,
{
let full_key = self.build_key(key);
let json_value: Option<String> = self.manager.get(full_key).await?;
match json_value {
Some(json) => {
let value = serde_json::from_str(&json)
.map_err(|e| RedisError::from((redis::ErrorKind::TypeError, e.to_string())))?;
Ok(Some(value))
}
None => Ok(None),
}
}
/// 删除缓存
pub async fn delete(&self, key: &str) -> RedisResult<bool> {
let full_key = self.build_key(key);
self.manager.del(full_key).await
}
/// 检查缓存是否存在
pub async fn exists(&self, key: &str) -> RedisResult<bool> {
let full_key = self.build_key(key);
self.manager.exists(full_key).await
}
/// 设置缓存过期时间
pub async fn expire(&self, key: &str, ttl: Duration) -> RedisResult<bool> {
let full_key = self.build_key(key);
self.manager.expire(full_key, ttl.as_secs() as usize).await
}
/// 获取或设置缓存(缓存穿透保护)
pub async fn get_or_set<T, F, Fut>(&self, key: &str, fetcher: F) -> RedisResult<T>
where
T: Serialize + for<'de> Deserialize<'de> + Clone,
F: FnOnce() -> Fut,
Fut: futures::Future<Output = Result<T, Box<dyn std::error::Error + Send + Sync>>>,
{
// 先尝试从缓存获取
if let Some(cached_value) = self.get_json::<T>(key).await? {
return Ok(cached_value);
}
// 缓存未命中,调用 fetcher 获取数据
let value = fetcher().await
.map_err(|e| RedisError::from((redis::ErrorKind::TypeError, e.to_string())))?;
// 将数据存入缓存
if let Err(e) = self.set_json(key, &value).await {
warn!(target = "udmin", key = %key, error = %e, "redis.cache.set_failed");
}
Ok(value)
}
}
/// 分布式锁
pub struct DistributedLock {
manager: RedisManager,
key: String,
value: String,
ttl: Duration,
}
impl DistributedLock {
/// 尝试获取锁
pub async fn acquire(
manager: RedisManager,
key: String,
ttl: Duration,
) -> RedisResult<Option<Self>> {
let value = uuid::Uuid::new_v4().to_string();
let lock_key = format!("lock:{}", key);
let mut conn = manager.connection.clone();
let result: Option<String> = redis::cmd("SET")
.arg(&lock_key)
.arg(&value)
.arg("EX")
.arg(ttl.as_secs())
.arg("NX")
.query_async(&mut conn)
.await?;
if result.is_some() {
info!(target = "udmin", key = %key, "distributed_lock.acquired");
Ok(Some(Self {
manager,
key: lock_key,
value,
ttl,
}))
} else {
Ok(None)
}
}
/// 释放锁
pub async fn release(self) -> RedisResult<bool> {
let script = r#"
if redis.call("GET", KEYS[1]) == ARGV[1] then
return redis.call("DEL", KEYS[1])
else
return 0
end
"#;
let mut conn = self.manager.connection.clone();
let result: i32 = redis::Script::new(script)
.key(&self.key)
.arg(&self.value)
.invoke_async(&mut conn)
.await?;
let released = result > 0;
if released {
info!(target = "udmin", key = %self.key, "distributed_lock.released");
}
Ok(released)
}
/// 续期锁
pub async fn renew(&self) -> RedisResult<bool> {
let script = r#"
if redis.call("GET", KEYS[1]) == ARGV[1] then
return redis.call("EXPIRE", KEYS[1], ARGV[2])
else
return 0
end
"#;
let mut conn = self.manager.connection.clone();
let result: i32 = redis::Script::new(script)
.key(&self.key)
.arg(&self.value)
.arg(self.ttl.as_secs())
.invoke_async(&mut conn)
.await?;
Ok(result > 0)
}
}
#[cfg(test)]
mod tests {
use super::*;
use tokio_test;
#[tokio::test]
async fn test_redis_config() {
let config = RedisConfig::default();
assert_eq!(config.max_connections, 50);
assert_eq!(config.connect_timeout, Duration::from_secs(5));
}
#[test]
fn test_cache_key_building() {
let manager = RedisManager {
connection: todo!(), // 在实际测试中需要模拟
config: RedisConfig::default(),
};
let cache = RedisCache::new(manager, "test".to_string(), Duration::from_secs(300));
assert_eq!(cache.build_key("user:123"), "test:user:123");
}
}
```
## 性能优化
### 连接池优化
- **动态连接池**: 根据负载自动调整连接数
- **连接复用**: 最大化连接利用率
- **健康检查**: 定期检查连接状态
- **超时控制**: 防止连接泄漏
### 查询优化
- **索引优化**: 合理设计数据库索引
- **查询缓存**: Redis 缓存热点数据
- **批量操作**: 减少数据库往返次数
- **分页优化**: 高效的分页查询
### 缓存策略
- **多级缓存**: 内存 + Redis 缓存
- **缓存预热**: 系统启动时预加载热点数据
- **缓存穿透保护**: 防止恶意查询
- **缓存雪崩保护**: 错开缓存过期时间
## 监控和日志
### 性能监控
- **连接池监控**: 活跃连接数、等待队列长度
- **查询性能**: 慢查询日志、执行时间统计
- **缓存命中率**: Redis 缓存效果监控
- **错误率监控**: 数据库错误统计
### 日志记录
- **操作日志**: 记录所有数据库操作
- **性能日志**: 记录查询执行时间
- **错误日志**: 详细的错误信息和堆栈
- **审计日志**: 敏感操作的审计记录
## 最佳实践
### 数据库设计
1. **规范化设计**: 避免数据冗余
2. **索引策略**: 合理创建和维护索引
3. **数据类型**: 选择合适的数据类型
4. **约束设计**: 充分利用数据库约束
### 查询优化
1. **避免 N+1 查询**: 使用 JOIN 或预加载
2. **分页查询**: 使用 LIMIT 和 OFFSET
3. **条件过滤**: 在数据库层面进行过滤
4. **批量操作**: 合并多个操作为批量操作
### 事务管理
1. **事务边界**: 明确事务的开始和结束
2. **隔离级别**: 根据需求选择合适的隔离级别
3. **死锁处理**: 设计避免死锁的策略
4. **回滚策略**: 合理的错误处理和回滚
### 缓存使用
1. **缓存键设计**: 有意义且唯一的缓存键
2. **过期策略**: 合理设置缓存过期时间
3. **缓存更新**: 及时更新或删除过期缓存
4. **缓存预热**: 系统启动时预加载重要数据
## 总结
UdminAI 的数据库模块提供了完整的数据持久化解决方案,具有以下特点:
- **高性能**: 连接池管理和查询优化
- **高可用**: 健康检查和自动重连
- **类型安全**: SeaORM 提供的编译时检查
- **易维护**: 清晰的模块结构和错误处理
- **可扩展**: 支持多种数据库和缓存策略
通过合理的架构设计和最佳实践,确保了系统的稳定性、性能和可维护性。