数据库事务如何与应用程序代码一起工作

How database Transaction work with application code

假设我们有以下伪 python 代码来处理 Mysql:

with connection.cursor() as cursor:
    sql = "SELECT `count` FROM `sometable` WHERE `name`=%s"
    cursor.execute(sql, ('somename',))
    result = cursor.fetchone()
    if result['count'] == 1:
        sql = "UPDATE `sometable` SET `count`=%d WHERE `name`=%s"
        cursor.execute(sql, (10, 'somename'))

connection.commit()

我知道事务将提供一个 "all-or-nothing" 命题,但不知道如果与应用程序代码混合它是如何工作的。数据库事务如何处理应用程序代码,如:if result['count'] == 1.

在我的理解中,提交时,所有sqls 将被处理到数据库服务器执行,服务器将确保sqls 执行"all-or-nothing"。但是上面的代码应该只在 count 等于 1 时执行 UPDATE sql。如果所有 sql 都已处理到数据库服务器并且其他一些事务只是更改 count 会发生什么?

事务包括对数据库所做的任何更改。如果应用程序代码测试阻止进行更改,则它们不是事务的一部分。因此,如果 result['count'] 不等于 1,则没有进行任何更改,因为您从未向数据库发送 UPDATE 命令。

将事务视为 'todo list' 您交给数据库,然后 commit 告诉数据库去执行该列表。您的 if 测试只是控制您是否在该列表上写下更新。数据库不必关心你是如何写列表的

如果您正在寻找方法来防止 UPDATE 在另一个事务也并行更新数据库 时发生,那么您需要看看进入锁定策略(创建排他锁以强制整个事务在一个系列中发生)或检测该行在具有乐观锁定的事务期间已被更改。参见 Do database transactions prevent race conditions?

对于你的具体情况,后者看起来像这样:

with connection.cursor() as cursor:
    sql = "SELECT `count` FROM `sometable` WHERE `name`=%s"
    cursor.execute(sql, ('somename',))
    result = cursor.fetchone()
    if result['count'] == 1:
        sql = "UPDATE `sometable` SET `count`=%d WHERE `name`=%s and `count`=1"
        cursor.execute(sql, (10, 'somename'))
        if cursor.rowcount != 1:
            # another transaction updated the row already. Handle this gracefully.
            # you could elect to roll back here.

如果具有匹配 name 列的行已更改为具有不同的 count 值,则 UPDATE 之后的 rowcount 将为 0;毕竟,我们选择的是 <code>count=1。