对于已经执行成功的sql命令,如何回滚

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了对于已经执行成功的sql命令,如何回滚相关的知识,希望对你有一定的参考价值。

当启动Binlog后,事务会产生Binlog Event,这些Event被看做事务数据的一部分。因此要保证事务的Binlog Event和InnoDB引擎中的数据的一致性。所以带Binlog的CrashSafe要求mysql宕机重启后能够保证:

- 所有已经提交的事务的数据仍然存在。

- 所有没有提交的事务的数据自动回滚。

- 所有已经提交了的事务的Binlog Event也仍然存在。

- 所有没有提交事务没有记录Binlog Event。

这些要求很好理解,如果重启后数据还在,但是Binlog Event没有了,就没办法复制到其他节点上了。如果重启后,数据没了,但是Binlog Event还在,那么不存在的数据就会被复制到其他节点上,从而导致主从的不一致。

为了保证带Binlog的CrashSafe,MySQL内部使用的两阶段提交(Two Phase Commit)。

2 - MySQL的Two Phase Commit(2PC)

在开启Binlog后,MySQL内部会自动将普通事务当做一个XA事务来处理:
- 自动为每个事务分配一个唯一的ID
- COMMIT会被自动的分成Prepare和Commit两个阶段。
- Binlog会被当做事务协调者(Transaction Coordinator),Binlog Event会被当做协调者日志。
想了解2PC,可以参考文档:【https://en.wikipedia.org/wiki/Two-phase_commit_protocol。】

- 分布式事务ID(XID)

使用2PC时,MySQL会自动的为每一个事务分配一个ID,叫XID。XID是唯一的,每个事务的XID都不相同。XID会分别被Binlog和InnoDB记入日志中,供恢复时使用。MySQ内部的XID由三部分组成:

- 前缀部分

前缀部分是字符串"MySQLXid"

- Server ID部分

当前MySQL的server_id
- query_id部分

为了保证XID的的唯一性,数字部分使用了query_id。MySQL内部会自动的为每一个语句分配一个query_id,全局唯一。

参考代码:sql/xa。h的struct xid_t结构。

- 事务的协调者Binlog

Binlog在2PC中充当了事务的协调者(Transaction Coordinator)。由Binlog来通知InnoDB引擎来执行prepare,commit或者rollback的步骤。事务提交的整个过程如下:

1. 协调者准备阶段(Prepare Phase)

告诉引擎做Prepare,InnoDB更改事务状态,并将Redo Log刷入磁盘。

2. 协调者提交阶段(Commit Phase)

2.1 记录协调者日志,即Binlog日志。

2.2 告诉引擎做commit。
注意:记录Binlog是在InnoDB引擎Prepare(即Redo Log写入磁盘)之后,这点至关重要。

在MySQ的代码中将协调者叫做tc_log。在MySQL启动时,tc_log将被初始化为mysql_bin_log对象。参考sql/binlog.cc中的init_server_components():
if (opt_bin_log) tc_log= &mysql_bin_log;

而在事务提交时,会依次执行:
tc_log->prepare();
tc_log->commit();
参考代码:sql/binlog.cc中的ha_commit_trans()。当mysql_bin_log是tc_log时,prepare和commit的代码在sql/binlog.cc中:

MYSQL_BIN_LOG::prepare();
MYSQL_BIN_LOG::commit();

-协调者日志Xid_log_event
作为协调者,Binlog需要将事务的XID记入日志,供恢复时使用。Xid_log_event有以下几个特点:
- 仅记录query_id
因为前缀部分不变,server_id已经记录在Event Header中,Xid_log_event中只记录query_id部分。
- 标志事务的结束

在Binlog中相当于一个事务的COMMIT语句。

一个事务在Binlog中看起来时这样的:
Query_log_event("BEGIN");DML产生的events; Xid_log_event;

- DDL没有BEGIN,也没有Xid_log_event 。
- 仅InnoDB的DML会产生Xid_log_event
因为MyISAM不支持2PC所以不能用Xid_log_event ,但会有COMMIT Event。
Query_log_event("BEGIN");DML产生的events;Query_log_event("COMMIT");

问题:Query_log_event("COMMIT")和Xid_log_event 有不同的影响吗?

