【问题标题】:web2py update_or_insert to increment a counterweb2py update_or_insert 增加一个计数器
【发布时间】:2018-09-18 06:05:49
【问题描述】:

我有一个 web2py 定义的数据库表,用于计算特定用户发出请求的次数 - 所以它有整数列“user_id”和“n_req”。我不想为每个可能的用户填充零。相反,我想检查是否存在具有 user_id 的行,如果存在,则将“n_req”加一,否则使用初始值为 1 的“n_req”创建它。为了避免竞争条件,我想做这使用单个 update_or_insert 调用,例如

db.count_table.update(db.count_table.user_id == uid, user_id = uid, n_req = n_req + 1)

但是,我想我不能这样做,因为它在递增时使用n_req 的预先存在的值。那么我如何告诉 DAL n_req 的初始值。例如n_req = (n_req || 0) + 1可以吗?

【问题讨论】:

    标签: sql-update counter sql-insert web2py


    【解决方案1】:

    我认为在这种情况下您将无法使用.update_or_insert 方法,因为n_rec 的值取决于是否找到记录。相反,您可以这样做:

    db.define_table('count_table',
        Field('user_id', 'integer', unique=True),
        Field('n_rec', 'integer', default=1))
    
    def update_count(user_id):
        return db(db.count_table.user_id == user_id).update(n_rec=db.count_table.n_rec + 1)
    
    if not update_count(uid):
        try:
            db.count_table.insert(user_id=uid)
        except db._adapter.driver.IntegrityError:
            update_count(uid)
    

    注意.updaten_rec 的值设置为db.count_table.n_rec + 1,这将转换为SQL 语句,让数据库增加现有值,而不是自己显式提供最终值。这应该可以避免两个请求同时更新计数的竞争条件。

    另外,请注意user_id 字段上有一个unique 约束。这将防止竞争条件允许为同一用户创建两条记录。在这种情况下,try/except 将捕获生成的IntegrityError,并且将对现有记录进行更新。

    【讨论】:

    • 谢谢@anthony。如果在第一个 'db.count_table' 调用和 'insert' 之间另一个进程添加了行会发生什么。我想这会失败,所以希望有一种方法可以原子地做到这一点。
    • 好点。上面的代码将避免更新的竞争条件,但您仍然可以通过初始插入获得竞争条件。为避免该问题,您应该在user_id 字段上放置unique 约束,以防止应用程序为同一用户创建两条记录。然后,您可以将 insert 包装在 try/except 中以捕获违反唯一约束的情况。如果出现此类异常,请再次检查现有记录,并进行更新而不是插入。
    • 我更新了答案以处理插入时的竞争条件。
    • 谢谢@Anthony。可惜这一切都这么复杂!我有点希望有一个简单的单线解决方案。但我想这对你来说是竞争条件。
    猜你喜欢
    • 1970-01-01
    • 2016-02-06
    • 2015-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多