【问题标题】:What is the difference (in MySQL) between transaction-rollback and not commiting?事务回滚和不提交之间有什么区别(在 MySQL 中)?
【发布时间】:2017-06-27 10:13:25
【问题描述】:

我有一个关于 MySQL 和事务的问题。我使用 MySQL 5.7.18、python 3 和 Oracle mysql 连接器 v2.1.4

我不明白两者之间的区别 a) 有一个事务并且——如果发生错误——回滚和 b) 没有事务并且——如果发生错误——根本不提交更改。

两者似乎都给我留下了完全相同的结果(即表中没有条目,请参见下面的代码示例)。这是否与使用 InnoDB 有关——否则结果会有所不同吗?

如果使用事务有什么好处? 1) 我无法回滚已提交的更改并且 2) 我也可以不提交更改(直到我完成我的任务或确定某些查询没有引发任何异常)?

我曾尝试在https://downloads.mysql.com/docs/connector-python-en.a4.pdf 中找到这些问题的答案,但未能找到本质区别。

有人问了一个几乎相同的问题并收到了一些回复,但我认为这些回复实际上并不包含答案:Mysql transaction : commit and rollback 回复的重点是打开多个连接和更改可见性。仅此而已吗?

import mysql.connector

# Connect to MySQL-Server
conn = mysql.connector.connect(user='test', password='blub',
                              host='127.0.0.1', db='my_test')
cursor = conn.cursor(buffered=True)

# This is anyway the default in mysql.connector
# cursor.autocommit = False

sql = """CREATE TABLE IF NOT EXISTS `my_test`.`employees` (
  `emp_no` int(11) NOT NULL AUTO_INCREMENT,
  `first_name` varchar(14) NOT NULL,
  PRIMARY KEY (`emp_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8"""
try:
    cursor.execute(sql)
    conn.commit()
except:
    print("error")

# Arguments on default values
# conn.start_transaction(consistent_snapshot=False,
#         isolation_level=None, readonly=False)

sql = """INSERT INTO `my_test`.`employees`
(`first_name`)
VALUES
(%s);"""

employees = {}
employees["1"] = ["Peter"]
employees["2"] = ["Bruce"]
for employee, value in employees.items():
    cursor.execute(sql, (value[0],))
    print(conn.in_transaction)

# If I do not commit the changes, table is left empty (whether I write
# start_transaction or not)
# If I rollback the changes (without commit first), table is left empty
# (whether I write start_transaction or not)
# If I commit and then rollback, the rollback had no effect (i.e. there are
# values in the table (whether I write start_transaction or not)
conn.commit()
conn.rollback()

非常感谢您提前提供的帮助!我很感激。

【问题讨论】:

    标签: python mysql sql python-3.x transactions


    【解决方案1】:

    我认为没有提交也没有回滚会使事务处于运行状态,在这种状态下它可能仍然持有锁等资源

    【讨论】:

      【解决方案2】:

      好吧,当您调用事务时,您使用哪个数据库并不重要,它会锁定资源(即任何表)直到事务完成或回滚,例如,如果我编写一个事务以将某些内容插入到table test 测试表将被锁定,直到事务完成这可能会导致死锁,因为其他人可能需要该表......您可以自己尝试,只需在第一个实例中打开 mysql 的两个实例,在不提交的情况下运行事务并在第二次尝试在同一张桌子上插入一些东西......它会清除你的疑问

      【讨论】:

        【解决方案3】:

        事务可防止其他查询在您的查询运行时修改数据。此外,事务范围可以包含多个查询 - 因此您可以在发生错误时回滚所有查询,而如果其中一些成功运行并且只有一个查询导致错误,则情况并非如此,在这种情况下您可能会结束像 JLH 所说的那样,得到部分承诺的结果。

        【讨论】:

        • 感谢您的回复。我想我不得不不同意——这正是我困惑的一部分。事务应该将语句“捆绑”成原子的东西。因此,如果我必须运行 3 个连续语句(例如 INSERT INTO)并且第二个语句失败,我不希望我的表/数据库有任何更改(all or nothing)。 但是:如果我只运行这三个语句而不将它们声明为事务并且仅在所有三个语句都成功完成时才提交,就会发生这种情况。
        • 见上面的代码。有两个数据更改语句(cursor.execute)。当且仅当我提交时,两者都适用。我是否明确写 start_transaction 。如果我检查异常/错误并且第二个语句引发异常,我可以避免提交。不会将任何数据写入表中。尽管我不会(明确地?)将语句捆绑为事务,但它们是“相当”原子的。也许我的困惑来自自动提交在 MySQL 中默认为 on 而在 python 的 mysql.connector 中默认为 off
        • 啊——很好的反馈——我想知道是不是这样!发现后请更新评论。关于捆绑 - 事务是很好的做法,因为它是明确的并且不依赖于其他变量(如默认设置、语句数量等) - 虽然在这个简单的示例中可能无关紧要,但在更复杂的情况下可能会出现问题场景!
        • @ThomasE 运气好吗?
        • 我确实对它进行了更多测试。不确定这是否是完整的解释。如果自动提交是on(在上面的代码conn.autocommit = True),则 cursor.execute 会按预期自动提交。因此,与 Python 中的 mysql.connector 类似的行为就像您在 MySQL Workbench 中运行单个 SQL 语句一样。如果 autocommit 是 off,因为它默认使用 mysql.connector,所有 SQL 语句/执行仅在显式提交时提交 (conn.commit())。因此,在捆绑/原子性的意义上,mysql.connector 语句是自动/默认情况下类似于事务的。
        【解决方案4】:

        您决定拥有一个事务应该考虑到拥有一个事务的众多原因,包括拥有多个语句,每个语句都会写入数据库。

        在您的示例中,我认为这没有什么区别,但在更复杂的场景中,您需要一个事务来确保 ACID。

        【讨论】:

          猜你喜欢
          • 2010-09-16
          • 2015-03-25
          • 2012-12-24
          • 2013-11-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-04-06
          • 1970-01-01
          相关资源
          最近更新 更多