ORM进阶指南

ORM全称是Object Relational Mapping(对象关系映射),它在编程中把面向对象的概念跟数据库中表的概念对应起来,定义一个对象,就对应着一张表,这个对象的实例,就对应着表中的一条记录。ORM封装了底层的数据库操作,降低了编程难度,提高了代码的可读性和易维护性。

虽然ORM可以带来极大的便利,但使用不当会带来运行效率的问题。下面以Django ORM为例,给出一些性能优化要点。

查询集(QuerySet)缓存

Django的查询集是惰性求值的,重复调用时可能会重复计算,利用缓存可以避免不必要的重复计算。

print([p.name for p in Person.objects.all()])  # 查询集计算并缓存
print([p.name for p in Person.objects.all()])  # 查询集再次计算并缓存

queryset = Person.objects.all()
print([p.name for p in queryset])  # 查询集计算并缓存
print([p.name for p in queryset])  # 使用缓存的值

queryset = Person.objects.all()
print(queryset[0])  # 查询数据库
print(queryset[0])  # 再次查询数据库

queryset = Person.objects.all()
list(queryset)  # 查询集计算并缓存
print(queryset[0])  # 使用缓存的值
print(queryset[0])  # 使用缓存的值

外键关系对象和逆关系对象的提前查询和缓存

Django计算查询集时不包括外键关系和逆关系,所以不会包含在缓存中。

person = Person.objects.get(id=1)
person.father  # 获得外键对象并缓存
person.father  # 使用缓存的值

person = Person.objects.get(id=1)
person.children.all()  # 数据库查询
person.children.all()  # 数据库再次查询

使用select_related()prefetch_related()指定需要的对象,Django会提前查询并缓存。

# 忌
queryset = Person.objects.all()
for person in queryset:
    person.father  # 每个迭代的外键对象都要在数据库查询

# 宜
queryset = Person.objects.all().select_related('father')  # 指定外键对象并缓存
for person in queryset:
    person.father  # 使用缓存的值

避免在循环中进行数据库查询

这是非常低效的做法,应该在循环之前先进行数据库查询。

# 忌
filtered = Person.objects.filter(first_name='Shallan', last_name='Davar')
for age in range(18):
    person = filtered.get(age=age)  # 每个迭代都要在数据库查询

# 宜
filtered = Person.objects.filter(
    first_name='Shallan',
    last_name='Davar',
    age_gte=0,
    age_lte=18,
)
lookup = {person.age: person for person in filtered}
for age in range(18):
    person = lookup[age]  # 没有数据库查询

iterator()遍历减少缓存

查询集非常大而且只要遍历一次可以用这种方法。

# 不会缓存
for person in Person.objects.iterator():
    # 一些操作

尽量使用数据库操作提高效率

# 忌
for person in Person.objects.all():
    if person.age >= 18:
        # 一些操作

# 宜
for person in Person.objects.filter(age__gte=18):
    # 一些操作

使用F()直接操作模型字段的值

这种方法不需要缓存。

# 忌
for person in Person.objects.all():
    person.age += 1
    person.save()

# 宜
from django.db.models import F
Person.objects.update(age=F('age') + 1)

使用数据库聚合

# 忌
max_age = 0
for person in Person.objects.all():
    if person.age > max_age:
        max_age = person.age
# 宜
max_age = Person.objects.all().aggregate(Max('age'))['age__max']

使用values()values_list()获取只包含特定字段的列表、字典或元组

# 忌
age_lookup = {
    person.name: person.age
    for person in Person.objects.all()
}

# 宜
age_lookup = {
    person['name']: person['age']
    for person in Person.objects.values('name', 'age')
}
# 忌
person_ids = [person.id for person in Person.objects.all()]

# 宜
person_ids = Person.objects.values_list('id', flat=True)

使用defer()only()获取只包含特定字段的查询集

queryset = Person.objects.defer('age')  # 假设age计算开销大
for person in queryset:
    print(person.id)
    print(person.name)
queryset = Person.objects.only('name')
for person in queryset:
    print(person.name)

当不需要查询集的内容时使用count()exists()

# 忌
count = len(Person.objects.all())

# 宜
count = Person.objects.count()
# 忌
count = len(Person.objects.all())

# 宜
count = Person.objects.count()

使用delete()update()bulk_create()进行批量操作

# 忌
for person in Person.objects.all():
    person.delete()

# 宜
Person.objects.all().delete()
# 忌
for person in Person.objects.all():
    person.age = 0
    person.save()

# 宜
Person.objects.update(age=0)
names = ['Jeff', 'Beth', 'Tim']
creates = []
for name in names:
    creates.append(
        Person(name=name, age=0)
    )
Person.objects.bulk_create(creates)

直接使用外键

Django ORM 自动获取和缓存外键,不需要额外不必要的查询。

# 忌
father_id = Person.objects.get(id=1).father.id

# 宜
father_id = Person.objects.get(id=1).father_id
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值