pub struct N1QueryDetector { /* private fields */ }Expand description
N+1 查询检测器
通过统计相同表/关系在循环加载场景下的查询次数,识别潜在的 N+1 查询问题。
§检测策略
- 次数阈值:同一
relation在一次“检测窗口“内被查询次数超过threshold(默认 5),即判定为 N+1 嫌疑; - 检测窗口:以
start_window()/end_window()显式划定窗口, 便于在循环外包裹; - 批量命中识别:如果一次
record_batch_load调用就加载了多个 key, 视为已通过批量加载规避 N+1,仅记 1 次批量查询; - 回调告警:触发 N+1 时调用可选的
on_alert回调(用于日志/指标上报)。
§设计动机
SeaORM 的 find_with_related 仅在显式调用时才会批量加载,缺乏运行时
检测机制。本检测器提供运行时 introspection,可在开发/测试环境启用,
在生产环境关闭(zero-cost 抽象)。
§线程安全
内部使用 RwLock<HashMap> 维护计数,可在线程间共享(Send + Sync)。
§示例
use sz_orm_query::entity_graph::{N1QueryDetector, N1DetectionConfig};
let detector = N1QueryDetector::new(N1DetectionConfig::default());
detector.start_window();
for _ in 0..10 {
detector.record_single_load("posts");
}
detector.end_window();
let alerts = detector.alerts();
assert_eq!(alerts.len(), 1);
assert_eq!(alerts[0].relation, "posts");
assert_eq!(alerts[0].query_count, 10);Implementations§
Source§impl N1QueryDetector
impl N1QueryDetector
Sourcepub fn new(config: N1DetectionConfig) -> N1QueryDetector
pub fn new(config: N1DetectionConfig) -> N1QueryDetector
创建检测器
Sourcepub fn with_defaults() -> N1QueryDetector
pub fn with_defaults() -> N1QueryDetector
创建默认配置的检测器
Sourcepub fn is_enabled(&self) -> bool
pub fn is_enabled(&self) -> bool
是否启用检测
Sourcepub fn start_window(&self)
pub fn start_window(&self)
开启检测窗口(清空旧计数与旧告警)
重复调用 start_window 会重置窗口。
Sourcepub fn end_window(&self) -> Vec<N1Alert>
pub fn end_window(&self) -> Vec<N1Alert>
结束检测窗口,分析并生成告警
结束后 record_* 调用会被忽略,直到下一次 start_window。
返回本次窗口产生的告警列表。
Sourcepub fn record_single_load(&self, relation: &str)
pub fn record_single_load(&self, relation: &str)
记录一次单条加载(典型的 N+1 来源:循环内 find_by_id)
Sourcepub fn record_batch_load(&self, relation: &str, _keys_count: usize)
pub fn record_batch_load(&self, relation: &str, _keys_count: usize)
记录一次批量加载(已规避 N+1 的良好实践)
keys_count:本次批量加载的 key 数量- 一个 batch 仅记 1 次批量查询,不论 keys_count 多少
Sourcepub fn current_count(&self, relation: &str) -> u64
pub fn current_count(&self, relation: &str) -> u64
当前窗口内某 relation 的单条查询次数
Sourcepub fn current_batch_count(&self, relation: &str) -> u64
pub fn current_batch_count(&self, relation: &str) -> u64
当前窗口内某 relation 的批量查询次数
Sourcepub fn is_window_active(&self) -> bool
pub fn is_window_active(&self) -> bool
窗口是否处于开启状态
Sourcepub fn has_n_plus_one(&self) -> bool
pub fn has_n_plus_one(&self) -> bool
是否已检测到 N+1(基于最近一次窗口的告警)
Trait Implementations§
Source§impl Default for N1QueryDetector
impl Default for N1QueryDetector
Source§fn default() -> N1QueryDetector
fn default() -> N1QueryDetector
Returns the “default value” for a type. Read more
Auto Trait Implementations§
impl !Freeze for N1QueryDetector
impl RefUnwindSafe for N1QueryDetector
impl Send for N1QueryDetector
impl Sync for N1QueryDetector
impl Unpin for N1QueryDetector
impl UnsafeUnpin for N1QueryDetector
impl UnwindSafe for N1QueryDetector
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
Converts
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
Converts
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more