有效使用Django的QuerySets 已翻译 100%

renwofei423 投递于 2013/06/17 09:38 (共 7 段, 翻译完成于 06-19)
阅读 11019
收藏 48
2
加载中

对象关系映射 (ORM) 使得与SQL数据库交互更为简单,不过也被认为效率不高,比原始的SQL要慢。

要有效的使用ORM,意味着需要多少要明白它是如何查询数据库的。本文我将重点介绍如何有效使用 Django ORM系统访问中到大型的数据集。

zicode
翻译于 2013/06/18 23:29
3

Django的queryset是惰性的

Django的queryset对应于数据库的若干记录(row),通过可选的查询来过滤。例如,下面的代码会得到数据库中名字为‘Dave’的所有的人:

person_set = Person.objects.filter(first_name="Dave")

上面的代码并没有运行任何的数据库查询。你可以使用person_set,给它加上一些过滤条件,或者将它传给某个函数,这些操作都不会发送给数据库。这是对的,因为数据库查询是显著影响web应用性能的因素之一。

要真正从数据库获得数据,你需要遍历queryset:

for person in person_set:
    print(person.last_name)
zicode
翻译于 2013/06/19 09:48
1

Django的queryset是具有cache的

当你遍历queryset时,所有匹配的记录会从数据库获取,然后转换成Django的model。这被称为执行(evaluation)。这些model会保存在queryset内置的cache中,这样如果你再次遍历这个queryset,你不需要重复运行通用的查询。

例如,下面的代码只会执行一次数据库查询:

pet_set = Pet.objects.filter(species="Dog")
# The query is executed and cached.
for pet in pet_set:
    print(pet.first_name)
# The cache is used for subsequent iteration.
for pet in pet_set:
    print(pet.last_name)
zicode
翻译于 2013/06/19 10:01
1

if语句会触发queryset的执行

queryset的cache最有用的地方是可以有效的测试queryset是否包含数据,只有有数据时才会去遍历:

restaurant_set = Restaurant.objects.filter(cuisine="Indian")
# `if`语句会触发queryset的执行。
if restaurant_set:
    # 遍历时用的是cache中的数据
    for restaurant in restaurant_set:
        print(restaurant.name)

如果不需要所有数据,queryset的cache可能会是个问题

有时候,你也许只想知道是否有数据存在,而不需要遍历所有的数据。这种情况,简单的使用if语句进行判断也会完全执行整个queryset并且把数据放入cache,虽然你并不需要这些数据!

city_set = City.objects.filter(name="Cambridge")
# `if`语句会执行queryset.。
if city_set:
    # 我们并不需要所有的数据,但是ORM仍然会获取所有记录!
    print("At least one city called Cambridge still stands!")
为了避免这个,可以用exists()方法来检查是否有数据:
tree_set = Tree.objects.filter(type="deciduous")
# `exists()`的检查可以避免数据放入queryset的cache。
if tree_set.exists():
    # 没有数据从数据库获取,从而节省了带宽和内存
    print("There are still hardwood trees in the world!")
zicode
翻译于 2013/06/19 10:10
1

当queryset非常巨大时,cache会成为问题

处理成千上万的记录时,将它们一次装入内存是很浪费的。更糟糕的是,巨大的queryset可能会锁住系统进程,让你的程序濒临崩溃。

要避免在遍历数据的同时产生queryset cache,可以使用iterator()方法来获取数据,处理完数据就将其丢弃。

star_set = Star.objects.all()
# `iterator()`可以一次只从数据库获取少量数据,这样可以节省内存
for star in star_set.iterator():
    print(star.name)
当然,使用iterator()方法来防止生成cache,意味着遍历同一个queryset时会重复执行查询。所以使用iterator()的时候要当心,确保你的代码在操作一个大的queryset时没有重复执行查询。
zicode
翻译于 2013/06/19 14:42
1

如果查询集很大的话,if 语句是个问题

如前所述,查询集缓存对于组合 if 语句和 for 语句是很强大的,它允许在一个查询集上进行有条件的循环。然而对于很大的查询集,则不适合使用查询集缓存。

最简单的解决方案是结合使用exists()和iterator(), 通过使用两次数据库查询来避免使用查询集缓存。

