当前位置: 首页 > 面试题库 >

可以使用SQLAlchemy事件更新非规范化数据缓存吗?

漆雕洋
2023-03-14
问题内容

出于性能原因,我有一个非规范化的数据库,其中某些表包含从其他表的许多行聚合而来的数据。我想通过使用SQLAlchemy事件来维护此非规范化数据缓存。举例来说,假设我正在编写论坛软件,并希望每个Thread人都有一个专栏来跟踪线程中所有注释的组合词数,以便有效地显示该信息:

class Thread(Base):
    id = Column(UUID, primary_key=True, default=uuid.uuid4)
    title = Column(UnicodeText(), nullable=False)
    word_count = Column(Integer, nullable=False, default=0)

class Comment(Base):
    id = Column(UUID, primary_key=True, default=uuid.uuid4)
    thread_id = Column(UUID, ForeignKey('thread.id', ondelete='CASCADE'), nullable=False)
    thread = relationship('Thread', backref='comments')
    message = Column(UnicodeText(), nullable=False)

    @property
    def word_count(self):
        return len(self.message.split())

因此,每次插入注释时(为简单起见,我们永远不要编辑或删除注释),我们希望更新word_count关联Thread对象的属性。所以我想做类似的事情

def after_insert(mapper, connection, target):
    thread = target.thread
    thread.word_count = sum(c.word_count for c in thread.comments)
    print "updated cached word count to", thread.word_count

event.listen(Comment, "after_insert", after_insert)

因此,当我插入一个时Comment,我可以看到事件触发并看到它已经正确计算了字数,但是该更改未保存到Thread数据库的行中。我没有在after_insert文档中看到有关更新其他表的任何警告,尽管在其他一些报告中确实看到了一些警告,例如after_delete。

那么,是否有支持的方法来对SQLAlchemy事件执行此操作?我已经将SQLAlchemy事件用于许多其他事情,所以我想以这种方式做所有事情,而不必编写数据库触发器。


问题答案:

after_insert()事件是执行此操作的一种方法,您可能会注意到它传递了一个SQLAlchemyConnection对象,而不是Session其他与刷新相关的事件那样的对象。映射程序级别的刷新事件通常用于直接在给定的条件下调用SQL
Connection

@event.listens_for(Comment, "after_insert")
def after_insert(mapper, connection, target):
    thread_table = Thread.__table__
    thread = target.thread
    connection.execute(
            thread_table.update().
             where(thread_table.c.id==thread.id).
             values(word_count=sum(c.word_count for c in thread.comments))
    )
    print "updated cached word count to", thread.word_count

这里值得注意的是,与再次在整个工作单元过程中运行该属性更改相比,直接调用UPDATE语句的性能要高得多。

但是,这里实际上并不需要诸如after_insert()之类的事件,因为我们知道刷新发生之前“
word_count”的值。实际上,我们知道它是因为Comment和Thread对象彼此关联,并且我们还可以使用属性事件始终保持Thread.word_count在内存中完全新鲜:

def _word_count(msg):
    return len(msg.split())

@event.listens_for(Comment.message, "set")
def set(target, value, oldvalue, initiator):
    if target.thread is not None:
        target.thread.word_count += (_word_count(value) - _word_count(oldvalue))

@event.listens_for(Comment.thread, "set")
def set(target, value, oldvalue, initiator):
    # the new Thread, if any
    if value is not None:
        value.word_count += _word_count(target.message)

    # the old Thread, if any
    if oldvalue is not None:
        oldvalue.word_count -= _word_count(target.message)

此方法的最大优点是,也不需要遍历thread.comments,对于卸载的集合意味着将发出另一个SELECT。

还有一种方法是在before_flush()中进行操作。以下是一个快速而肮脏的版本,可以对其进行完善以更仔细地分析已更改的内容,以确定是否需要更新word_count:

@event.listens_for(Session, "before_flush")
def before_flush(session, flush_context, instances):
    for obj in session.new | session.dirty:
        if isinstance(obj, Thread):
            obj.word_count = sum(c.word_count for c in obj.comments)
        elif isinstance(obj, Comment):
            obj.thread.word_count = sum(c.word_count for c in obj.comments)

我会选择属性事件方法,因为它是性能最高且最新的方法。



 类似资料:
  • 问题内容: 用例说明 我们有一个关系数据库,其中包含有关我们日常运营的数据。目的是允许用户使用全文本搜索引擎来搜索重要数据。数据经过规范化,因此不是进行全文查询的最佳形式,因此,其想法是对数据的一部分进行规范化,然后将其实时复制到Elasticsearch,这使我们能够创建快速而准确的搜索应用程序。 我们已经有了一个启用数据库操作(插入,更新,删除)事件搜索的系统。这些事件仅包含已更改的列和主键(

  • 问题内容: 我正在使用Ruby on Rails 3.0.7和MySQL5。在我的应用程序中,我有两个数据库表,即TABLE1和TABLE2,并且出于性能原因,我对TABLE2中的某些数据进行了非规范化,因此我在该表中重复了TABLE1的值。现在,在TABLE1中,我需要更新一些涉及的值,当然,我还必须在TABLE2中正确地更新非规范化的值。 我该如何以高效的方式更新这些值? 也就是说,如果TAB

  • 非规范化数据不存储规范化的数据。换句话说非规范化意味着相同数据的多个拷贝同时存在。 上一章中,我们在帖子中非规范化评论总数,以避免每次都加载所有的评论。在数据建模意义上说这是冗余的,因为我们可以通过计数每个评论,随时计算出该总数(当不考虑运行速度)。 非规范化通常意味着额外的开发工作。在例子中,我们每次添加或删除评论时,还需要同时更新相关的帖子,以确保 commentsCount 字段保持准确。这

  • 我阅读了cassandra数据建模,除了非规范化数据可能会发生变化之外,一切都很清楚。我如何同步它?当用户电子邮件更改时,更新的方法是什么: < code>groupname是组的一部分,数据模型中的用户可能不知道任何组,因此在用户更改后无法更新电子邮件。 下面描述的解决方案是否合适? 向用户模型中添加一列(类型

  • 问题内容: 我有一个简单的表,其中包含学生编号和相应的教师编号,并且需要对它进行规范化,以输入到旧版系统中。 例如,下面是数据现在的样子: 我希望它看起来像这样,将每个Teacher分成一个单独的列,从左到右填充各列。一个业务规则是,每个学生最多只能有六位老师: 原始表中有10,000多行,因此我需要以编程方式执行此操作。谢谢! 问题答案: 您可以使用数据透视。您还需要“排名”您的老师1-6。请参

  • 问题内容: 好的,我刚开始使用Firebase。我已阅读:https://www.firebase.com/docs/data- structure.html, 并且已阅读:https://www.firebase.com/blog/2013-04-12-denormalizing- is- normal.html 所以我很困惑,因为一个似乎与另一个矛盾。您可以按层次结构组织数据,但是如果您想使其