针对社交排行榜进行优化
Optimizing for Social Leaderboards
我正在使用 Google App Engine (python) 作为移动社交游戏的后端。该游戏使用 Twitter 集成,让人们可以关注相关排行榜并与他们的朋友或关注者对战。
到目前为止,最昂贵的难题是点击 Twitter API 查询给定用户的朋友和关注者的后台(推送)任务,然后将该数据存储在我们的数据存储中.我正在尝试对其进行优化以尽可能降低成本。
数据模型:
与这部分应用程序相关的主要模型有以下三个:
User
'''General user info, like scores and stats'''
# key id => randomly generated string that uniquely identifies a user
# along the lines of user_kdsgj326
# (I realize I probably should have just used the integer ID that GAE
# creates, but its too late for that)
AuthAccount
'''Authentication mechanism.
A user may have multiple auth accounts- one for each provider'''
# key id => concatenation of the auth provider and the auth provider's unique
# ID for that user, ie, "tw:555555", where '555555' is their twitter ID
auth_id = ndb.StringProperty(indexed=True) # ie, '555555'
user = ndb.KeyProperty(kind=User, indexed=True)
extra_data = ndb.JsonProperty(indexed=False) # twitter picture url, name, etc.
RelativeUserScore
'''Denormalization for quickly generated relative leaderboards'''
# key id => same as their User id, ie, user_kdsgj326, so that we can quickly
# retrieve the object for each user
follower_ids = ndb.StringProperty(indexed=True, repeated=True)
# misc properties for the user's score, name, etc. needed for leaderboard
我认为这个问题没有必要,但为了以防万一,here 是导致此设计的更详细的讨论。
任务
后台线程接收 Twitter 身份验证数据并通过 tweepy 从 Twitter API 请求一大块朋友 ID。 Twitter 默认发送最多 5000 个朋友 ID,如果可以避免的话,我宁愿不任意限制更多(你每分钟只能向他们 API 发出这么多请求)。
获得朋友 ID 列表后,我可以轻松地将其转换为 "tw:" AuthAccount 密钥 ID,并使用 get_multi 检索 AuthAccount。然后我删除所有不在我们系统中的 Twitter 用户的 Null 帐户,并获取我们系统中的 Twitter 好友的所有用户 ID。这些 id 也是 RelativeUserScores 的键,所以我使用一堆 transactional_tasklets 将此用户的 ID 添加到 RelativeUserScore 的关注者列表中。
优化问题
- 发生的第一件事是调用 Twitter 的 API。鉴于任务中的其他所有内容都需要这样做,我假设我不会从异步中获得任何收益,对吗? (GAE 已经足够聪明,可以在这个阻塞时使用服务器来处理其他任务?)
判断twitter好友是否在玩我们的游戏时,我目前将所有twitter好友id转换为auth账号id,通过get_multi获取。鉴于此数据稀疏(大多数 Twitter 朋友很可能不会玩我们的游戏),我是否最好使用直接检索用户 ID 的投影查询?像...
twitter_friend_ids = twitter_api.friend_ids() # potentially 5000 values
friend_system_ids = AuthAccount\
.query(AuthAccount.auth_id.IN(twitter_friend_ids))\
.fetch(projection=[AuthAccount.user_id])
(我不记得或找不到位置,但我读到这个更好,因为您不会浪费时间尝试读取不存在的模型对象
- 无论我最终使用 get_multi 还是投影查询,将请求分解为多个异步查询而不是尝试一次获取/查询可能的 5000 个对象是否有任何好处?
我会这样组织任务:
- 对 Twitter 提要进行异步提取调用
- Use memcache 保存所有 AuthAccount->User 数据:
- 从 memcache 请求数据,如果不存在则调用
fetch_async()
调用 AuthAccount
来填充 memcache 和本地字典
- 运行 通过字典的每个推特 ID
下面是一些示例代码:
future = twitter_api.friend_ids() # make this asynchronous
auth_users = memcache.get('auth_users')
if auth_users is None:
auth_accounts = AuthAccount.query()
.fetch(projection=[AuthAccount.auth_id,
AuthAccount.user_id])
auth_users = dict([(a.auth_id, a.user_id) for a in auth_accounts])
memcache.add('auth_users', auth_users, 60)
twitter_friend_ids = future.get_result() # get async twitter results
friend_system_ids = []
for id in twitter_friend_ids:
friend_id = auth_users.get("tw:%s" % id)
if friend_id:
friend_system_ids.append(friend_id)
这针对相对较少的用户和较高的请求率进行了优化。您上面的评论表明用户数量较多,请求率较低,因此我只会对您的代码进行此更改:
twitter_friend_ids = twitter_api.friend_ids() # potentially 5000 values
auth_account_keys = [ndb.Key("AuthAccount", "tw:%s" % id) for id in twitter_friend_ids]
friend_system_ids = filter(None, ndb.get_multi(auth_account_keys))
当使用 get_multi()
键时,这将使用 ndb 的内置内存缓存来保存数据。
我正在使用 Google App Engine (python) 作为移动社交游戏的后端。该游戏使用 Twitter 集成,让人们可以关注相关排行榜并与他们的朋友或关注者对战。
到目前为止,最昂贵的难题是点击 Twitter API 查询给定用户的朋友和关注者的后台(推送)任务,然后将该数据存储在我们的数据存储中.我正在尝试对其进行优化以尽可能降低成本。
数据模型:
与这部分应用程序相关的主要模型有以下三个:
User
'''General user info, like scores and stats'''
# key id => randomly generated string that uniquely identifies a user
# along the lines of user_kdsgj326
# (I realize I probably should have just used the integer ID that GAE
# creates, but its too late for that)
AuthAccount
'''Authentication mechanism.
A user may have multiple auth accounts- one for each provider'''
# key id => concatenation of the auth provider and the auth provider's unique
# ID for that user, ie, "tw:555555", where '555555' is their twitter ID
auth_id = ndb.StringProperty(indexed=True) # ie, '555555'
user = ndb.KeyProperty(kind=User, indexed=True)
extra_data = ndb.JsonProperty(indexed=False) # twitter picture url, name, etc.
RelativeUserScore
'''Denormalization for quickly generated relative leaderboards'''
# key id => same as their User id, ie, user_kdsgj326, so that we can quickly
# retrieve the object for each user
follower_ids = ndb.StringProperty(indexed=True, repeated=True)
# misc properties for the user's score, name, etc. needed for leaderboard
我认为这个问题没有必要,但为了以防万一,here 是导致此设计的更详细的讨论。
任务
后台线程接收 Twitter 身份验证数据并通过 tweepy 从 Twitter API 请求一大块朋友 ID。 Twitter 默认发送最多 5000 个朋友 ID,如果可以避免的话,我宁愿不任意限制更多(你每分钟只能向他们 API 发出这么多请求)。
获得朋友 ID 列表后,我可以轻松地将其转换为 "tw:" AuthAccount 密钥 ID,并使用 get_multi 检索 AuthAccount。然后我删除所有不在我们系统中的 Twitter 用户的 Null 帐户,并获取我们系统中的 Twitter 好友的所有用户 ID。这些 id 也是 RelativeUserScores 的键,所以我使用一堆 transactional_tasklets 将此用户的 ID 添加到 RelativeUserScore 的关注者列表中。
优化问题
- 发生的第一件事是调用 Twitter 的 API。鉴于任务中的其他所有内容都需要这样做,我假设我不会从异步中获得任何收益,对吗? (GAE 已经足够聪明,可以在这个阻塞时使用服务器来处理其他任务?)
判断twitter好友是否在玩我们的游戏时,我目前将所有twitter好友id转换为auth账号id,通过get_multi获取。鉴于此数据稀疏(大多数 Twitter 朋友很可能不会玩我们的游戏),我是否最好使用直接检索用户 ID 的投影查询?像...
twitter_friend_ids = twitter_api.friend_ids() # potentially 5000 values friend_system_ids = AuthAccount\ .query(AuthAccount.auth_id.IN(twitter_friend_ids))\ .fetch(projection=[AuthAccount.user_id])
(我不记得或找不到位置,但我读到这个更好,因为您不会浪费时间尝试读取不存在的模型对象
- 无论我最终使用 get_multi 还是投影查询,将请求分解为多个异步查询而不是尝试一次获取/查询可能的 5000 个对象是否有任何好处?
我会这样组织任务:
- 对 Twitter 提要进行异步提取调用
- Use memcache 保存所有 AuthAccount->User 数据:
- 从 memcache 请求数据,如果不存在则调用
fetch_async()
调用AuthAccount
来填充 memcache 和本地字典
- 从 memcache 请求数据,如果不存在则调用
- 运行 通过字典的每个推特 ID
下面是一些示例代码:
future = twitter_api.friend_ids() # make this asynchronous
auth_users = memcache.get('auth_users')
if auth_users is None:
auth_accounts = AuthAccount.query()
.fetch(projection=[AuthAccount.auth_id,
AuthAccount.user_id])
auth_users = dict([(a.auth_id, a.user_id) for a in auth_accounts])
memcache.add('auth_users', auth_users, 60)
twitter_friend_ids = future.get_result() # get async twitter results
friend_system_ids = []
for id in twitter_friend_ids:
friend_id = auth_users.get("tw:%s" % id)
if friend_id:
friend_system_ids.append(friend_id)
这针对相对较少的用户和较高的请求率进行了优化。您上面的评论表明用户数量较多,请求率较低,因此我只会对您的代码进行此更改:
twitter_friend_ids = twitter_api.friend_ids() # potentially 5000 values
auth_account_keys = [ndb.Key("AuthAccount", "tw:%s" % id) for id in twitter_friend_ids]
friend_system_ids = filter(None, ndb.get_multi(auth_account_keys))
当使用 get_multi()
键时,这将使用 ndb 的内置内存缓存来保存数据。