- Xid_log_event 中的Xid可以帮助master实现CrashSafe。
- Slave的CrashSafe不依赖Xid_log_event
事务在Slave上重做时,会重新产生XID。所以Slave服务器的CrashSafe并不依赖于Xid_log_event 。Xid_log_event 和Query_log_event("COMMIT"),只是作为事务的结尾,告诉Slave Applier去提交这个事务。因此二者在Slave上的影响是一样的。

3 - 恢复(Recovery)
这个机制是如何保证MySQL的CrashSafe的呢,我们来分析一下。这里我们假设用户设置了以下参数来保证可靠性:

- 恢复前事务的状态
在恢复开始前事务有以下几种状态:
- InnoDB中已经提交
根据前面2PC的过程,可知Binlog中也一定记录了该事务的的Events。所以这种事务是一致的不需要处理。
- InnoDB中是prepared状态,Binlog中有该事务的Events。
需要通知InnoDB提交这些事务。
- InnoDB中是prepared状态,Binlog中没有该事务的Events。
因为Binlog还没记录,需要通知InnoDB回滚这些事务。
- Before InnoDB Prepare
事务可能还没执行完,因此InnoDB中的状态还没有prepare。根据2PC的过程,Binlog中也没有该事务的events。 需要通知InnoDB回滚这些事务。

- 恢复过程
从上面的事务状态可以看出:恢复时事务要提交还是回滚,是由Binlog来决定的。
- 事务的Xid_log_event 存在,就要提交。
- 事务的Xid_log_event 不存在,就要回滚。

恢复的过程非常简单:
- 从Binlog中读出所有的Xid_log_event
- 告诉InnoDB提交这些XID的事务
- InnoDB回滚其它的事务
参考技术A mysql命令行下怎样实现数据的回滚操作
在MySQL有时执行了错误的update或者delete时导致大量数据错误恢复的办法。执行时没有开启事务,也没有对数据进行。这时就需要使用到sqlbinlog工具。
sqlbinlog需要开启,具体的打开方法就不说了。

使用sqlbinlog会产生bin文件,恢复就需要用到这些文件。文件中记录着数据库的所有操作。(此方法的操作是将数据库之前所执行的语句重新执行一次,以达到恢复效果)
具体步骤:1,先找到bin文件,一般都是在mysql的data文件夹中,结尾以.00000X等形式结束。
2,寻找需要还原的时间点 使用语句 mysqlbinlog 文件名 例(MySQLbinlog xxbin.000001)来查看内容,然后找到对应的具体时间
3,导出sql语句,使用语句 mysqlbinlog 文件名>sql文件路径 例(mysqlbinlog xxxbin,00001>>a.sql | mysql -u root -p )
如果需要指定时间导出--start--date -stop='' --date='' 来导出指定时间执行的语句例(sqlbinlog --start-stop='2015-11-22 10:00:00' xxbin.000001>a.sql | mysql -u root -p )这句意思是导出在2015-11-22 10点之前的语句,反之start是导出时间之后的。 start和stop可以同时使用。
如果存在多个bin文件,则按照需要导出。
4,使用mysql将导出的语句执行一次。

如何在 SQL Server 2005 中回滚 UPDATE 查询?

【中文标题】如何在 SQL Server 2005 中回滚 UPDATE 查询?【英文标题】:How can I rollback an UPDATE query in SQL server 2005? 【发布时间】:2010-10-17 19:31:50 【问题描述】:

我需要在 SQL 中执行此操作,而不是通过代码。

【问题讨论】:

更新是什么意思?你能在这个问题上再深入一点吗? 我更新了一个表,这是我的严重错误..而不是更新单行..我的粗心已经更新了所有行...我如何回滚这个....请帮助。 .... 【参考方案1】:

在这个例子中,我们在查询中运行 2 行插入,如果它们都为真,则运行,但如果不是,则运行任何东西并回滚

DECLARE @rowcount int  set @rowcount = 0 ; 
BEGIN TRANSACTION [Tran1]
BEGIN TRY 
 insert into [database].[dbo].[tbl1] (fld1) values('1') ;
    set @rowcount = (@rowcount + @@ROWCOUNT); 
 insert into [database].[dbo].[tbl2] (fld1) values('2') ;
    set @rowcount = (@rowcount + @@ROWCOUNT); 

