Django中prefetch_related()函数优化实战指南

对于多对多字段(ManyToManyField)和一对多字段, 可以使用prefetch_related()来进行优化

prefetch_related()和select_related()的设计目的很相似,都是为了减少SQL查询的数量,但是实现的方式不一样。后者是通过JOIN语句,在SQL查询内解决问题。但是对于多对多关系,使用SQL语句解决就显得有些不太明智,因为JOIN得到的表将会很长,会导致SQL语句运行时间的增加和内存占用的增加。若有n个对象,每个对象的多对多字段对应Mi条,就会生成Σ(n)Mi 行的结果表。prefetch_related()的解决方法是,分别查询每个表,然后用Python处理他们之间的关系。继续以上边的例子进行说明,如果我们要获得张三所有去过的城市,使用prefetch_related()应该是这么做:

1

2

3

zhangs = Person.objects.prefetch_related('visitation').get(firstname=u"张",lastname=u"三")

>>> for city in zhangs.visitation.all() :

...   print city

上述代码触发的SQL查询如下:

1

2

3

4

5

6

7

8

9

10

SELECT `QSOptimize_person`.`id`, `QSOptimize_person`.`firstname`,

`QSOptimize_person`.`lastname`, `QSOptimize_person`.`hometown_id`, `QSOptimize_person`.`living_id`

FROM `QSOptimize_person`

WHERE (`QSOptimize_person`.`lastname` = '三'  AND `QSOptimize_person`.`firstname` = '张');

   

SELECT (`QSOptimize_person_visitation`.`person_id`) AS `_prefetch_related_val`, `QSOptimize_city`.`id`,

`QSOptimize_city`.`name`, `QSOptimize_city`.`province_id`

FROM `QSOptimize_city`

INNER JOIN `QSOptimize_person_visitation` ON (`QSOptimize_city`.`id` = `QSOptimize_person_visitation`.`city_id`)

WHERE `QSOptimize_person_visitation`.`person_id` IN (1);

第一条SQL查询仅仅是获取张三的Person对象,第二条比较关键,它选取关系表`QSOptimize_person_visitation`中`person_id`为张三的行,然后和`city`表内联(INNER JOIN 也叫等值连接)得到结果表。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

+----+-----------+----------+-------------+-----------+

| id | firstname | lastname | hometown_id | living_id |

+----+-----------+----------+-------------+-----------+

| 1 | 张 | 三 | 3 | 1 |

+----+-----------+----------+-------------+-----------+

1 row in set (0.00 sec)

  

+-----------------------+----+-----------+-------------+

| _prefetch_related_val | id | name | province_id |

+-----------------------+----+-----------+-------------+

| 1 | 1 | 武汉市 | 1 |

| 1 | 2 | 广州市 | 2 |

| 1 | 3 | 十堰市 | 1 |

+-----------------------+----+-----------+-------------+

3 rows in set (0.00 sec)

显然张三武汉、广州、十堰都去过。

又或者,我们要获得湖北的所有城市名,可以这样:

1

2

3

4

>>> hb = Province.objects.prefetch_related('city_set').get(name__iexact=u"湖北省")

>>> for city in hb.city_set.all():

... city.name

...

触发的SQL查询:

1

2

3

4

5

6

7

SELECT `QSOptimize_province`.`id`, `QSOptimize_province`.`name`

FROM `QSOptimize_province`

WHERE `QSOptimize_province`.`name` LIKE '湖北省' ;

  

SELECT `QSOptimize_city`.`id`, `QSOptimize_city`.`name`, `QSOptimize_city`.`province_id`

FROM `QSOptimize_city`

WHERE `QSOptimize_city`.`province_id` IN (1);

得到的表:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

+----+-----------+

| id | name      |

+----+-----------+

|  1 | 湖北省    |

+----+-----------+

1 row in set (0.00 sec)

  

+----+-----------+-------------+

| id | name      | province_id |

+----+-----------+-------------+

|  1 | 武汉市    |           1 |

|  3 | 十堰市    |           1 |

+----+-----------+-------------+

2 rows in set (0.00 sec)

我们可以看见,prefetch使用的是 IN 语句实现的。这样,在QuerySet中的对象数量过多的时候,根据数据库特性的不同有可能造成性能问题。

使用方法

*lookups 参数

prefetch_related()在Django < 1.7 只有这一种用法。和select_related()一样,prefetch_related()也支持深度查询,例如要获得所有姓张的人去过的省:

1

2

3

4

5

>>> zhangs = Person.objects.prefetch_related('visitation__province').filter(firstname__iexact=

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jh035512

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值