Mongoid 6中belongs_to关联查询为何携带sort与limit参数?
为什么Mongoid 6里
user.project和Project.find(user.project_id)的查询日志差这么多? 嘿,这个问题我之前在项目里也碰到过,刚好能给你掰扯清楚背后的门道:
1. 关联加载自带的安全兜底操作
当你调用user.project时,Mongoid走的是belongs_to关联加载的逻辑。虽说咱们业务上一个用户肯定只对应一个项目,但MongoDB是无Schema的,没法像关系型数据库那样强制外键唯一(除非你手动加唯一索引)。为了防止哪天数据库里真出现多个匹配project_id的项目文档,Mongoid默认给这个查询加了俩保险:
limit: 1:不管匹配多少条,只返回第一个sort: {_id: 1}:万一真有多条,保证每次返回的都是_id最小的那个,结果一致不翻车
至于singleBatch,那是Ruby的MongoDB驱动看到limit:1后自动加上的,意思是让MongoDB一次性返回结果,别分批折腾。
2. 直接主键查询是极简模式
而Project.find(user.project_id)就简单多了——这是Mongoid专门针对主键的查询方法。MongoDB的_id天生是唯一主键,Mongoid心里门儿清:查这个肯定只会返回一条,完全没必要画蛇添足加sort、limit这些参数,直接走最精简的主键查找就行。
3. 为啥前者更慢还搞坏缓存?
- 性能慢一点的原因:虽说都是查主键,但关联加载除了数据库查询,还会触发Mongoid的关联钩子、上下文校验这些额外逻辑,多少会多一点开销。而且带sort和limit的查询,MongoDB的查询计划虽说不会差太多,但极端场景下还是能感觉到差异。
- 缓存被破坏的根源:Mongoid的查询缓存是靠查询语句的哈希值来识别的。
user.project生成的查询带了一堆额外参数,和Project.find(...)的查询语句哈希完全不一样,所以Mongoid会把它们当成两个完全不同的查询,缓存没法共享。哪怕结果一模一样,也得各自存一份,甚至重复查,缓存利用率直接就下来了。
要是你想让关联加载的查询和直接find保持一致,也可以给belongs_to关联加个自定义查询器:
class User belongs_to :project, -> { order(nil).limit(nil) } end
不过得提醒你,这么做的前提是你能100%保证数据库里project_id对应的项目是唯一的,不然哪天冒出来多个项目,这个查询就会返回所有匹配的,搞不好业务逻辑就崩了。
内容的提问来源于stack exchange,提问作者rjurado01
相关产品推荐
相关产品推荐

