有没有办法让MySQL告诉我我目前是否在交易中?我在命令行mysql客户端的交互式会话中,我已经打开并关闭了几个事务,现在我不应该在事务中,但它的行为就好像我可能。那么如何检查/验证我的连接状态?我试过运气并输入SHOW TRANSACTION
,但没有这样的事情。
尽职调查:
我已经查看了其他问题(当然还有transaction documentation),但没有找到答案。 This question是关于在删除连接后恢复事务。 This one似乎在问其他线程中是否有活动的事务。我想知道我的连接是否在交易中。
我也按照建议here尝试SELECT @@AUTOCOMMIT FROM DUAL
。但它没有帮助:当我开始一个事务时,它的值不会改变,但保持1
(“启用自动提交”)。
答案 0 :(得分:2)
information_schema
。innodb_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_trx
或SIGNAL
,那么下面对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 ;