对象关系映射 (ORM) 使得与SQL数据库交互更为简单,不过也被认为效率不高,比原始的SQL要慢。
要有效的使用ORM,意味着需要多少要明白它是如何查询数据库的。本文我将重点介绍如何有效使用 Django ORM系统访问中到大型的数据集。
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)
当你遍历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)
queryset的cache最有用的地方是可以有效的测试queryset是否包含数据,只有有数据时才会去遍历:
restaurant_set = Restaurant.objects.filter(cuisine="Indian") # `if`语句会触发queryset的执行。 if restaurant_set: # 遍历时用的是cache中的数据 for restaurant in restaurant_set: print(restaurant.name)
有时候,你也许只想知道是否有数据存在,而不需要遍历所有的数据。这种情况,简单的使用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!")
处理成千上万的记录时,将它们一次装入内存是很浪费的。更糟糕的是,巨大的queryset可能会锁住系统进程,让你的程序濒临崩溃。
要避免在遍历数据的同时产生queryset cache,可以使用iterator()方法来获取数据,处理完数据就将其丢弃。
star_set = Star.objects.all() # `iterator()`可以一次只从数据库获取少量数据,这样可以节省内存 for star in star_set.iterator(): print(star.name)当然,使用iterator()方法来防止生成cache,意味着遍历同一个queryset时会重复执行查询。所以使用iterator()的时候要当心,确保你的代码在操作一个大的queryset时没有重复执行查询。
如前所述,查询集缓存对于组合 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)
评论删除后,数据将无法恢复
评论(15)
引用来自“鲲鹏Web数据采集”的评论
引用来自“鲲鹏Web数据采集”的评论
- for atom in chain([first_atom], atom_set)
我觉得这句写的有问题,应该是 for atom in chain([first_atom], atom_iterator)才对吧?
请译者做相应修改。原文已改为:for atom in chain([first_atom], atom_iterator):
可惜这里的翻译不太好改。
引用来自“鲲鹏Web数据采集”的评论
- for atom in chain([first_atom], atom_set)
我觉得这句写的有问题,应该是 for atom in chain([first_atom], atom_iterator)才对吧?
请译者做相应修改。原文已改为:for atom in chain([first_atom], atom_iterator):
我觉得这句写的有问题,应该是 for atom in chain([first_atom], atom_iterator)才对吧?
引用来自“renwofei423”的评论
这里的折腾 和 方案 是一个意思 @HankZhou
引用来自“renwofei423”的评论
引用来自“HankZhou”的评论
引用来自“renwofei423”的评论
引用来自“炸酱面”的评论
引用来自“HankZhou”的评论
引用来自“renwofei423”的评论
引用来自“HankZhou”的评论
如果是小型网站的话,大家推荐使用文本作为数据库还是真正的数据库。
个人觉得即使是小型网站,除非你觉得永远都不可能翻身,还是用数据库比较好。
即使是小网站,也可以使用sqlite,迁移也也很方便啊!
如果追求性能,先要找出整个应用的性能瓶颈在哪里,再去优化和追求速度,才有意义。
流量监控、显示?
你这种属于用户数量、负载几乎为个位数,怎么折腾都无所谓。。。
引用来自“HankZhou”的评论
引用来自“renwofei423”的评论
引用来自“炸酱面”的评论
引用来自“HankZhou”的评论
引用来自“renwofei423”的评论
引用来自“HankZhou”的评论
如果是小型网站的话,大家推荐使用文本作为数据库还是真正的数据库。
个人觉得即使是小型网站,除非你觉得永远都不可能翻身,还是用数据库比较好。
即使是小网站,也可以使用sqlite,迁移也也很方便啊!
如果追求性能,先要找出整个应用的性能瓶颈在哪里,再去优化和追求速度,才有意义。
流量监控、显示?
你这种属于用户数量、负载几乎为个位数,怎么折腾都无所谓。。。
引用来自“renwofei423”的评论
引用来自“炸酱面”的评论
引用来自“HankZhou”的评论
引用来自“renwofei423”的评论
引用来自“HankZhou”的评论
如果是小型网站的话,大家推荐使用文本作为数据库还是真正的数据库。
个人觉得即使是小型网站,除非你觉得永远都不可能翻身,还是用数据库比较好。
即使是小网站,也可以使用sqlite,迁移也也很方便啊!
如果追求性能,先要找出整个应用的性能瓶颈在哪里,再去优化和追求速度,才有意义。
引用来自“炸酱面”的评论
引用来自“HankZhou”的评论
引用来自“renwofei423”的评论
引用来自“HankZhou”的评论
如果是小型网站的话,大家推荐使用文本作为数据库还是真正的数据库。
个人觉得即使是小型网站,除非你觉得永远都不可能翻身,还是用数据库比较好。
即使是小网站,也可以使用sqlite,迁移也也很方便啊!
如果追求性能,先要找出整个应用的性能瓶颈在哪里,再去优化和追求速度,才有意义。
引用来自“HankZhou”的评论
引用来自“renwofei423”的评论
引用来自“HankZhou”的评论
如果是小型网站的话,大家推荐使用文本作为数据库还是真正的数据库。
个人觉得即使是小型网站,除非你觉得永远都不可能翻身,还是用数据库比较好。
即使是小网站,也可以使用sqlite,迁移也也很方便啊!