【Propagation.NOT_SUPPORTED使用不当会锁超时?】

写在前面

欢迎交流指教

经测试发现在特殊情况下,事务方法通过依赖注入的方式调用非事务方法(@Transactional(propagation = Propagation.NOT_SUPPORTED)),会造成锁超时。

主流程截图

在这里插入图片描述

代码执行异常

2024-06-28T00:54:00.652+08:00 ERROR 3784 --- [nio-8080-exec-7] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.dao.CannotAcquireLockException: 
### Error updating database.  Cause: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction
### The error may exist in com/gyg/demo1/mapper/OrderItemsMapper.java (best guess)
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### SQL: DELETE FROM order_items      WHERE  (order_id = ?)
### Cause: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction
; Lock wait timeout exceeded; try restarting transaction] with root cause

com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:124) ~[mysql-connector-j-8.0.33.jar:8.0.33]
	at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122) ~[mysql-connector-j-8.0.33.jar:8.0.33]

数据库分析

select * from information_schema.innodb_trx;
SELECT * FROM sys.innodb_lock_waits;

通过innodb_trx可以看到有两个事务,WTF。。。。
1、innodb_trx结果显示 6231是delete方法的事务,6228是整体事务,事务6228锁住了这两条记录,事务6231想要对一行记录加锁,但是和之前的事务部分重合了导致等待锁(不会释放,已经死锁了,事务6228不会提交也不胡回滚,导致会获取锁超时)
在这里插入图片描述
在这里插入图片描述

{
  "trx_id": 6231,
  "trx_state": "LOCK WAIT",
  "trx_started": "2024-06-27 17:22:32",
  "trx_requested_lock_id": "140081923306280:5:4:2:140081834817296",
  "trx_wait_started": "2024-06-27 17:22:32",
  "trx_weight": 2,
  "trx_mysql_thread_id": 173,
  "trx_query": "DELETE FROM order_items WHERE (order_id = 1854)",
  "trx_operation_state": "starting index read",
  "trx_tables_in_use": 1,
  "trx_tables_locked": 1,
  "trx_lock_structs": 2,
  "trx_lock_memory_bytes": 1128,
  "trx_rows_locked": 1,
  "trx_rows_modified": 0,
  "trx_concurrency_tickets": 0,
  "trx_isolation_level": "REPEATABLE READ",
  "trx_unique_checks": 1,
  "trx_foreign_key_checks": 1,
  "trx_last_foreign_key_error": null,
  "trx_adaptive_hash_latched": 0,
  "trx_adaptive_hash_timeout": 0,
  "trx_is_read_only": 0,
  "trx_autocommit_non_locking": 0,
  "trx_schedule_weight": 1
},
{
  "trx_id": 6228,
  "trx_state": "RUNNING",
  "trx_started": "2024-06-27 17:22:32",
  "trx_requested_lock_id": null,
  "trx_wait_started": null,
  "trx_weight": 5,
  "trx_mysql_thread_id": 172,
  "trx_query": null,
  "trx_operation_state": null,
  "trx_tables_in_use": 0,
  "trx_tables_locked": 2,
  "trx_lock_structs": 3,
  "trx_lock_memory_bytes": 1128,
  "trx_rows_locked": 1,
  "trx_rows_modified": 2,
  "trx_concurrency_tickets": 0,
  "trx_isolation_level": "REPEATABLE READ",
  "trx_unique_checks": 1,
  "trx_foreign_key_checks": 1,
  "trx_last_foreign_key_error": null,
  "trx_adaptive_hash_latched": 0,
  "trx_adaptive_hash_timeout": 0,
  "trx_is_read_only": 0,
  "trx_autocommit_non_locking": 0,
  "trx_schedule_weight": null
}


2、innodb_lock_waits结果显示blocking_trx_id(阻塞事务的 ID)正是6228
在这里插入图片描述

{
  "wait_started": "2024-06-27 17:22:32",
  "wait_age": "00:00:13",
  "wait_age_secs": 13,
  "locked_table": "`test`.`order_items`",
  "locked_table_schema": "test",
  "locked_table_name": "order_items",
  "locked_table_partition": null,
  "locked_table_subpartition": null,
  "locked_index": "PRIMARY",
  "locked_type": "RECORD",
  "waiting_trx_id": 6231,
  "waiting_trx_started": "2024-06-27 17:22:32",
  "waiting_trx_age": "00:00:13",
  "waiting_trx_rows_locked": 1,
  "waiting_trx_rows_modified": 0,
  "waiting_pid": 173,
  "waiting_query": "DELETE FROM order_items WHERE (order_id = 1854)",
  "waiting_lock_id": "140081923306280:5:4:2:140081834817296",
  "waiting_lock_mode": "X",
  "blocking_trx_id": 6228,
  "blocking_pid": 172,
  "blocking_query": null,
  "blocking_lock_id": "140081923305472:5:4:2:140081834811312",
  "blocking_lock_mode": "X,REC_NOT_GAP",
  "blocking_trx_started": "2024-06-27 17:22:32",
  "blocking_trx_age": "00:00:13",
  "blocking_trx_rows_locked": 1,
  "blocking_trx_rows_modified": 2,
  "sql_kill_blocking_query": "KILL QUERY 172",
  "sql_kill_blocking_connection": "KILL 172"
}


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 的标识。

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。

结论

不要在一个事务没提交又进行其他修改操作,会容易导致获取不到锁

测试代码

测试类

@Transactional(timeout = 30)
@SpringBootTest(classes = Application.class)
class IOrdersServiceTest {

    @Autowired
    private IOrdersService service;

    @Autowired
    private IOrderItemsService orderItemsService;

	/**
	* 新增,再手动删除(业务子表会做分库分表,所有最后手动删除关联数据)
	**/
    @Test
    public void test_transactional() {
        Orders orders = generate_orders();
        try {
            service.save(orders);
            OrderItems orderItems = generate_order_items(orders);
            orderItemsService.save(orderItems);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            orderItemsService.delete(orders.getOrderId());
        }
    }

    private Orders generate_orders() {
        Orders orders = new Orders();
        orders.setCustomerId(123456);
        orders.setOrderDate(LocalDateTime.now());
        orders.setStatus("1");
        orders.setTotalAmount(new BigDecimal("100.12"));
        orders.setShippingAddress("江西省九江市");
        orders.setBillingAddress("浙江省杭州市");
        orders.setPaymentMethod("微信");

        return orders;
    }

    private OrderItems generate_order_items(Orders orders) {
        OrderItems orderItems = new OrderItems();
        orderItems.setOrderId(orders.getOrderId());
        orderItems.setProductId(1223);
        orderItems.setQuantity(1);
        orderItems.setUnitPrice(new BigDecimal("2123.12"));

        return orderItems;
    }
}

服务

@Service
public class OrderItemsServiceImpl extends ServiceImpl<OrderItemsMapper, OrderItems> implements IOrderItemsService {

    @Autowired
    private OrderItemsMapper mapper;

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void delete(Integer orderId) {
        LambdaQueryWrapper<OrderItems> wrapper = Wrappers.<OrderItems>lambdaQuery().eq(OrderItems::getOrderId, orderId);
        mapper.delete(wrapper);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值