2017-03-18 41 views
0

我会尽我所能解释我的问题。我遇到性能问题,我想知道是否有更好的方法来设置它。优化Django queryset调用,大列表?

我有一个数据库,大概有150年的年份数据。每行有大约10列。

我在每30年一次的范围内运行一次“模拟”(我们将每个30yr块称为“周期”)。所以,第一周期将是1-31年。第二周期将是2-32年。周期3,3-33年。明白了吗?

all_data = DataPoint.objects.all().yearly() 
cycle_length = 30 
previous_data_points = [] 
for data_point in all_data: 
    if len(previous_data_points) < cycle_length: 
     previous_data_points.append(data_point) 
     continue 
    simulation_cycles.append(Cycle(previous_data_points)) 
    if len(previous_data_points) == cycle_length: 
     previous_data_points.pop(0) 
    previous_data_points.append(data_point) 

所以,对于每一个30年的周期,我喂Cycle功能的30项查询集来初始化数据。问题是,当我使用Django的connection.queries列出发生了什么事情时,它看起来正在做3000多个查询,并且需要10-12秒,这对于它正在做的事情来说相当长。

在connection.queries列表中,当我将它传递给Cycle时(每个使用“我认为是我的.yearly的WHERE EXTRACT(MONTH FROM”),我看到它正在做30个单独的调用(每个数据点为1) ()过滤器被调用,但它也在Cycle()中记录查询,它实际上按日期查找数据点。我的应用程序确实运行了3000个查询(使用db上的连接),还是我的DataPoint.objects.all().your()调用中的一个大查询,并且其他所有内容都在内存中?

我正在尝试要理解为什么这样慢的运行我假设,我创造了这个巨大的对象列表:120个“周期”,每个对象都有30个单独的“年”对象,这些对象拥有自己的数据(以及用于计算的数据)供以后使用。记忆中有那么多物体会伤害我,还是那个小土豆?

编辑:

class Cycle: 
def __init__(self, data_points): 
    self.range_start = data_points[0].data_date 
    self.range_end = data_points[-1].data_date 
    self.start_CPI = data_points[0].cpi 
    self.years = relativedelta(self.range_end, self.range_start).years + 1 
    self.sim = [] 

    for i in range (0, self.years): 
     data_point = data_points[i] 
     self.sim.append(Segment(
      date=self.range_start + relativedelta(years=i), 
      start_CPI=self.start_CPI, 
      yearly_equities_growth=data_point.yearly_equities_growth, 
      cpi=data_point.cpi, 
      dividend=data_point.dividend, 
      s_and_p_composite=data_point.s_and_p_composite, 
      long_interest_rate=data_point.long_interest_rate 
     )) 

class Segment: 
def __init__(self, date, start_CPI, yearly_equities_growth, cpi, dividend, s_and_p_composite, long_interest_rate): 
    self.start_CPI = D(start_CPI) 
    self.date = date 
    self.portfolio = { 
     "start": None, 
     "end": None, 
     "inflation_adjusted_start": None, 
     "inflation_adjusted_end": None, 
     "fees": None 
    } 
    self.spending = None 
    self.inflation_adjusted_spending = None 
    self.equities = { 
     "start": None, 
     "growth": None, 
     "val": None 
    } 
    self.bonds = { 
     "start": None, 
     "growth": None, 
     "val": None 
    } 
    self.gold = { 
     "start": None, 
     "growth": None, 
     "val": None 
    } 
    self.cash = { 
     "start": None, 
     "growth": None, 
     "val": None 
    } 
    self.dividends = { 
     "growth": None, 
     "val": None 
    } 
    self.fees = None 

    self.yearly_equities_growth = D(yearly_equities_growth) 
    self.cumulative_inflation = 1 + (D(cpi) - self.start_CPI)/self.start_CPI 
    self.sum_of_adjustments = None 
    self.cpi = cpi 
    self.dividend = dividend 
    self.s_and_p_composite = s_and_p_composite 
    self.long_interest_rate = long_interest_rate 
+1

你可以分享Cycle类的代码吗?更好地了解你的问题 – jperelli

+0

完成。 Cycle()也调用Segment(),所以我添加了这个。每个周期有30个分段。 –

回答

0

从你的代码,在我看来,这部分是做一个O(N²)算法

for i in range (0, self.years): 

如果里面那个for需要去运行任意代码数据库,这是问题。

检查是否有任何此字段正在击中数据库。如果其中任何一个是相关字段,则需要prefetch_related或select_related。或者,也许这些都是需要访问数据库的属性,为此寻求帮助。

data_point.yearly_equities_growth, 
data_point.cpi, 
data_point.dividend, 
data_point.s_and_p_composite, 
data_point.long_interest_rate 

此外,什么是D()函数在做什么?它可能会强制查询数据库,请检查。

+0

那些属性确实碰到了数据库,但我*认为*当我传入'data_point'时,它已经有了数据。 D()只是'从十进制导入十进制为D' –

+0

我不熟悉'prefetch_related或select_related',我会检查出来并报告回来。 –

+0

prefetch_related和select_related帮助的情况下,你使用属性是关系与其他表 – jperelli

0

对QuerySet进行迭代应该在第一次迭代中查询所有内容,然后将缓存的结果用于后续迭代(source)。我不熟悉connection.queries,但是

您可以使用django-debug-toolbar,django-extensions或django silk来理智地检查3000+查询假设(source)。

@jperelli建议'data_point = data_points [i]可能是罪魁祸首。

for i in range (0, self.years): 
    data_point = data_points[i] 

如果在将all_data传递到迭代器之前将其转换为列表会发生什么?