IF @rowcount =  2
  COMMIT TRANSACTION[Tran1]
ELSE
  ROLLBACK TRANSACTION[Tran1]
END TRY
  BEGIN CATCH
  ROLLBACK TRANSACTION[Tran1]
END CATCH

【讨论】:

【参考方案2】:

您可以为此使用隐式事务

SET IMPLICIT_TRANSACTIONS ON

update Staff set staff_Name='jas' where staff_id=7

ROLLBACK

根据您的要求 - 您可以通过将存储过程设置为启动过程来从存储过程中设置此设置 (SET IMPLICIT_TRANSACTIONS ON)。

SET IMPLICIT TRANSACTION ON 命令是特定于连接的。因此,除了运行启动存储过程的连接之外的任何连接都不会受益于您设置的设置。

【讨论】:

【参考方案3】:

简单易做:

标题代码...

Set objMyConn = New ADODB.Connection

Set objMyCmd = New ADODB.Command Set

objMyRecordset = New ADODB.Recordset

On Error GoTo ERRORHAND 

工作代码...

objMyConn.ConnectionString = ConnStr

objMyConn.Open 

代码....

'从 Excel 复制数据'

objMyConn.BeginTrans <-- define transactions to possible be rolled back 

For NewRows = 2 To Rows

objMyRecordset.AddNew 

For NewColumns = 0 To Columns - 1

objMyRecordset.Fields(NewColumns).Value = ActiveSheet.Cells(NewRows, NewColumns + 1)

Next NewColumns objMyRecordset.Update Next NewRows

objMyConn.CommitTrans <- if success, commit them to DB

objMyConn.Close

错误手:

Success = False 

objMyConn.RollbackTrans <-- here we roll back if error encountered somewhere

LogMessage = "ERROR writing database: " & Err.Description

...

【讨论】:

【参考方案4】:

试试

ROLLBACK WORK;

它通常有效

【讨论】:

【参考方案5】:

如前所述,除了从备份恢复之外,您无能为力。至少现在您将学会始终将语句包装在事务中,以查看在您决定提交之前会发生什么。此外,如果您没有数据库备份,这也将教您定期备份数据库。

虽然我们对您的紧迫问题没有太大帮助...但希望这些答案将确保您将来不会再次遇到此问题。

【讨论】:

【参考方案6】:

你需要这个工具,你可以找到交易并撤销它。

ApexSQL Log

【讨论】:

“哎呀——希望您可以在没有 where 子句的情况下回滚该 Update 语句!?ApexSQL Log 能够回滚 SQL Server 数据库的事务。”很棒的路线! 收费看起来不错,但花了 2000 美元【参考方案7】:

根据您指定的信息,您最好的恢复机会是通过数据库备份。我认为您将无法回滚您推动的任何更改,因为您当时显然没有使用事务。

【讨论】:

【参考方案8】:

一旦提交更新,您就不能只回滚单个更新。最好的办法是回滚到数据库的先前备份。

【讨论】:

【参考方案9】:
begin transaction

// execute SQL code here

rollback transaction

如果您已经执行了查询并想要回滚它,那么不幸的是,您唯一真正的选择是恢复数据库备份。如果您使用的是完整备份,那么您应该能够将数据库恢复到特定时间点。

【讨论】:

完整备份是什么意思,我应该每天备份数据库吗?我是新手?【参考方案10】:

您可以回滚您在事务中执行的语句。 与其提交事务,不如回滚事务。

如果您更新了某些内容并想要回滚这些更新,并且您还没有在(尚未提交的)事务中完成此操作,那么我认为这只是运气......

(手动修复,或恢复备份)

【讨论】:

以上是关于对于已经执行成功的sql命令,如何回滚的主要内容,如果未能解决你的问题,请参考以下文章

sql 回滚语句

事务 回滚

MySQL误操作如何快速回滚?

如何在SQL Server 2005中回滚UPDATE查询?

如何从事务日志执行 SQL 回滚

使用JDBC进行数据库的事务操作