ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
[TOC] >[success] # 常见错误N+1 问题 ~~~ 就是往往不注意循环中的orm 处理,导致出现N+1的问题,进而导致系统变慢, 然后拖垮整个系统。 ~~~ >[success] # 常见的几种N+1 >[danger] ##### 没有合理使用derfer ~~~ 1.derfer 是查询除了什么字段以外的字段 2.下面是在单元测试写的一个小案例,我们主要关心这段代码 categories = Categroy.objects.defer('name') for i in categories: print(i.name) ~~~ ~~~ @override_settings(DEBUG=True) def set_filter(self): categories = Categroy.objects.exists() print(categories) categories = Categroy.objects.defer('name') for i in categories: print(i.name) print(categories) # categories = categories.filter(status=1) pp(connection.queries) ~~~ * 实际产生的sql ~~~ 1.第一次查询,实在for i in categories 打印出来的,不是在创建的时候因为懒加载机制 ---'SELECT "blong_categroy"."id", "blong_categroy"."status", ' '"blong_categroy"."is_nav", "blong_categroy"."owner_id", ' '"blong_categroy"."created_time" FROM "blong_categroy"', 'time' 2.当我们i.name 由于derfer 是除了name 字段以外的字段查询,我们 却偏偏打印了这个字段就产生了下面N的情况 --'SELECT "blong_categroy"."id", "blong_categroy"."name" FROM ' '"blong_categroy" WHERE "blong_categroy"."id" = 1 乘循环次数的N,其中为什么是N次,因为我们给查询每个id 的name,所以相当于执行了N次的where条件id=1 id=2 .....直到最后一个id ~~~ >[danger] ##### only ~~~ 1.only 是只查询这个字段 2.下面是在单元测试写的一个小案例,我们主要关心这段代码 def set_filter(self): categories = Categroy.objects.only('name') for i in categories: print(i.created_time) ~~~ ~~~ @override_settings(DEBUG=True) def set_filter(self): categories = Categroy.objects.only('name') for i in categories: print(i.created_time) print(categories) # categories = categories.filter(status=1) pp(connection.queries) ~~~ * 实际产生的sql ~~~ 1.第一次查询,实在for i in categories 打印出来的,不是在创建的时候因为懒加载机制 ---' 'SELECT "blong_categroy"."id", "blong_categroy"."name" FROM ' '"blong_categroy"' 2.当我们查询i.created_time 由于only是只查询什么字段, 导致由于id是唯一的所以,判断条件要where 每一个id查询结果 却偏偏打印了这个字段就产生了下面N的情况 --'SSELECT "blong_categroy"."id", "blong_categroy"."created_time" FROM ' '"blong_categroy" WHERE "blong_categroy"."id" = 1', 乘循环次数的N,其中为什么是N次,因为我们给查询每个id created_time,所以相当于执行了N次的where条件id=1 id=2 .....直到最后一个id ~~~ >[danger] ##### 关联对象 ~~~ 1.当我们两个表做了关联的时候也就是ForeignKey的时候,我们可能最容易忽略 这个n+1的问题,两个表的关联查询 在sql语句上我们通常使用jion on 如果不使 用,我们看看执行情况,这是表的创建展示 class Categroy(models.Model): STATUS_ITMES = ( (1, "正常"), (2, "删除"), ) name = models.CharField(max_length=50, verbose_name="名称") status = models.PositiveIntegerField(default=1, choices=STATUS_ITMES, verbose_name="状态") is_nav = models.BooleanField(default=False, verbose_name="是否为导航") owner = models.ForeignKey(User, verbose_name="作者") created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") class Meta: verbose_name = verbose_name_plural = '分类' ~~~ * 问题我们想去查关联表,我们来看看 常见的N+1 错误 ~~~ @override_settings(DEBUG=True) def set_filter(self): categories = Categroy.objects.all() for i in categories: print(i.owner) # categories = categories.filter(status=1) pp(connection.queries) ~~~ ~~~ 1.上面的代码有一个很严重的N+1问题 2.其实每一打印的owner 对象实际是又重新生成了sql 语句,如下展示 --- SELECT "blong_categroy"."id", "blong_categroy"."name", ' '"blong_categroy"."status", "blong_categroy"."is_nav", ' '"blong_categroy"."owner_id", "blong_categroy"."created_time" FROM ' '"blong_categroy"', 'time' 3.打印查询了N边的,其中N 是where 条件中的id ----'SELECT "auth_user"."id", "auth_user"."password", ' '"auth_user"."last_login", "auth_user"."is_superuser", ' '"auth_user"."username", "auth_user"."first_name", ' '"auth_user"."last_name", "auth_user"."email", ' '"auth_user"."is_staff", "auth_user"."is_active", ' '"auth_user"."date_joined" FROM "auth_user" WHERE "auth_user"."id" = ' '1', ~~~ * 怎么解决这样问题--select_related() 来解决这个问题 ~~~ @override_settings(DEBUG=True) def set_filter(self): categories = Categroy.objects.select_related().filter(status=1) for i in categories: print(i.owner) # categories = categories.filter(status=1) pp(connection.queries) ~~~ * 执行的sql 语句用jion on 吧ower 对象查询,这样我们就可以直接操对象而不是他的本身 ~~~ 'SELECT "blong_categroy"."id", "blong_categroy"."name", ' '"blong_categroy"."status", "blong_categroy"."is_nav", ' '"blong_categroy"."owner_id", "blong_categroy"."created_time", ' '"auth_user"."id", "auth_user"."password", "auth_user"."last_login", ' '"auth_user"."is_superuser", "auth_user"."username", ' '"auth_user"."first_name", "auth_user"."last_name", ' '"auth_user"."email", "auth_user"."is_staff", ' '"auth_user"."is_active", "auth_user"."date_joined" FROM ' '"blong_categroy" INNER JOIN "auth_user" ON ' '("blong_categroy"."owner_id" = "auth_user"."id") WHERE ' '"blong_categroy"."status" = 1', ~~~