【问题标题】:Python's counter() function is too slowPython counter() 函数太慢了
【发布时间】:2018-06-07 02:45:29
【问题描述】:

我正在使用 Django REST 框架,并且在我使用 Counter() 函数的那一行中,有一个 post API 我面临速度问题。

@api_view(["POST"])
def calculate_stuff(request):
  t1 = time.time()
  machine_type = request.data['machine_type']
  machine_nos = Machine.objects.filter(machine_type=machine_type).values_list('machine_no', flat=True)
  query = Performance.objects.filter(Q(power=100) | Q(power=192),machine_no__in=machine_nos,
                ).values_list("machine_no", "power")
  t2 = time.time()
  print t2 - t1 # is around 0.2 seconds 
  count_192_100 = Counter(query) 
  t3 = time.time()
  print t3 - t2 # is around 1.3 seconds

模型是这样的:

class Machine(models.Model):
  machine_type = models.CharField(null=True, max_length=10)
  machine_no = models.IntegerField(null=True)
  store_code = models.IntegerField(null=True)
  created = models.DateTimeField(auto_now_add=True)
  updated = models.DateTimeField(auto_now = True)
class Performance(models.Model):
  machine_no = models.IntegerField(null=True)
  power = models.IntegerField(null=True)
  store_code = models.IntegerField(null=True) 
  created = models.DateTimeField(auto_now_add=True)
  updated = models.DateTimeField(auto_now=True)

这个项目正在生产中运行,由于某种原因,两个模型之间没有使用外键。我想在这里实现的是首先:从某个 machine_type 的“Machine”模型中获取 machine_no。第二:我想得到每台机器多少次。在“性能”模型中具有功率 = 100 和功率 = 192。 附加信息:我正在使用 Django 1.11、python 2.7.10、postgresql。

【问题讨论】:

    标签: python django python-2.7


    【解决方案1】:

    我实际上并不认为您的聚合计数器很慢,而是您的查询集评估,即您的数据访问。请参阅 Django ORM 查询集是所谓的“惰性”。它们可以被操纵,但评估(实际上是获取数据)被推迟到一些触发执行上下文。这是概述此内容的文档: https://docs.djangoproject.com/en/2.0/ref/models/querysets/#when-querysets-are-evaluated

    当查询集被评估时¶

    在内部,一个 QuerySet 可以被构造、过滤、切片和 通常在没有实际访问数据库的情况下传递。不 数据库活动实际上会发生,直到您执行某些操作来评估 查询集。

    因此,由于您的 timeit 构造,看起来瓶颈是您的聚合计数器,但可能不是。这可能是对查询集的评估。

    有一些优化 dB 访问的策略,这里概述了它们: https://docs.djangoproject.com/en/2.0/topics/db/optimization/

    如果不了解更多信息,我们就无法更具体。如果您不介意分享您的 dB 模型以及有关它实际获取的数据量的一些详细信息,我很乐意提供帮助。

    您可以使用本文中概述的方法之一在强制评估上运行 timeit,例如在查询集上使用内置的 list(),并在计数器构造函数之前确定它会发生。

    【讨论】:

    • 我已经编辑了问题并添加了模型,这个项目正在生产中运行,由于某种原因,两个模型之间没有使用外键。我想在这里实现的是首先:从某个 machine_type 的 Machine 模型中获取 machine_no。第二:我想从性能模型中获取每个 machine_no 的 power=192 或 100 个计数。
    【解决方案2】:

    计数器并不是花费所有时间的东西。 实际运行查询并获取结果是花费所有时间的原因。在您迭代 QuerySet 之前,查询不会运行,这在您调用 Counter 时会发生。

    不要调用Counter,而是考虑让数据库为您计算。

    【讨论】:

    • 我单独运行查询并删除了计数器功能,查询需要0.1秒计算。
    • @jencko:在不调用Counter 的情况下显示运行查询的代码。我敢打赌,您构建 QuerySet 时实际上并没有做任何事情来导致查询执行。
    • 其实你是对的。我没有做任何导致查询执行的事情。谢谢 !!在数据库方面这样做有什么建议吗?
    • 你想要什么,行数? select count(*) from table where a = 1 在 Django ORM 语法中?
    • @PaulBecotte:我已经编辑了问题并添加了模型,这个项目正在生产中运行,由于某种原因,两个模型之间没有使用外键。我想在这里实现的是首先:从某个 machine_type 的 Machine 模型中获取 machine_no。第二:我想从性能模型中获取每个 machine_no 的 power=192 或 100 个计数。
    【解决方案3】:
     query = Performance.objects.filter(Q(power=100) | 
        Q(power=192),machine_no__in=machine_nos,
                    ).values_list("machine_no", "power")
     count_192_100 = Counter(query) 
    

    这会为数据库中的每一行提取一个实际对象,反序列化它们,然后使用散列对它们进行唯一计数。取而代之的是,将您的数据库用于它的好处-

    count_192_100 = Performance.objects.filter(Q(power=100) | 
        Q(power=192),machine_no__in=machine_nos,
                    ).values_list("machine_no", "power").count()
    

    .count() 基本上是select COUNT(*) from...

    当然我应该提一下,这与您的查询完全不同 - Counter 使用散列来计算唯一值!如果您可能在该表中有重复的行,您可以使用 select COUNT(DISTINCT *) FROM ... 来处理

    count_192_100 = Performance.objects.filter(Q(power=100) | 
        Q(power=192),machine_no__in=machine_nos,
                    ).values_list("machine_no", "power").distinct().count()
    

    【讨论】:

      猜你喜欢
      • 2022-01-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-14
      • 2018-11-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多