Django/REST-Framework/PostgreSQL 项目。
有主要是一对多连接的模型,以及对数据库进行查询的 API。
现在加载一些页面时大约有 700-1500 个查询到数据库,数据库正在增长并且会进一步恶化。看哪条路解决问题?
编辑现有代码/使用原始查询?据我了解,这是一个临时解决方案,我们将再次触及天花板。
键值存储 - Redis/memcached 等?但是在这里,正如我也假设的那样,数据增长存在上限,我们必须改变项目的结构。
在寻找解决方案时,我看到了另一种方法——阻止从数据库中读取。解释一下。我们可以在构建 API 时使用它吗?比如逐渐给数据,最多一条记录,传给消费者?我们可以结合使用块读取和键值存储吗?
关于这些方法还有哪些其他评论以及我没有考虑到的内容?
添加了部分代码以供理解。有两个相关模型。第一个对应表中的 120 条记录,第二个 - 超过 10000(每个元素大约 100 个 Property 对象)
class Element(models.Model):
name = models.CharField(verbose_name="Name", max_length=255, blank=True, null=True)
class Meta:
verbose_name = "Element"
verbose_name_plural = "Elements"
class Property(models.Model):
element = ForeignKey(Element, related_name='properties')
title = models.CharField(max_length=255)
type = models.CharField(max_length=50, choices=TYPE)
value = models.CharField(max_length=255, blank=True, null=True)
class Meta:
verbose_name = "Property"
verbose_name_plural = "Properties"
ordering = ('title',)
从 API 中,我得到以下结构——我需要从表格的某些行中进行选择property
[
{
"name": "Lithium",
"properties": {
"group": "1",
"atomic_number": "3",
"symbol": "Li",
"period": "2",
"atomic_weight": "6.941",
"type": "alkali"
}
},
{
"name": "Beryllium",
"properties": {
"group": "2",
"atomic_number": "4",
"symbol": "Be",
"period": "2",
"atomic_weight": "9.012182",
"type": "alkaline"
}
}
...
在我使用视图请求的方法之一中(obj 是一个 Element 模型对象)
def get_properties(self, obj):
properties = ['Symbol', 'Group', 'Period', 'Type', 'Atomic weight', 'Atomic number']
func = lambda property: obj.properties.get(title=property).value if obj.properties.filter(title=property).exists() else None
data = map(func, properties)
return dict(zip(properties, data))
这里这个函数中的检查使对数据库的请求数增加了一倍,我们删除了检查 - 请求数下降了一半,我不明白如何绕过
func = lambda property: obj.properties.get(title=property).value if obj.properties.filter(title=property).exists() else None
在我看来,这里的问题更有可能是不正确的查询构造和架构,而不是数据库性能的严重问题。
每页 700-1500 个请求是禁止的。我确信请求的数量可以大大减少。
例如,如果我们以您引用的方法为例:
在 10 分钟内,我设法重写了它,使其仅使用一次对数据库的调用:
我确信您的代码中这样的地方并不少,只需重写它们就可以显着提高性能,这样它们就不会生成不必要的请求,也不会重新计算已经计算过的内容,等等。
在现代云系统和良好的旧大型机系统中,对来自用户程序的请求数量有限制。如果用户的程序超出了限制,它会被踢出而不说话。典型限制为 150 个 SQL 查询 + 100 个 DML 查询。触发器中的请求数量也有限制,但这是一个单独的问题。
加载页面时,请求的数量不能超过某个合理的数量,通常<10。如果你有 1500 - 它根本不属于任何标准,是时候向那些如此勤奋地生成这些请求的开发人员提出问题了:-)
好吧,你自己判断——毕竟,你可以通过一个请求选择所有 20 到 30 岁的人,或者你可以用一个单独的请求来追逐他们中的每个人……如果可以一次全部分开,为什么要分开呢?