Django搜索结果优化:序列化对象缓存方案及响应提速问题
问题分析与优化方案
一、解决序列化时的额外查询问题
你已经用了prefetch_related但仍有额外查询,大概率是这几个原因,对应解决方法如下:
- 检查Serializer字段是否匹配prefetch层级:如果
TripListSerializer里嵌套了子Serializer(比如TripValuesSerializer),且子Serializer用到了operator_trip_field__trip_field这类深层关联,要确保prefetch_related覆盖了所有用到的关联路径。可以用Prefetch对象精准控制预取的查询集,减少冗余查询:from django.db.models import Prefetch trips = ( Trip.objects.filter(filter_query) .prefetch_related( Prefetch( "trip_values", queryset=TripValue.objects.select_related("operator_trip_field__trip_field") ), "operator_service_day", "trip_occurrences", "trip_locations", "assigned_vehicle_formation", ) ) - 区分
select_related和prefetch_related:如果是正向一对一/外键关联(比如Trip到某个单对象的关联),用select_related替代prefetch_related,它会用JOIN语句一次性拉取数据,比预取更高效。 - 排查
SerializerMethodField:如果Serializer里用了SerializerMethodField,要确保方法内部没有触发新的数据库查询,比如提前预取所需数据,或者把逻辑移到数据库层面处理。
二、批量缓存优化方案
不要逐个查询缓存,改用批量获取+批量缓存的方式,把N次缓存查询降到1次:
# 1. 先拿到所有trip的ID列表 trip_ids = list(trips.values_list('id', flat=True)) # 2. 构造批量缓存key cache_keys = [f"trip:{trip_id}" for trip_id in trip_ids] # 3. 一次性获取所有缓存数据 cached_data = cache.get_many(cache_keys) # 4. 分离已缓存和未缓存的ID cached_ids = {int(key.split(':')[1]) for key in cached_data.keys()} uncached_ids = [tid for tid in trip_ids if tid not in cached_ids] # 5. 仅查询未缓存的trip并序列化 uncached_trips = Trip.objects.filter(id__in=uncached_ids).prefetch_related( "trip_values", "trip_values__operator_trip_field__trip_field", # 其他需要的关联... ) uncached_serialized = TripListSerializer(uncached_trips, many=True).data # 6. 批量缓存未命中的结果 cache_batch = {f"trip:{tid}": data for tid, data in zip(uncached_ids, uncached_serialized)} cache.set_many(cache_batch, timeout=3600) # 7. 按原顺序合并缓存与新查询结果 final_data = [] for tid in trip_ids: if tid in cached_ids: final_data.append(cached_data[f"trip:{tid}"]) else: idx = uncached_ids.index(tid) final_data.append(uncached_serialized[idx])
三、Response阶段提速方法
序列化时间短但总请求时长占比大,主要优化方向是减少数据传输和JSON序列化耗时:
- 替换高效JSON序列化库:用
ujson替代Django默认的JSON编码器,速度提升明显。可以直接在Response中使用:import ujson return HttpResponse(ujson.dumps(final_data), content_type='application/json') - 开启Gzip压缩:在Django的
settings.py中添加Gzip中间件,大幅减少传输的数据量:MIDDLEWARE = [ 'django.middleware.gzip.GZipMiddleware', # 其他中间件... ] - 分页返回结果:200+条数据一次性返回压力大,改用分页,比如每页20条,既减少单次传输的数据量,也降低前端渲染压力:
from django.core.paginator import Paginator paginator = Paginator(trips, 20) page = request.GET.get('page', 1) current_page = paginator.get_page(page) serialized = TripListSerializer(current_page, many=True) return Response({ 'data': serialized.data, 'page': current_page.number, 'total': paginator.count }) - 缓存整个查询结果:如果查询条件重复率高,直接缓存整个查询的序列化结果。用查询条件的哈希值作为缓存key,避免重复查询和序列化:
import hashlib # 生成查询唯一标识(比如结合filter参数和请求参数) query_signature = str(filter_query) + str(request.GET.dict()) cache_key = hashlib.md5(query_signature.encode()).hexdigest() cached_result = cache.get(cache_key) if cached_result: return Response(cached_result) # 执行查询、序列化并缓存 serialized = TripListSerializer(trips, many=True).data cache.set(cache_key, serialized, timeout=3600) return Response(serialized)
内容的提问来源于stack exchange,提问作者AlxVallejo
相关产品推荐
相关产品推荐

