暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Odoo优化案例二

Odoo哥 2021-11-17
495

有次在生产系统中,PG日志输出一条超过3秒的简单查询SQL,经分析这个SQL生成的过程,觉得有点参考意义,现提供给大家参考。

先介绍一下这个SQL用到的两个数据模型定义:

model A:

    name:姓名字段(char)

    mobile:电话号码字段(char)

    state:状态字段(selection)

    reports:报告附件(one2many,关联model B)


model B:

    parent_id:主表ID(many2one,关联model A)

    name:报告名称(char)

    ......


在其中一个业务方法(定义在model A)中,用到了如下的一条查询:

self.search([("mobile","=""1380013800"),
              ("name","=","张三"),
("id","!="self.id),
              ("state","!=","cancel"),
              ("reports","!=",False)])

这个查询的作用,是检测姓名+电话相同的客户,之前是否已经有记录,并且有对应的报告附件。

然后在PG日志中,我看根据ORM生成的SQL语法是这样的:

SELECT "model_A".id 
FROM "model_A"
WHERE (
(
((
(("model_A"."active" = true)
AND ("model_A"."mobile" = '1380013800'))
      AND  ("model_A"."name" = '张三'))  
AND "model_A".id != 432814)
AND (("model_A"."state" not in ('cancel','reuse'))
OR "model_A"."state" IS NULL))
    AND  ("model_A"."id" in (/*许许多多的id*/))
)
ORDER BY "model_A"."id" DESC

这个单表查询,用到了id的索引,本来应该是比较快的,但因为莫名的多了一个id in (大量id)的条件,造成速度变慢。一般走索引的话,返回少量的记录才会提升性能,如果通过索引返回大量数据,则性能反而会变慢。

这里我们就要考虑这么多的id是哪里来的?另外domain中有一个('reports','!=',False),并没有在SQL中反应出来,所以我们继续在odoo中追踪生成的SQL语句,发现这个search其实是生成了两条SQL语句,第一条是:

select distinct(parent_idfrom model_B where parent_id != 432814

这条SQL是从model B中查询出所有的已经有附件的主表id,然后odoo把从这里取到的id放到上一条in条件中。所以这里in条件里的id都是已经有报告附件的记录,就可以对应到domain中的('reports','!=',False)条件。

因为我们model B中有十多万笔记录,这个distinct(parent_id)的结果就会比较多,造成上面的SQL在in条件中有太多的id值。查询速度显著降低。

现在我们不能改ORM底层生成SQL的逻辑,所以就把上面search的语法改为:

self.search([("mobile","=", "1380013800"),
("name","=","张三"),
("id","!=", self.id),
              ("state","!=","cancel")]).filtered(lambda l: l.reports)

这里search的domain中,去掉reports条件,根据其它条件获取记录以后,再通过ORM的filtered方法,设置过滤条件,只过滤reports有值的记录。

因为根据前面的条件,一般也只会查询到一两笔记录,在这一两笔记录中再通过filtered去查询model B就会很快了。

通过这个案例,我们需要建议一条odoo开发的军规,严禁在domain中使用one2many或many2many的条件

文章转载自Odoo哥,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论