molecule_set = Molecule.objects.all()
# One database query to test if any rows exist.
if molecule_set.exists():
    # Another database query to start fetching the rows in batches.
    for molecule in molecule_set.iterator():
        print(molecule.velocity)
一个更复杂点的方案是使用 Python 的“ 高级迭代方法 ”在开始循环前先查看一下 iterator() 的第一个元素再决定是否进行循环。
atom_set = Atom.objects.all()
# One database query to start fetching the rows in batches.
atom_iterator = atom_set.iterator()
# Peek at the first item in the iterator.
try:
    first_atom = next(atom_iterator)
except StopIteration:
    # No rows were found, so do nothing.
    pass
else:
    # At least one row was found, so iterate over
    # all the rows, including the first one.
    from itertools import chain
    for atom in chain([first_atom], atom_set):
        print(atom.mass)
S
翻译于 2013/06/19 14:01
1

防止不当的优化

queryset的cache是用于减少程序对数据库的查询,在通常的使用下会保证只有在需要的时候才会查询数据库。

使用exists()和iterator()方法可以优化程序对内存的使用。不过,由于它们并不会生成queryset cache,可能会造成额外的数据库查询。

所以编码时需要注意一下,如果程序开始变慢,你需要看看代码的瓶颈在哪里,是否会有一些小的优化可以帮到你。

zicode
翻译于 2013/06/19 10:28
1
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
加载中

评论(15)

renwofei423
renwofei423

引用来自“鲲鹏Web数据采集”的评论

引用来自“鲲鹏Web数据采集”的评论

- for atom in chain([first_atom], atom_set)
我觉得这句写的有问题,应该是 for atom in chain([first_atom], atom_iterator)才对吧?

我给原文作者留言了。作者已更正:Good point. I'm changing that right now.
请译者做相应修改。原文已改为:for atom in chain([first_atom], atom_iterator):

o,太好了。
可惜这里的翻译不太好改。
西安鲲之鹏
西安鲲之鹏

引用来自“鲲鹏Web数据采集”的评论

- for atom in chain([first_atom], atom_set)
我觉得这句写的有问题,应该是 for atom in chain([first_atom], atom_iterator)才对吧?

我给原文作者留言了。作者已更正:Good point. I'm changing that right now.
请译者做相应修改。原文已改为:for atom in chain([first_atom], atom_iterator):
西安鲲之鹏
西安鲲之鹏
- for atom in chain([first_atom], atom_set)
我觉得这句写的有问题,应该是 for atom in chain([first_atom], atom_iterator)才对吧?

拜仁慕尼黑
拜仁慕尼黑

引用来自“renwofei423”的评论

这里的折腾 和 方案 是一个意思 @HankZhou

别解释了
renwofei423
renwofei423
这里的折腾 和 方案 是一个意思 @HankZhou
拜仁慕尼黑
拜仁慕尼黑

引用来自“renwofei423”的评论

引用来自“HankZhou”的评论

引用来自“renwofei423”的评论

引用来自“炸酱面”的评论

引用来自“HankZhou”的评论

引用来自“renwofei423”的评论

引用来自“HankZhou”的评论

如果是小型网站的话,大家推荐使用文本作为数据库还是真正的数据库。

文本?有必要吗?
个人觉得即使是小型网站,除非你觉得永远都不可能翻身,还是用数据库比较好。
即使是小网站,也可以使用sqlite,迁移也也很方便啊!

文本在数据处理速度会很有优势,怎么就不行?

我最近用django弄了一个小网站,代码很胡乱,没有任何优化,数据量也不大,可速度飞快了,所谓的优势是没有用的。另外小网站更要将就快速开发,使用django就是原因之一,如果使用文本,就要增加大量代码量(本来用django做一个小网站就要不了多少代码),就是舍本逐末了。

是啊。

如果追求性能,先要找出整个应用的性能瓶颈在哪里,再去优化和追求速度,才有意义。

防火墙web管理页面。认证采用htaccess,页面PHP,所有数据全部采用文本,调用shell处理文本和执行系统命令。我也不知道为啥偏得用数据库。

用户权限(管理员、非管理员)?
流量监控、显示?
你这种属于用户数量、负载几乎为个位数,怎么折腾都无所谓。。。

你居然把这种看作成为折腾!
renwofei423
renwofei423

引用来自“HankZhou”的评论

