使用Item外键关联SaleItem性能过慢,寻求优化方案
问题场景与代码
Item 模型
class Item(models.Model): title = models.CharField(max_length=100) model = models.CharField(max_length=100, blank=True, null=True) sku = models.CharField(max_length=100, blank=True, null=True) ean = models.CharField(max_length=100, blank=True, null=True) price = models.FloatField(blank=True, null=True) inventory = models.IntegerField(blank=True, null=True) sales_price = models.DecimalField(decimal_places=2, max_digits=15, blank=True, null=True) web_shop_price = models.DecimalField(decimal_places=2, max_digits=15, blank=True, null=True) bb_price = models.DecimalField(decimal_places=2, max_digits=15, blank=True, null=True) discount_price = models.FloatField(blank=True, null=True) categorie = models.ForeignKey(Categorie, on_delete=models.CASCADE, null=True, blank=True) # slug = models.SlugField(blank=True, null=True) description = models.TextField(blank=True, null=True) image = models.ImageField(upload_to='images', null=True, blank=True)
SaleItem 模型
class SaleItem(models.Model): invoice_id = models.PositiveIntegerField(auto_created=True) product = models.ForeignKey(Item, on_delete=models.CASCADE)
当前数据库中已存有15000+条Item数据,在通过SaleItem的外键关联查询或操作时,耗时极长,性能严重下降。使用的是JawsDB 10美元套餐的MySQL数据库,推测除数据量因素外,数据库内存共享也可能是诱因。
优化方案
索引优化:外键字段默认会创建索引,但需确认
SaleItem.product的索引是否正常生效。另外,若业务查询常使用Item的特定字段(如sku、title)进行关联或过滤,可为这些字段添加索引:# 在Item模型的Meta类中添加索引 class Item(models.Model): # 原有字段... class Meta: indexes = [ models.Index(fields=['sku']), models.Index(fields=['title']), ]执行迁移后,MySQL会生成对应索引,直接加速关联查询效率。
避免无效查询:检查业务中涉及SaleItem与Item的查询逻辑,杜绝全表扫描:
- 避免使用
__contains这类无法命中索引的模糊查询,改用__startswith; - 不要默认查询所有字段,用
only()或values()指定所需字段,减少数据传输量:# 示例:仅加载必要字段,跳过description、image等大字段 SaleItem.objects.select_related('product').only('invoice_id', 'product__title', 'product__sku')
- 避免使用
数据库配置调整:针对JawsDB的共享内存限制,可尝试:
- 联系服务商调整
join_buffer_size和sort_buffer_size参数,提升关联查询的内存分配; - 定期归档历史SaleItem数据,减少单表数据体量。
- 联系服务商调整
批量操作替代循环单条操作:如果存在批量创建/更新SaleItem的场景,使用Django的
bulk_create()和bulk_update()方法,减少数据库交互次数:# 批量创建SaleItem示例 sale_items = [ SaleItem(invoice_id=1, product=item1), SaleItem(invoice_id=1, product=item2), # 更多对象 ] SaleItem.objects.bulk_create(sale_items)用select_related解决N+1查询:查询SaleItem时,通过
select_related()提前加载关联的Item数据,避免多次数据库请求:# 一次查询获取SaleItem及关联的Item数据 sale_items = SaleItem.objects.select_related('product').all()
内容的提问来源于stack exchange,提问作者Sesshōmaru
相关产品推荐
相关产品推荐

