在mysql中显示当前活动的事务

时间:2016-05-20 11:31:18

标签: mysql transactions

有没有办法让MySQL告诉我我目前是否在交易中?我在命令行mysql客户端的交互式会话中,我已经打开并关闭了几个事务,现在我不应该在事务中,但它的行为就好像我可能。那么如何检查/验证我的连接状态?我试过运气并输入SHOW TRANSACTION,但没有这样的事情。

尽职调查:

我已经查看了其他问题(当然还有transaction documentation),但没有找到答案。 This question是关于在删除连接后恢复事务。 This one似乎在问其他线程中是否有活动的事务。我想知道我的连接是否在交易中。

我也按照建议here尝试SELECT @@AUTOCOMMIT FROM DUAL。但它没有帮助:当我开始一个事务时,它的值不会改变,但保持1(“启用自动提交”)。

3 个答案:

答案 0 :(得分:2)

information_schemainnodb_trx 告诉您是否在InnoDB 内的交易中。问题在于,如果您尚未显式访问任何表或创建读取快照,那么您只能在事务在MySQL内部(“服务器层”)而不在InnoDB内部( “存储引擎层”)。

mysql> SELECT count(1) FROM information_schema.innodb_trx 
    -> WHERE trx_mysql_thread_id = CONNECTION_ID();
+----------+
| count(1) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)

mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)

好的,我之前没有,但现在我有一笔交易,而且......

mysql> SELECT count(1) FROM information_schema.innodb_trx 
    -> WHERE trx_mysql_thread_id = CONNECTION_ID();
+----------+
| count(1) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)

...对于我在innodb_trx中的当前CONNECTION_ID(),仍然没有任何内容。

但是,如果我写信或只是从InnoDB表中读取......

mysql> SELECT COUNT(1) FROM t1;
+----------+
| COUNT(1) |
+----------+
|      301 |
+----------+
1 row in set (0.00 sec)

...现在,我可以看到我的交易,因为InnoDB意识到了这一点。

mysql> SELECT count(1) FROM information_schema.innodb_trx 
    -> WHERE trx_mysql_thread_id = CONNECTION_ID();
+----------+
| count(1) |
+----------+
|        1 |
+----------+
1 row in set (0.00 sec)

mysql> ROLLBACK;
Query OK, 0 rows affected (0.00 sec)

让我们确认它已经消失了......

mysql> SELECT count(1) FROM information_schema.innodb_trx 
    -> WHERE trx_mysql_thread_id = CONNECTION_ID();
+----------+
| count(1) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)

现在,告诉服务器告诉存储引擎我的MVCC视图现在开始,而不是稍后:

mysql> START TRANSACTION WITH CONSISTENT SNAPSHOT;
Query OK, 0 rows affected (0.00 sec)

请注意,除非我的隔离级别允许,否则这实际上并不会给我一个“一致”的快照。但InnoDB现在知道我在这里已经足够了。

mysql> SELECT count(1) FROM information_schema.innodb_trx 
    -> WHERE trx_mysql_thread_id = CONNECTION_ID();
+----------+
| count(1) |
+----------+
|        1 |
+----------+
1 row in set (0.00 sec)

...并且InnoDB立即了解该交易。

现在,还有另一种方法可以确定您是否正在进行交易。或者,更准确地说,我应该说现在还有另一种方法可以确定您在交易中

我将此用于必须在事务中运行的存储过程 - 调用者负责启动和提交或回滚,如果没有活动事务,则该过程将拒绝运行。怎么样?

如果我有事务,该过程会调用另一个静默成功的过程,但如果不这样做则抛出异常。当一个过程调用第二个过程,并且第二个过程抛出异常时,第一个过程以相同的异常终止,除非第一个过程安装了HANDLER来捕获错误。

因此,当我的外部过程调用此过程时,如果事务处于活动状态,则不会发生任何事情,并且允许外部过程运行:

mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)

mysql> CALL mysql.require_transaction;
Query OK, 0 rows affected (0.00 sec)

^^^这个^^^是我在开头附近做的,在我的存储过程中,只有在事务中调用它们才需要运行。

没有错误,我们在交易中。如果这是另一个调用它的过程,那么该过程将继续执行下一条指令。

但是如果我们调用我的require_transaction程序并且我们不在交易中:

mysql> ROLLBACK;
Query OK, 0 rows affected (0.00 sec)

mysql> CALL mysql.require_transaction;
ERROR 1644 (42000): you must have an active database transaction before attempting
this operation

纯。我们通过自定义错误消息使调用者崩溃。怎么样?