引用来自“renwofei423”的评论

引用来自“炸酱面”的评论

引用来自“HankZhou”的评论

引用来自“renwofei423”的评论

引用来自“HankZhou”的评论

如果是小型网站的话,大家推荐使用文本作为数据库还是真正的数据库。

文本?有必要吗?
个人觉得即使是小型网站,除非你觉得永远都不可能翻身,还是用数据库比较好。
即使是小网站,也可以使用sqlite,迁移也也很方便啊!

文本在数据处理速度会很有优势,怎么就不行?

我最近用django弄了一个小网站,代码很胡乱,没有任何优化,数据量也不大,可速度飞快了,所谓的优势是没有用的。另外小网站更要将就快速开发,使用django就是原因之一,如果使用文本,就要增加大量代码量(本来用django做一个小网站就要不了多少代码),就是舍本逐末了。

是啊。

如果追求性能,先要找出整个应用的性能瓶颈在哪里,再去优化和追求速度,才有意义。

防火墙web管理页面。认证采用htaccess,页面PHP,所有数据全部采用文本,调用shell处理文本和执行系统命令。我也不知道为啥偏得用数据库。

用户权限(管理员、非管理员)?
流量监控、显示?
你这种属于用户数量、负载几乎为个位数,怎么折腾都无所谓。。。
拜仁慕尼黑
拜仁慕尼黑

引用来自“renwofei423”的评论

引用来自“炸酱面”的评论

引用来自“HankZhou”的评论

引用来自“renwofei423”的评论

引用来自“HankZhou”的评论

如果是小型网站的话,大家推荐使用文本作为数据库还是真正的数据库。

文本?有必要吗?
个人觉得即使是小型网站,除非你觉得永远都不可能翻身,还是用数据库比较好。
即使是小网站,也可以使用sqlite,迁移也也很方便啊!

文本在数据处理速度会很有优势,怎么就不行?

我最近用django弄了一个小网站,代码很胡乱,没有任何优化,数据量也不大,可速度飞快了,所谓的优势是没有用的。另外小网站更要将就快速开发,使用django就是原因之一,如果使用文本,就要增加大量代码量(本来用django做一个小网站就要不了多少代码),就是舍本逐末了。

是啊。

如果追求性能,先要找出整个应用的性能瓶颈在哪里,再去优化和追求速度,才有意义。

防火墙web管理页面。认证采用htaccess,页面PHP,所有数据全部采用文本,调用shell处理文本和执行系统命令。我也不知道为啥偏得用数据库。
renwofei423
renwofei423

引用来自“炸酱面”的评论

引用来自“HankZhou”的评论

引用来自“renwofei423”的评论

引用来自“HankZhou”的评论

如果是小型网站的话,大家推荐使用文本作为数据库还是真正的数据库。

文本?有必要吗?
个人觉得即使是小型网站,除非你觉得永远都不可能翻身,还是用数据库比较好。
即使是小网站,也可以使用sqlite,迁移也也很方便啊!

文本在数据处理速度会很有优势,怎么就不行?

我最近用django弄了一个小网站,代码很胡乱,没有任何优化,数据量也不大,可速度飞快了,所谓的优势是没有用的。另外小网站更要将就快速开发,使用django就是原因之一,如果使用文本,就要增加大量代码量(本来用django做一个小网站就要不了多少代码),就是舍本逐末了。

是啊。

如果追求性能,先要找出整个应用的性能瓶颈在哪里,再去优化和追求速度,才有意义。

炸酱面
炸酱面

引用来自“HankZhou”的评论

引用来自“renwofei423”的评论

引用来自“HankZhou”的评论

如果是小型网站的话,大家推荐使用文本作为数据库还是真正的数据库。

文本?有必要吗?
个人觉得即使是小型网站,除非你觉得永远都不可能翻身,还是用数据库比较好。
即使是小网站,也可以使用sqlite,迁移也也很方便啊!

文本在数据处理速度会很有优势,怎么就不行?

我最近用django弄了一个小网站,代码很胡乱,没有任何优化,数据量也不大,可速度飞快了,所谓的优势是没有用的。另外小网站更要将就快速开发,使用django就是原因之一,如果使用文本,就要增加大量代码量(本来用django做一个小网站就要不了多少代码),就是舍本逐末了。
返回顶部
顶部