Короче: используйте Cache
ИМХО, я не думаю, что есть проблема с самим сериализатором.Но проблема заключается в размере данных.
Я провел некоторое тестирование с сериализатором с 100K строк на вашей модели (без части POSTGIS) и обнаружил, что в среднем сериализованные данные генерируются за 18 секундместная машина.Я тестировал стандартный сериализатор django , но для получения строк 100K потребовалось около 20 секунд.
Сравнение между сериализатором DRF и Django Serializer :
Итак, поскольку отношение FK не очень важно, и я также проверил с prefetch_related
, оно не принесло значительных улучшений.или.
Так что, я бы сказал, нам нужно улучшить в другом месте.ИМХО, я думаю узким местом здесь является БД.Таким образом, вы можете внести некоторые улучшения, такие как Индексное кэширование (к вашему сведению: я не эксперт в этом, я не знаю, возможно ли это или нет; возможно, стоит попробовать).
Но есть еще лучший подход - использовать in memory storage
для кэширования данных.Вы можете использовать Redis .
Сохранение / извлечение 100K строк в redis также занимает значительно меньше времени, чем запрос к БД (около 2 секунд).Скриншот с моего локального компьютера:
Вы можете попробовать вот так:
- Сначала сохраните данные Json вRedis с таймаутом.Таким образом, через некоторое время данные redis будут стерты и загружены из БД снова.
- Когда вызывается API, сначала проверьте, существует ли он в Redis, если он есть, а затем отправьте из Redis
- Иначе, подайте с Seralizer и снова сохраните JSON в Redis.
Пример кодирования:
import json
import redis
class SomeView(APIView):
def get(self, request, *args, **kwargs):
host = getattr(settings, "REDIS_HOST", 'localhost') # assuming you have configuration in settings.py
port = getattr(settings, "REDIS_PORT", 6379)
KEY = getattr(settings, "REDIS_KEY", "TELE_DATA")
TIME_OUT = getattr(settings, "REDIS_TIMEOUT", 3600)
pool=redis.StrictRedis(host, port)
data = pool.get(KEY)
if not data:
data = TelematicsDataSerializer(TelematicsData.objects.all(), many=True).data
pool.set(KEY, json.dumps(data), e=TIME_OUT)
return Response(data)
else:
return Response(json.loads(data))
У этого решения есть два основных недостатка.
- Строки, которые вставляются между тайм-аутом (скажем, один час), затем не будут отправлены в ответе
- Если Redis пуст, то для отправки потребуется более 40 секундответ пользователю.
Чтобы преодолеть эти проблемы, мы можем ввести что-то вроде celery , которое будет периодически обновлять данные в Redis.То есть мы определим новую задачу сельдерея, которая будет периодически загружать данные в Redis и удалять старые.Мы можем попробовать вот так:
from celery.task.schedules import crontab
from celery.decorators import periodic_task
@periodic_task(run_every=(crontab(minute='*/5')), name="load_cache", ignore_result=True) # Runs every 5 minute
def load_cache():
...
pool=redis.StrictRedis(host, port)
json_data = TelematicsDataSerializer(TelematicsData.objects.all(), many=True).data
pool.set(KEY, json.dumps(data)) # No need for timeout
А в представлении:
class SomeView(APIView):
def get(self, request, *args, **kwargs):
host = getattr(settings, "REDIS_HOST", 'localhost') # assuming you have configuration in settings.py
port = getattr(settings, "REDIS_PORT", 6379)
KEY = getattr(settings, "REDIS_KEY", "TELE_DATA")
TIME_OUT = getattr(settings, "REDIS_TIMEOUT", 3600)
pool=redis.StrictRedis(host, port)
data = pool.get(KEY)
return Response(json.loads(data))
Таким образом, пользователь всегда будет получать данные из кеша.У этого решения есть и обратная сторона: пользователь может получить не самые последние строки данных (если они находятся между интервалами времени задачи сельдерея).Но допустим, что вы хотите заставить celery перезагрузить кэш, используя load_cache.apply_async()
(для асинхронного запуска) или load_cache.apply()
(для синхронного запуска).
Кроме того, вы можете использовать множество альтернатив Redis для кэширования, например memcache
, elastic search
и т. Д.
Эксперимент:
Поскольку размер данных огромен, возможно, вы можете сжать данные при хранении и распаковать их при загрузке.Но это снизит производительность, не уверен, на какой маржеВы можете попробовать вот так:
Сжатие
import pickle
import gzip
....
binary_data = pickle.dumps(data)
compressed_data = gzip.compress(binary_data)
pool.set(KEY, compressed_data) # No need to use JSON Dumps
Декомпрессия
import pickle
import gzip
....
compressed_data = pool.get(KEY)
binary_data = gzip.decompress(compressed_data)
data = pickle.loads(binary_data)