提高SQLAlchemy更新效率

时间:2017-01-31 19:24:49

标签: python performance session sqlalchemy

我有两个表,用户(~200.000)和推文(~2.000.000) 我需要更新所有用户,包括推文的数量,收藏(他们的推文),回复,转推。 这是在一个脚本中:

@classmethod
def get_user_tweet_counts(cls, user_id):
    return (db_session
        .query(
            func.sum(Tweet.favorite_count).label('favorite_count'),
            func.sum(Tweet.retweet_count).label('retweet_count'),
            func.sum(Tweet.reply_count).label('reply_count'),
            func.count(Tweet.id).label('tweet_count'))
        .filter(Tweet.user_id == user_id)
        .group_by(Tweet.user_id).first())  # This will always be one result, should I query differently?

db_session:

engine = create_engine('postgresql://tweetsql:tweetsql@127.0.0.1/tweetsql')
db_session = scoped_session(sessionmaker(autocommit=False,
                                         autoflush=True,
                                         bind=engine))
Base = declarative_base()
Base.query = db_session.query_property()

10分钟循环:

for user in all_users:
    update_count += 1
    aggregation_result = Tweet.get_user_tweet_counts(user.id)
    user.total_tweet_favourites = aggregation_result[0] or 0
    user.total_tweet_retweets = aggregation_result[1] or 0
    user.total_tweet_replies = aggregation_result[2] or 0
    user.tweet_count = aggregation_result[3] or 0
User.save()  # this just calls db_session.commit()
# We only commit the session once to speed things up

用户和推文被声明为:

User(Base),Tweet(Base)(来自db_session片段)。

当它运行时,python命中80%的cpu和~600mb的内存。我怎样才能做得更好? Tweet在user_id及其自己的id上有索引。

1 个答案:

答案 0 :(得分:1)

Here是SQLAlchemy的作者的一个很好的答案。基本上,如果您需要扩展到大量行,您将想要绕过ORM。

在您的特定情况下,您可以编写单个查询以使用SQL聚合实现相同的结果:

UPDATE users SET
  total_tweet_favourites = aggregated.total_tweet_favourites,
  total_tweet_retweets = aggregated.total_tweet_retweets,
  total_tweet_replies = aggregated.total_tweet_replies,
  tweet_count = aggregated.tweet_count
FROM (
  SELECT
    users.id AS id,
    SUM(tweets.favorite_count) AS total_tweet_favourites,
    SUM(tweets.retweet_count) AS total_tweet_retweets,
    SUM(tweets.reply_count) AS total_tweet_replies,
    COUNT(tweets.id) AS tweet_count
  FROM users JOIN tweets ON tweets.user_id = users.id
  GROUP BY users.id
) aggregated
WHERE users.id = aggregated.id;

将其翻译为SQLAlchemy:

aggregated = session \
    .query(
        User.id.label("id"),
        func.sum(Tweet.favorite_count).label("total_tweet_favourites"),
        func.sum(Tweet.retweet_count).label("total_tweet_retweets"),
        func.sum(Tweet.reply_count).label("total_tweet_replies"),
        func.count(Tweet.id).label("tweet_count")) \
    .select_from(User) \
    .join(Tweet) \
    .group_by(User.id) \
    .subquery() \
    .alias("aggregated")
query = User.__table__ \
    .update() \
    .values(
        total_tweet_favourites=aggregated.c.total_tweet_favourites,
        total_tweet_retweets=aggregated.c.total_tweet_retweets,
        total_tweet_replies=aggregated.c.total_tweet_replies,
        tweet_count=aggregated.c.tweet_count) \
    .where(User.__table__.c.id == aggregated.c.id)
session.execute(query)