- Java Web开发过程经常需要在数据库服务端写sql过程语言进行复杂的业务处理
- 那么Spring Transactional注解开启的事务同存储过程使用的事务是怎么样的呢?是否是同一个事务还是不同的事务?
- 以下代码使用的是MySQL
- Spring Tranactional开启的事务中调用存储过程:
@Override
@Transactional
public void method() {
AreaPo areaPo = new AreaPo()
.setName("zhangning");
//调用存储过程
areaService.method();
//int zero = 1 / 0;
areaMapper.insert(areaPo);
areaMapper.insert(areaPo);
//开启新的事务
areaService.method2();
System.out.println("----------------------");
}
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void method2() {
areaMapper.insert(new AreaPo().setName("jk"));
try {
//之所以睡眠是为了观察数据库事务表的数据
Thread.sleep(10_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
- areaService.method() 调用存储过程
<insert id="callProcedure">
call addProcess()
</insert>
create
definer = root@localhost procedure addProcess()
begin
declare i int default 0;
# start transaction;
while i < 100000 do
insert into demo.area_po(name) values ('lisi');
set i=i+1;
end while;
# commit ;
end;
- 测试过程及结论:
- method方法抛出异常 存储过程不开启事务 存储过程会采用Spring开启的事务执行 异常会导致回滚存储过程执行的代码
# INSERT INTO information_schema.INNODB_TRX (trx_id, trx_state, trx_started, trx_requested_lock_id, trx_wait_started,
# trx_weight, trx_mysql_thread_id, trx_query, trx_operation_state,
# trx_tables_in_use, trx_tables_locked, trx_lock_structs,
# trx_lock_memory_bytes, trx_rows_locked, trx_rows_modified,
# trx_concurrency_tickets, trx_isolation_level, trx_unique_checks,
# trx_foreign_key_checks, trx_last_foreign_key_error,
# trx_adaptive_hash_latched, trx_adaptive_hash_timeout, trx_is_read_only,
# trx_autocommit_non_locking, trx_schedule_weight)
# VALUES (19011, 'RUNNING', '2022-01-24 13:36:17', null, null, 30669, 2962, 'call addProcess()', null, 0, 1, 1, 1128, 0,
# 30668, 0, 'REPEATABLE READ', 1, 1, null, 0, 0, 0, 0, null);
# INSERT INTO information_schema.INNODB_TRX (trx_id, trx_state, trx_started, trx_requested_lock_id, trx_wait_started,
# trx_weight, trx_mysql_thread_id, trx_query, trx_operation_state,
# trx_tables_in_use, trx_tables_locked, trx_lock_structs,
# trx_lock_memory_bytes, trx_rows_locked, trx_rows_modified,
# trx_concurrency_tickets, trx_isolation_level, trx_unique_checks,
# trx_foreign_key_checks, trx_last_foreign_key_error,
# trx_adaptive_hash_latched, trx_adaptive_hash_timeout, trx_is_read_only,
# trx_autocommit_non_locking, trx_schedule_weight)
# VALUES (19011, 'RUNNING', '2022-01-24 13:36:17', null, null, 100002, 2962, null, null, 0, 1, 1, 1128, 0, 100001, 0,
# 'REPEATABLE READ', 1, 1, null, 0, 0, 0, 0, null);
事务Id是同一个 也就是使用同一个事务
- method方法抛出异常 存储过程开启事务 存储过程不会采用Spring开启的事务执行 异常不会导致回滚存储过程执行的代码
select *
from INFORMATION_SCHEMA.INNODB_TRX;
# INSERT INTO information_schema.INNODB_TRX (trx_id, trx_state, trx_started, trx_requested_lock_id, trx_wait_started,
# trx_weight, trx_mysql_thread_id, trx_query, trx_operation_state,
# trx_tables_in_use, trx_tables_locked, trx_lock_structs,
# trx_lock_memory_bytes, trx_rows_locked, trx_rows_modified,
# trx_concurrency_tickets, trx_isolation_level, trx_unique_checks,
# trx_foreign_key_checks, trx_last_foreign_key_error,
# trx_adaptive_hash_latched, trx_adaptive_hash_timeout, trx_is_read_only,
# trx_autocommit_non_locking, trx_schedule_weight)
# VALUES (19002, 'RUNNING', '2022-01-24 13:34:31', null, null, 43240, 2950,
# 'insert into demo.area_po(name) values (''lisi'')', null, 1, 1, 1, 1128, 0, 43239, 0, 'REPEATABLE READ', 1, 1,
# null, 0, 0, 0, 0, null);
# INSERT INTO information_schema.INNODB_TRX (trx_id, trx_state, trx_started, trx_requested_lock_id, trx_wait_started,
# trx_weight, trx_mysql_thread_id, trx_query, trx_operation_state,
# trx_tables_in_use, trx_tables_locked, trx_lock_structs,
# trx_lock_memory_bytes, trx_rows_locked, trx_rows_modified,
# trx_concurrency_tickets, trx_isolation_level, trx_unique_checks,
# trx_foreign_key_checks, trx_last_foreign_key_error,
# trx_adaptive_hash_latched, trx_adaptive_hash_timeout, trx_is_read_only,
# trx_autocommit_non_locking, trx_schedule_weight)
# VALUES (19007, 'RUNNING', '2022-01-24 13:34:49', null, null, 2, 2950, null, null, 0, 1, 1, 1128, 0, 1, 0,
# 'REPEATABLE READ', 1, 1, null, 0, 0, 0, 0, null);
事务Id不是同一个 也就是使用不同的事务
-
method方法不抛出异常 method2事务使用method事务或者使用新的事务 观察DB事务表的数据变化
-
- select *
from INFORMATION_SCHEMA.INNODB_TRX; INNODB_TRX该事务表是对数据库当前事务的真实反映
- select *
拓展:
当前运行的所有事务: SELECT * FROM information_schema.INNODB_TRX;
当前出现的锁:SELECT * FROM information_schema.INNODB_LOCKs;
锁等待的对应关系:SELECT * FROM information_schema.INNODB_LOCK_waits;
PROCESSLIST表记录了每个MySql线程的用户,地址以及操作的db等其他信息:
SELECT * FROM information_schema.PROCESSLIST;
或者
SHOW PROCESSLIST;information_schema.INNODB_TRX表详情
innodb_trx 表的每个字段解释如下:
trx_id:事务ID。
trx_state:事务状态,有以下几种状态:RUNNING、LOCK WAIT、ROLLING BACK 和 COMMITTING。
trx_started:事务开始时间。
trx_requested_lock_id:事务当前正在等待锁的标识,可以和 INNODB_LOCKS 表 JOIN 以得到更多详细信息。
trx_wait_started:事务开始等待的时间。
trx_weight:事务的权重。
trx_mysql_thread_id:事务线程 ID,可以和 PROCESSLIST 表 JOIN。
trx_query:事务正在执行的 SQL 语句。
trx_operation_state:事务当前操作状态。
trx_tables_in_use:当前事务执行的 SQL 中使用的表的个数。
trx_tables_locked:当前执行 SQL 的行锁数量。
trx_lock_structs:事务保留的锁数量。
trx_lock_memory_bytes:事务锁住的内存大小,单位为 BYTES。
trx_rows_locked:事务锁住的记录数。包含标记为 DELETED,并且已经保存到磁盘但对事务不可见的行。
trx_rows_modified:事务更改的行数。
trx_concurrency_tickets:事务并发票数。
trx_isolation_level:当前事务的隔离级别。
trx_unique_checks:是否打开唯一性检查的标识。
trx_foreign_key_checks:是否打开外键检查的标识。
trx_last_foreign_key_error:最后一次的外键错误信息。
trx_adaptive_hash_latched:自适应散列索引是否被当前事务锁住的标识。
trx_adaptive_hash_timeout:是否立刻放弃为自适应散列索引搜索 LATCH 的标识。
- information_schema.INNODB_LOCKs表详情
innodb_locks 表的每个字段解释如下:
lock_id:锁 ID。
lock_trx_id:拥有锁的事务 ID。可以和 INNODB_TRX 表 JOIN 得到事务的详细信息。
lock_mode:锁的模式。有如下锁类型:行级锁包括:S、X、IS、IX,分别代表:共享锁、排它锁、意向共享锁、意向排它锁。表级锁包括:S_GAP、X_GAP、IS_GAP、IX_GAP 和 AUTO_INC,分别代表共享间隙锁、排它间隙锁、意向共享间隙锁、意向排它间隙锁和自动递增锁。
lock_type:锁的类型。RECORD 代表行级锁,TABLE 代表表级锁。
lock_table:被锁定的或者包含锁定记录的表的名称。
lock_index:当 LOCK_TYPE=’RECORD’ 时,表示索引的名称;否则为 NULL。
lock_space:当 LOCK_TYPE=’RECORD’ 时,表示锁定行的表空间 ID;否则为 NULL。
lock_page:当 LOCK_TYPE=’RECORD’ 时,表示锁定行的页号;否则为 NULL。
lock_rec:当 LOCK_TYPE=’RECORD’ 时,表示一堆页面中锁定行的数量,亦即被锁定的记录号;否则为 NULL。
lock_data:当 LOCK_TYPE=’RECORD’ 时,表示锁定行的主键;否则为NULL。
- information_schema.INNODB_LOCK_waits表详情
innodb_lock_waits 表的每个字段解释如下:
requesting_trx_id:请求事务的 ID。
requested_lock_id:事务所等待的锁定的 ID。可以和 INNODB_LOCKS 表 JOIN。
blocking_trx_id:阻塞事务的 ID。
blocking_lock_id:某一事务的锁的 ID,该事务阻塞了另一事务的运行。可以和 INNODB_LOCKS 表 JOIN。
- INFORMATION_SCHEMA.PROCESSLIST表详情
PROCESSLIST 表的每个字段解释如下:
id:标识ID。这与在SHOW PROCESSLIST语句的Id列、Performance Schema threads表的PROCESSLIST_ID列中显示的值类型相同,并由CONNECTION_ID()函数返回
user:发出该语句的mysql用户。
host:发出该语句的客户机的主机名(系统用户除外,没有主机)。
db:默认数据库。
command:线程正在执行的命令的类型。
time:线程处于当前状态的时间(以秒为单位)。
state:指示线程正在执行的操作、事件或状态。
info:线程正在执行的语句,如果没有执行任何语句,则为NULL。
- 事务一直处于RUNNING状态解决办法
1、需要区分的是 Lock wait timeout exceeded 与 Dead Lock 是不一样。
Lock wait timeout exceeded:后提交的事务等待前面处理的事务释放锁,但是在等待的时候超过了mysql的锁等待时间,就会引发这个异常。
Dead Lock:两个事务互相等待对方释放相同资源的锁,从而造成的死循环,就会引发这个异常。
2、还有一个要注意的是 innodb_lock_wait_timeout 与 lock_wait_timeout 也是不一样的。
innodb_lock_wait_timeout:innodb的dml操作的行级锁的等待时间。
lock_wait_timeout:数据结构ddl操作的锁的等待时间。
定位步骤如下:
(1)使用如下语句查看事务,找到状态为RUNNING的记录
SELECT * FROM information_schema.INNODB_TRX;
(2)通过trx_mysql_thread_id去查询information_schema.processlist表,找到执行事务的客户端请求的SQL线程
select * from information_schema.PROCESSLIST WHERE ID = 'xxx';
(3)查看到端口和host以后,再到对应的服务器查看相关的应用和日志
netstat -nlatp | grep 23452
ps -eaf | grep 12059
(4)如果无法定位,此时我们需要从performance_schema表寻找特定线程的信息 查看事件
select * from performance_schema.events_statements_current
(5)根据我们拿到的线程id去查,可以获取到具体的执行sql
select * from performance_schema.events_statements_current
where THREAD_ID in (select THREAD_ID from performance_schema.threads where PROCESSLIST_ID=15844)
思考: 存储过程发生异常应该如何回滚存储过程修改的数据?参考Spring事务控制-存储过程异常事务处理机制