Python 在Django视图中保留状态以提高分页性能

Python 在Django视图中保留状态以提高分页性能,python,django,postgresql,datatables,Python,Django,Postgresql,Datatables,我正在设计一个数据表驱动的Django应用程序,它有一个API视图,可以用AJAX调用数据表(我在服务器端处理模式中使用数据表)。它实现了搜索、分页和排序 我的数据库最近变得很大(大约500000个条目),无论是搜索还是仅仅移动到下一页,性能都受到很大影响。我怀疑我写这个观点的方式是非常低效的。以下是我在视图中的操作(假设数据库中的对象是pizzas): filtered=Pizza.objects.filter(…)以获取与搜索条件匹配的Pizza集。(或Pizza.objects.all(

我正在设计一个
数据表
驱动的Django应用程序,它有一个API视图,可以用AJAX调用
数据表
(我在服务器端处理模式中使用
数据表
)。它实现了搜索、分页和排序

我的数据库最近变得很大(大约500000个条目),无论是搜索还是仅仅移动到下一页,性能都受到很大影响。我怀疑我写这个观点的方式是非常低效的。以下是我在视图中的操作(假设数据库中的对象是pizzas):

  • filtered=Pizza.objects.filter(…)
    以获取与搜索条件匹配的Pizza集。(或
    Pizza.objects.all()
    如果没有搜索条件)

  • paginated=filtered[start:start+length]
    仅获取当前比萨饼页面。(最多只有100个)。根据用户所在的页面,从
    数据表
    客户端代码传入起始和长度


  • pizzas=paginated.order_by(…)
    将排序应用于当前页面

然后我将
pizzas
转换为JSON并从视图中返回它们

看起来,虽然搜索500000条条目可能是一个缓慢的操作,但简单地移动到下一页不应该要求我们重做整个搜索。所以我想做的是在视图中缓存一些东西(这是一个基于类的视图)。我会跟踪最后一个搜索字符串是什么,以及它生成的结果集

然后,如果请求通过并且搜索字符串没有什么不同(如果用户点击了几页结果,就会发生这种情况),我就不必再次点击数据库来获得过滤结果——我可以使用缓存版本

它是一个只读应用程序,因此不同步不会成为问题

我甚至可以保留一本包含一大堆搜索字符串和他们应该制作的比萨饼的字典


我想知道的是:这是解决问题的合理方法吗?还是我忽略了什么?还有,我在这里重新发明轮子吗?并不是说这不容易实现,而是在
QuerySet
上有内置的选项吗?或者可以这样做吗?

pizzas=paginated.order\u by(…)
很慢,它对所有的pizzas进行排序,而不是当前页面。索引帮助:


如果你真的想要缓存,checkout,“一款支持自动或手动queryset缓存和自动细粒度事件驱动失效的灵活应用。”

有多种方法可以改进你的代码结构

首先,使用Django ORM hit仅获取根据页码需要的数据,其次,缓存ORM输出,并在再次传递相同查询时重用该结果

第一步是这样的。

在代码中

Pizza.objects.all()
分页=已筛选[开始:开始+长度]
首先获取所有数据,然后对数据进行切片,这是非常昂贵的SQL查询,将其转换为

filtered=Pizza.objects.all()[(页码1)*30,(页码1)*30+30]

上面给出的ORM将只获取那些根据提供的页码的行,与获取所有行然后对其进行切片相比速度非常快


第二种方法,是首先根据查询获取数据,然后使用memcache或redis等缓存解决方案,下次需要从数据库中获取数据时,首先检查该查询的缓存中是否存在数据,如果存在,则只需使用该数据,因为内存缓存解决方案比从数据库中获取数据快得多,因为内存和硬盘之间的输入输出传输非常大,而且我们知道硬盘驱动器传统上速度很慢。

Wow,这一般是真的吗?如果我首先对查询集进行切片,然后对其进行过滤/排序/任何操作,那么它是否真的进行过滤/排序/任何操作,然后进行切片?根据django 1.8的文档,“切片一个未评估的查询集将返回另一个未评估的查询集,不允许对其进行进一步修改(例如,添加更多过滤器或修改排序)”。看来我有一部分错了,
pizzas=paginated.order\u by(…)
是不允许的。仍然“考虑向经常使用filter()、exclude()和order_by()查询的字段添加索引”