fix: 添加只读链接 读写分离
This commit is contained in:
parent
8d8e98188d
commit
0ea1b2cd38
|
|
@ -74,29 +74,52 @@ impl ModelRepository {
|
|||
}
|
||||
|
||||
/// 根据ID获取模特
|
||||
/// 使用读写分离避免嵌套锁问题
|
||||
pub fn get_by_id(&self, id: &str) -> Result<Option<Model>> {
|
||||
let conn = self.database.get_connection();
|
||||
let conn = conn.lock().unwrap();
|
||||
println!("get_by_id 开始执行,model_id: {}", id);
|
||||
|
||||
let mut stmt = conn.prepare(
|
||||
"SELECT id, name, stage_name, gender, age, height, weight, measurements,
|
||||
description, tags, avatar_path, contact_info, social_media,
|
||||
status, rating, created_at, updated_at, is_active
|
||||
FROM models WHERE id = ?1"
|
||||
)?;
|
||||
// 首先获取模特基本信息(使用只读连接)
|
||||
let model = {
|
||||
match self.database.try_get_read_connection() {
|
||||
Some(conn) => {
|
||||
println!("get_by_id 使用只读连接获取基本信息");
|
||||
|
||||
let model_iter = stmt.query_map([id], |row| {
|
||||
self.row_to_model(row)
|
||||
})?;
|
||||
let mut stmt = conn.prepare(
|
||||
"SELECT id, name, stage_name, gender, age, height, weight, measurements,
|
||||
description, tags, avatar_path, contact_info, social_media,
|
||||
status, rating, created_at, updated_at, is_active
|
||||
FROM models WHERE id = ?1"
|
||||
)?;
|
||||
|
||||
for model in model_iter {
|
||||
let mut model = model?;
|
||||
// 加载照片信息
|
||||
let model_iter = stmt.query_map([id], |row| {
|
||||
self.row_to_model(row)
|
||||
})?;
|
||||
|
||||
let mut result = None;
|
||||
for model in model_iter {
|
||||
result = Some(model?);
|
||||
break;
|
||||
}
|
||||
result
|
||||
},
|
||||
None => {
|
||||
println!("get_by_id 只读连接被占用,使用基本方法");
|
||||
// 如果只读连接被占用,使用不加载照片的基本方法
|
||||
return self.get_basic_by_id(id);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 如果找到模特,加载照片信息(此时已释放了基本信息查询的锁)
|
||||
if let Some(mut model) = model {
|
||||
println!("get_by_id 开始加载照片信息");
|
||||
model.photos = self.get_photos(&model.id)?;
|
||||
return Ok(Some(model));
|
||||
println!("get_by_id 执行完成");
|
||||
Ok(Some(model))
|
||||
} else {
|
||||
println!("get_by_id 未找到模特");
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// 根据ID获取模特基本信息(不加载照片,避免死锁)
|
||||
|
|
@ -269,12 +292,25 @@ impl ModelRepository {
|
|||
self.row_to_model(row)
|
||||
})?;
|
||||
|
||||
// 先收集所有模特基本信息
|
||||
let mut models = Vec::new();
|
||||
for model in model_iter {
|
||||
let mut model = model?;
|
||||
// 加载照片信息
|
||||
model.photos = self.get_photos(&model.id)?;
|
||||
models.push(model);
|
||||
models.push(model?);
|
||||
}
|
||||
|
||||
// 释放连接锁
|
||||
drop(stmt);
|
||||
drop(conn);
|
||||
|
||||
// 为每个模特加载照片信息(避免嵌套锁)
|
||||
for model in &mut models {
|
||||
model.photos = match self.get_photos(&model.id) {
|
||||
Ok(photos) => photos,
|
||||
Err(e) => {
|
||||
println!("search 加载模特 {} 的照片失败: {},跳过照片加载", model.id, e);
|
||||
Vec::new()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Ok(models)
|
||||
|
|
@ -373,22 +409,19 @@ impl ModelRepository {
|
|||
}
|
||||
|
||||
/// 获取模特照片
|
||||
/// 使用连接池优化的数据库访问模式,避免锁竞争
|
||||
/// 使用专用只读连接,避免与写操作的锁竞争
|
||||
pub fn get_photos(&self, model_id: &str) -> Result<Vec<ModelPhoto>> {
|
||||
println!("get_photos 开始执行,model_id: {}", model_id);
|
||||
|
||||
// 优先使用连接池,如果不可用则回退到单连接模式
|
||||
match self.database.get_best_connection() {
|
||||
// 使用专用的只读连接,避免与写操作竞争
|
||||
match self.database.get_best_read_connection() {
|
||||
Ok(conn) => {
|
||||
println!("get_photos 成功获取数据库连接({}模式)",
|
||||
if self.database.has_pool() { "连接池" } else { "单连接" });
|
||||
|
||||
let photos = self.execute_photo_query(&conn, model_id)?;
|
||||
println!("get_photos 执行完成,返回 {} 张照片", photos.len());
|
||||
Ok(photos)
|
||||
},
|
||||
Err(e) => {
|
||||
println!("get_photos 无法获取数据库连接: {}", e);
|
||||
println!("get_photos 无法获取只读数据库连接: {}", e);
|
||||
// 如果所有连接都被占用,返回空结果避免阻塞UI
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,11 +36,13 @@ impl<'a> DerefMut for ConnectionHandle<'a> {
|
|||
|
||||
/// 数据库管理器
|
||||
/// 遵循 Tauri 开发规范的数据库设计模式
|
||||
/// 支持连接池以提高并发性能
|
||||
/// 支持连接池以提高并发性能,实现读写分离
|
||||
pub struct Database {
|
||||
// 保留原有的单连接模式以兼容现有代码
|
||||
// 主连接(用于写操作和复杂事务)
|
||||
connection: Arc<Mutex<Connection>>,
|
||||
// 新增连接池支持
|
||||
// 只读连接(专门用于读操作,避免锁竞争)
|
||||
read_connection: Arc<Mutex<Connection>>,
|
||||
// 连接池支持
|
||||
pool: Option<Arc<ConnectionPool>>,
|
||||
}
|
||||
|
||||
|
|
@ -183,8 +185,23 @@ impl Database {
|
|||
None
|
||||
};
|
||||
|
||||
// 创建专用的只读连接
|
||||
let read_connection = Connection::open(&db_path)?;
|
||||
read_connection.pragma_update(None, "secure_delete", "ON")?;
|
||||
read_connection.pragma_update(None, "temp_store", "MEMORY")?;
|
||||
read_connection.pragma_update(None, "foreign_keys", "ON")?;
|
||||
read_connection.pragma_update(None, "journal_mode", "WAL")?;
|
||||
read_connection.pragma_update(None, "synchronous", "NORMAL")?;
|
||||
read_connection.pragma_update(None, "cache_size", "10000")?;
|
||||
read_connection.pragma_update(None, "busy_timeout", "5000")?;
|
||||
// 只读连接设置为只读模式
|
||||
read_connection.pragma_update(None, "query_only", "ON")?;
|
||||
|
||||
println!("只读数据库连接创建成功");
|
||||
|
||||
let database = Database {
|
||||
connection: Arc::new(Mutex::new(connection)),
|
||||
read_connection: Arc::new(Mutex::new(read_connection)),
|
||||
pool,
|
||||
};
|
||||
|
||||
|
|
@ -197,11 +214,17 @@ impl Database {
|
|||
Ok(database)
|
||||
}
|
||||
|
||||
/// 获取数据库连接
|
||||
/// 获取数据库连接(用于写操作)
|
||||
pub fn get_connection(&self) -> Arc<Mutex<Connection>> {
|
||||
Arc::clone(&self.connection)
|
||||
}
|
||||
|
||||
/// 获取只读数据库连接(用于读操作)
|
||||
/// 这个连接专门用于查询操作,避免与写操作竞争锁
|
||||
pub fn get_read_connection(&self) -> Arc<Mutex<Connection>> {
|
||||
Arc::clone(&self.read_connection)
|
||||
}
|
||||
|
||||
/// 检查数据库连接状态
|
||||
pub fn check_connection_status(&self) -> String {
|
||||
match self.connection.try_lock() {
|
||||
|
|
@ -211,7 +234,7 @@ impl Database {
|
|||
}
|
||||
}
|
||||
|
||||
/// 尝试非阻塞获取数据库连接
|
||||
/// 尝试非阻塞获取数据库连接(写连接)
|
||||
/// 如果连接被占用,立即返回 None
|
||||
pub fn try_get_connection(&self) -> Option<std::sync::MutexGuard<Connection>> {
|
||||
match self.connection.try_lock() {
|
||||
|
|
@ -220,6 +243,55 @@ impl Database {
|
|||
}
|
||||
}
|
||||
|
||||
/// 尝试非阻塞获取只读数据库连接
|
||||
/// 如果连接被占用,立即返回 None
|
||||
pub fn try_get_read_connection(&self) -> Option<std::sync::MutexGuard<Connection>> {
|
||||
match self.read_connection.try_lock() {
|
||||
Ok(conn) => Some(conn),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取最佳的只读连接
|
||||
/// 优先使用专用只读连接,如果不可用则尝试主连接
|
||||
pub fn get_best_read_connection(&self) -> Result<ConnectionHandle> {
|
||||
// 首先尝试专用只读连接
|
||||
match self.read_connection.try_lock() {
|
||||
Ok(conn) => {
|
||||
println!("使用专用只读连接");
|
||||
return Ok(ConnectionHandle::Single(conn));
|
||||
},
|
||||
Err(_) => {
|
||||
println!("只读连接被占用,尝试连接池");
|
||||
}
|
||||
}
|
||||
|
||||
// 如果只读连接被占用,尝试连接池
|
||||
if let Some(pool) = &self.pool {
|
||||
match pool.try_acquire()? {
|
||||
Some(conn) => {
|
||||
println!("使用连接池连接进行读操作");
|
||||
return Ok(ConnectionHandle::Pooled(conn));
|
||||
},
|
||||
None => {
|
||||
println!("连接池无可用连接,尝试主连接");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 最后尝试主连接(非阻塞)
|
||||
match self.connection.try_lock() {
|
||||
Ok(conn) => {
|
||||
println!("使用主连接进行读操作");
|
||||
Ok(ConnectionHandle::Single(conn))
|
||||
},
|
||||
Err(_) => {
|
||||
println!("所有连接都被占用,读操作失败");
|
||||
Err(anyhow!("所有数据库连接都被占用"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 检查是否启用了连接池
|
||||
pub fn has_pool(&self) -> bool {
|
||||
self.pool.is_some()
|
||||
|
|
|
|||
Loading…
Reference in New Issue