DELIMITER $$
CREATE PROCEDURE `mysql`.`require_transaction`()
BEGIN

-- test the session's transactional status, 
-- throwing an exception if we aren't in a transaction,
-- but finishing successfully if we are

DECLARE CONTINUE HANDLER 
        FOR 1305 
        SIGNAL SQLSTATE '42000' 
        SET MESSAGE_TEXT = 'you must have an active database transaction before attempting this operation';

SAVEPOINT `we created to be sure you were in a transaction`;
ROLLBACK TO SAVEPOINT `we created to be sure you were in a transaction`;

END $$
DELIMITER ;

这是我长期以来的解决方法,因为我认为这是对MySQL设计的重大疏忽 - 显然无法从SQL界面确定您当前是否正在进行交易。这就是为什么这样做的原因:

  • 创建SAVEPOINT并立即回滚到它本质上是一个无操作。只要没有一个具有相同名称的活动保存点,没有任何伤害,没有犯规。我为we created to be sure you were in a transaction使用了极不可能的名称SAVEPOINT

  • 如果您不在事务中,则无法创建SAVEPOINT,但实际上这无效。

  • 回滚到不存在的SAVEPOINT将抛出错误1305,因此如果您不在事务中,则不会创建它,现在它将不存在,并且这是你的错误。如果您正在进行交易,则会创建SAVEPOINT然后将其释放,从而使您的交易保持原样。

mysql> ROLLBACK TO SAVEPOINT `we created to be sure you were in a transaction`;
ERROR 1305 (42000): SAVEPOINT we created to be sure you were in a transaction does not exist
mysql>
哈哈哈哈,这是一个漂亮的黑客。现在你明白为什么我使用我为我的虚假保存点所做的名字 - “不存在”被附加到对象名称,以形成错误信息。

在没有SIGNAL的MySQL 5.1上,我的require_transaction存储过程只是终止于那个几乎有意义的本机错误...或者至少足够有意义,有人会问DBA(我)这意味着什么。

为了让它更漂亮,在MySQL Server 5.5及更高版本中,我们使用CONTINUE HANDLER捕获错误1305,允许我们使用SIGNAL设置我们自己的自定义错误消息。

设置然后立即回滚到保存点是一种可悲的hacky但是可靠的方法来确定您是否在交易中。

答案 1 :(得分:1)

您是否尝试使用22.31.4 The INFORMATION_SCHEMA INNODB_TRX Table

SELECT
  COUNT(`trx_id`) `inTransaction?`
FROM
  `INFORMATION_SCHEMA`.`INNODB_TRX`
WHERE
  `trx_mysql_thread_id` = CONNECTION_ID();

答案 2 :(得分:0)

如果您像我一样,被众神诅咒,无法在一个旧系统上工作,而该旧系统却没有innodb_trxSIGNAL,那么下面对Michael-sqlbot过程的修改可能对您有用(nb:由于某种原因,它不能是函数,必须是过程)。

SET @saved_cs_client      = @@character_set_client;
SET @saved_cs_results     = @@character_set_results;
SET @saved_col_connection = @@collation_connection;
SET character_set_client  = utf8;
SET character_set_results = utf8;
SET collation_connection  = utf8_general_ci;
SET @saved_sql_mode       = @@sql_mode;
SET sql_mode              = 'STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ALLOW_INVALID_DATES,ERROR_FOR_DIVISION_BY_ZERO,TRADITIONAL,NO_AUTO_CREATE_USER';

DROP PROCEDURE IF EXISTS `sp_is_in_transaction`;
DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `sp_is_in_transaction`(
OUT is_in_transaction TINYINT
)
BEGIN

DECLARE CONTINUE HANDLER FOR SQLEXCEPTION # 1305
    BEGIN
        SET is_in_transaction = 0 ; # on error realize we are NOT in a transaction
    END;

SET is_in_transaction = 1 ;
SAVEPOINT `savepoint_sp_is_in_transaction`;
ROLLBACK TO SAVEPOINT `savepoint_sp_is_in_transaction`;

END $$
DELIMITER ;

SET sql_mode              = @saved_sql_mode;
SET character_set_client  = @saved_cs_client;
SET character_set_results = @saved_cs_results;
SET collation_connection  = @saved_col_connection;

测试块

call sp_is_in_transaction( @in_trans );
select @in_trans ;

start transaction ;
call sp_is_in_transaction( @in_trans );
select @in_trans ;
rollback ;

call sp_is_in_transaction( @in_trans );
select @in_trans ;