我设计了一个处理客户股票投资组合的交易应用程序。
我使用两种数据存储类型:
db.Model python模块:
class Stocks (db.Model):
stockname = db.StringProperty(multiline=True)
dailyPercentChange=db.FloatProperty(default=1.0)
class UserTransactions (db.Model):
buyer = db.UserProperty()
value=db.FloatProperty()
stockref = db.ReferenceProperty(Stocks)
每小时我需要更新数据库:更新Stocks
中的每日百分比更改,然后更新引用该库存的UserTransactions
中所有实体的值。
以下python模块遍历所有股票,更新dailyPercentChange属性,并调用任务来检查引用股票并更新其价值的所有UserTransactions
个实体:
Stocks.py
# Iterate over all stocks in datastore
for stock in Stocks.all():
# update daily percent change in datastore
db.run_in_transaction(updateStockTxn, stock.key())
# create a task to update all user transactions entities referring to this stock
taskqueue.add(url='/task', params={'stock_key': str(stock.key(), 'value' : self.request.get ('some_val_for_stock') })
def updateStockTxn(stock_key):
#fetch the stock again - necessary to avoid concurrency updates
stock = db.get(stock_key)
stock.dailyPercentChange= data.get('some_val_for_stock') # I get this value from outside
... some more calculations here ...
stock.put()
Task.py(/ task)
# Amount of transaction per task
amountPerCall=10
stock=db.get(self.request.get("stock_key"))
# Get all user transactions which point to current stock
user_transaction_query=stock.usertransactions_set
cursor=self.request.get("cursor")
if cursor:
user_transaction_query.with_cursor(cursor)
# Spawn another task if more than 10 transactions are in datastore
transactions = user_transaction_query.fetch(amountPerCall)
if len(transactions)==amountPerCall:
taskqueue.add(url='/task', params={'stock_key': str(stock.key(), 'value' : self.request.get ('some_val_for_stock'), 'cursor': user_transaction_query.cursor() })
# Iterate over all transaction pointing to stock and update their value
for transaction in transactions:
db.run_in_transaction(updateUserTransactionTxn, transaction.key())
def updateUserTransactionTxn(transaction_key):
#fetch the transaction again - necessary to avoid concurrency updates
transaction = db.get(transaction_key)
transaction.value= transaction.value* self.request.get ('some_val_for_stock')
db.put(transaction)
问题:
目前系统运行良好,但问题是它不能很好地扩展...我有大约100个股票,有300个用户交易,我每小时运行一次更新。在仪表板中,我看到task.py需要大约65%的CPU(Stock.py需要大约20%-30%)而且我几乎使用app引擎给我的所有6.5小时免费CPU时间。我没有问题启用计费和支付额外的CPU,但问题是系统的扩展...使用6.5 CPU小时100个股票是非常差的。
我想知道,考虑到上面提到的系统要求,如果有一个更好更有效的实现(或者只是一个可以帮助当前实现的小改动),而不是这里提供的实现。
谢谢!
乔尔
答案 0 :(得分:8)
有几个明显的改进:
Queue
对象的.add
方法批量添加任务,记录为here。这比单独添加任务更有效。.fetch
和游标,而不是迭代;迭代20个实体的小批量提取。UserTransaction
实体?如果是这样,您可以跳过该事务并批量更新它们。最后,我建议进行整体重构:不是为每个股票开始新任务,而是使用上面提到的计时器在任务中运行外部循环。当您链接下一个任务时,使用游标传递当前状态并从上次停止的地方继续。
唯一需要考虑的是,如果有某种方法可以重构数据,以避免需要这么多更新。例如,您可以让UserTransaction实体引用Stock实体中的某个值,以便您可以在运行时计算它们的实际值,并且您只需要使用更改来更新单个Stock实体吗?