You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django存储序列化JSON数据优化发票序列化性能的最佳方案咨询

优化DRF发票序列化性能:预存储JSON方案详解

这个问题我之前在项目里也碰到过,预序列化存储确实是解决大量数据实时序列化性能瓶颈的有效方案,下面给你梳理几个可行的方案,按推荐优先级排序:

一、最优方案:直接在现有Invoice模型添加JSONField

这是最简洁、低侵入的方案,完全不需要新建模型,直接在发票模型里新增一个字段存储序列化后的JSON数据:

实现步骤

  1. 添加字段
    在你的Invoice模型中新增serialized_data字段:

    from django.db import models
    
    class Invoice(models.Model):
        # 原有字段:customer、products、number、date等
        customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
        products = models.ManyToManyField(Product)
        
        # 新增存储序列化数据的字段
        serialized_data = models.JSONField(null=True, blank=True, help_text="预序列化的发票搜索数据")
    
  2. 自动更新序列化数据
    可以通过两种方式实现创建/编辑发票时自动更新serialized_data:

    • 重写模型save方法(适合简单场景):
      from rest_framework import serializers
      
      class InvoiceSearchSerializer(serializers.ModelSerializer):
          # 只序列化搜索需要的字段,减少数据量
          customer = serializers.StringRelatedField()
          products = serializers.StringRelatedField(many=True)
          
          class Meta:
              model = Invoice
              fields = ["id", "number", "date", "customer", "products"]
      
      class Invoice(models.Model):
          # 字段定义...
          serialized_data = models.JSONField(null=True, blank=True)
      
          def save(self, *args, **kwargs):
              # 序列化当前发票实例
              serializer = InvoiceSearchSerializer(self)
              self.serialized_data = serializer.data
              super().save(*args, **kwargs)
      
    • 使用Django信号(适合需要监听外键模型更新的场景):
      比如当关联的Customer或Product数据更新时,需要同步更新所有关联发票的序列化数据:
      from django.db.models.signals import post_save
      from django.dispatch import receiver
      
      @receiver(post_save, sender=Customer)
      def update_invoice_customer_data(sender, instance, **kwargs):
          # 更新所有关联该客户的发票序列化数据
          invoices = Invoice.objects.filter(customer=instance)
          for invoice in invoices:
              serializer = InvoiceSearchSerializer(invoice)
              invoice.serialized_data = serializer.data
              invoice.save(update_fields=["serialized_data"])
      
  3. 视图层使用预存数据
    搜索时直接读取serialized_data字段,无需再调用序列化器:

    from django.http import JsonResponse
    
    def invoice_search(request):
        # 示例:按客户名称搜索
        keyword = request.GET.get("customer", "")
        invoices = Invoice.objects.filter(serialized_data__customer__icontains=keyword)
        # 直接返回预存的JSON数据
        return JsonResponse([inv.serialized_data for inv in invoices], safe=False)
    

优点

  • 零额外模型维护成本,逻辑集中在原发票模型
  • 查询时直接读取JSON,避免重复序列化和数据库关联查询
  • 数据一致性容易保证,创建/编辑/外键更新时自动同步

二、不推荐:新建独立模型存储序列化数据

除非你有特殊需求(比如需要保留序列化数据的历史版本、审计日志),否则完全没必要新建模型。如果一定要做,正确的方式是一对一关联发票,而非将所有发票JSON存入单个字段:

class InvoiceSerialized(models.Model):
    invoice = models.OneToOneField(Invoice, on_delete=models.CASCADE, primary_key=True)
    data = models.JSONField()

这种方案的问题在于:增加了数据库表关联的复杂度,读写时多了一次关联查询,维护成本远高于直接在原模型加字段。

三、进阶优化:结合缓存进一步提升性能

如果你的发票搜索是高频操作,可以在数据库预存的基础上,再加上缓存(Redis/Memcached),实现"缓存优先"的查询逻辑:

from django.core.cache import cache

def get_invoice_serialized_data(invoice_id):
    cache_key = f"invoice_search_data_{invoice_id}"
    data = cache.get(cache_key)
    
    if not data:
        invoice = Invoice.objects.get(id=invoice_id)
        # 先读数据库预存的JSON
        data = invoice.serialized_data or InvoiceSearchSerializer(invoice).data
        # 同步更新数据库(如果之前没预存)
        if not invoice.serialized_data:
            invoice.serialized_data = data
            invoice.save(update_fields=["serialized_data"])
        # 缓存一天
        cache.set(cache_key, data, timeout=86400)
    
    return data

这种双层存储的方式,既能保证数据一致性,又能把高频查询的性能拉到最高。

四、额外优化建议

  • 精简序列化字段:只序列化搜索需要的字段,不要全量序列化,减少数据存储体积和序列化耗时
  • 批量更新历史数据:对于已有的老发票,可以写一个脚本批量生成serialized_data,避免手动更新
  • 索引优化:如果需要对JSON字段做复杂搜索,可以给serialized_data添加GIN索引(PostgreSQL支持),提升查询速度

内容的提问来源于stack exchange,提问作者serbboy23

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:30:39