seata线上问题汇总

问题一:死锁

记录一次分布式事务seata遇到的巨坑报错,开发环境没有问题,但是测试环境频繁出现事务报错。

报错提示:

org.springframework.orm.jpa.JpaSystemException: Unable to commit JDBC Connection; 
  nested exception is org.hibernate.TransactionException:Unable to commit against JDBC Connection

在开发环境从没遇过这样的报错,但是测试环境经常会出现,而且某些数据在编辑时候只要报了一次这样的错,后面编辑都会一直报这个错。

原因:

查看了一下这两个环境不一样的地方是防火墙,测试环境防火墙设置了老化时间为20分钟,而开发环境设置了防火墙为不老化,由于分布式事务服务与数据库需要一直保持长连接,但防火墙超过20分钟后会删除该会话,导致部分新增事务控制的数据库锁记录未被删除,因此数据超时异常。

解决办法:

将防火墙设置为不老化,不再出现死锁。

问题二:死锁(二)

上一次已经踩过一个坑了,这次报错没想到又遇到类似的锁问题了,查看全局锁的表lock_table发现确实又锁了一堆数据。

报错提示:

Caused by: org.hibernate.TransactionException: Unable to commit against JDBC Connection
 at org.hibernate.resource.jdbc.internal.AbstractLogicalConnectionImplementor.commit(AbstractLogicalConnectionImplementor.java:87)
 at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:272)
 at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:104)
 at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:533)
 ... 77 common frames omitted
Caused by: java.sql.SQLException: io.seata.core.exception.RmTransactionException: Response[ TransactionException[Could not found global transaction xid = 11.11.11.50:7003:2021769421, may be has finished.] ]
 at io.seata.rm.datasource.ConnectionProxy.recognizeLockKeyConflictException(ConnectionProxy.java:157)
 at io.seata.rm.datasource.ConnectionProxy.processGlobalTransactionCommit(ConnectionProxy.java:218)
 at io.seata.rm.datasource.ConnectionProxy.doCommit(ConnectionProxy.java:196)
 at io.seata.rm.datasource.ConnectionProxy.lambda$commit$0(ConnectionProxy.java:184)
 at io.seata.rm.datasource.ConnectionProxy$LockRetryPolicy.execute(ConnectionProxy.java:289)
 at io.seata.rm.datasource.ConnectionProxy.commit(ConnectionProxy.java:183)
 at org.hibernate.resource.jdbc.internal.AbstractLogicalConnectionImplementor.commit(AbstractLogicalConnectionImplementor.java:81)
 ... 80 common frames omitted
Caused by: io.seata.core.exception.RmTransactionException: Response[ TransactionException[Could not found global transaction xid = 11.11.11.50:7003:2021769421, may be has finished.] ]
 at io.seata.rm.AbstractResourceManager.branchRegister(AbstractResourceManager.java:69)
 at io.seata.rm.DefaultResourceManager.branchRegister(DefaultResourceManager.java:96)
 at io.seata.rm.datasource.ConnectionProxy.register(ConnectionProxy.java:238)
 at io.seata.rm.datasource.ConnectionProxy.processGlobalTransactionCommit(C

原因:

这次报错看到了后面的一句话may be has finished翻译过来就是可能事务已经结束了,检查了一下代码发现代码中会有一个地方调用其它服务进行批量update操作,而这个地方耗时比较长,大概知道原因了,这可能是服务重试,或者网络延迟导致调用参与方服务的时候超时,事务已经回滚结束了,这个延迟的请求才到底对应服务,并尝试加入全局事务,但是全局事务已经结束了,所以抛出异常来回滚所做的操作保证一致性。

解决办法:

在配置文件中设置http建立socket超时时间和http读取响应socket超时时间

# Ribbon配置
ribbon:
# http建立socket超时时间,毫秒
    ConnectTimeout: 6000
# http读取响应socket超时时间
    ReadTimeout: 12000

问题三:批量保存

项目使用了微服务,并且将一些模块进行了拆分,现在遇到了一个批量保存的场景,而且还是跨服务调用,因此选用了seataAT模式比较简单方便。

seata官网http://seata.io/zh-cn/docs/overview/what-is-seata.html

在进行一个单据保存时,由于整单保存需要批量保存一千条数据,耗时竟然要8秒,首先看了下JPAsaveAll方法底层其实调用了for循环一条一条保存,但修改后使用了自定义的批量保存其实还是没得到多大改善。

后来检查发现这个业务加了@GlobalTransactional注解需要跨服务,虽然这段批量保存不是其他服务的,但也会非常耗时,去掉这个注解之后保存,只需要1秒。

原因:

看了下seata官网的AT模式,我个人理解是虽然修改成了批量保存,但是AT模式是基于本地ACID事务的关系型数据库的,这些数据插入到数据库时每一条数据都加上了锁,而加锁是很耗时的,当前业务需要批量插入1000条数据也就是说这1000条数据每条都加了锁。

问题四:高并发

seata在高并发下出现的问题:AT模式获取全局锁失败

seata服务器中出现的异常:

io.seata.core.exception.BranchTransactionException: Global lock acquire failed xid = 127.0.0.1:8091:121648404042354689 branchId = 121648409478172673

后台出现的异常:

Could not commit JDBC transaction; nested exception is io.seata.rm.datasource.exec.LockConflictException: get global lock fail, xid:127.0.0.1:8091:121648404042354689, lockKeys:bs_product_price_goods_item:26

AT模式出现的异常:

org.springframework.transaction.CannotCreateTransactionException: Could not open JDBC Connection for transaction; nested exception is java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30077ms.
Caused by: java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30077ms.
at com.zaxxer.hikari.pool.HikariPool.createTimeoutException(HikariPool.java:695)
at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:197)
at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:162)
at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:128)
at org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:261)
... 65 more

问题分析:

事务在提交之前需要获取seata全局锁,由于全局锁被其他事务占用着,从而出现获取全局锁失败异常;可细看seata官网描述:https://seata.io/zh-cn/docs/dev/mode/at-mode.html

seata结论:

seata的AT、XA模式都是基于全局事务实现的,在高并发的场景下会出现获取全局锁异常,因此这两种模式都不适用高并发场景;
seata TCC模式性能比AT模式的好一点,但是并发量大于100的话还是不适合;
如果基本没有什么并发量的话,可以选择AT模式;并发量在一百内的话可以使用TCC模式
高并发场景,不适合使用seata,适合用中间件,例如用rocketmq替代seata,可以弥补seata的不足

问题五:高并发超时

模拟场景

@GlobalTransactional
public void purchase(String userId, String commodityCode, int orderCount) {
    try {
        lock.lock();
        Product product = productService.getById(commodityCode);
        if (product.getStock() - orderCount  > 0) {
            LocalDateTime now = LocalDateTime.now();
            Account account = accountService.getByUserId(userId);
            Orders orders = new Orders();
            orders.setCreateTime(now);
            orders.setProductId(product.getId());
            orders.setReplaceTime(now);
            orders.setSum(orderCount );
            orders.setAmount(product.getPrice());
            orders.setAccountId(account.getId());
            product.setStock(product.getStock() - orderCount );
            account.setSum(account.getSum() != null ? account.getSum() + orderCount : orderCount);
            account.setLastUpdateTime(now);
            productService.updateById(product);
            accountService.updateById(account);
            ordersService.save(orders);
        }
    } catch (Exception e) {
        // TODO: handle exception
        throw new RuntimeException();
    } finally {
        lock.unlock();
    }
}

压测后发现,容易出现事务超时的异常,如图:

20191217095158776.png

可以看到出现异常后回滚超时等情况了。

分析原因:

我们可以按照以上的代码看到,官网的介绍比较多的都是注解形势,api提及比较少,我们首先分析刚才代码

服务调用->发现注解->创建事务->等待锁->获取锁->业务处理

可以发现优先创建了事务,这时候如果再大并发下,一直等待抢不到锁的话......这时候会引发上图所示的各种超时异常。

解决方案"

再来看下流程:服务调用->发现注解->创建事务->等待锁->获取锁->业务处理。发现问题所在了不?只要我们能把等待锁获取锁的操作放在创建事务前,这个问题迎刃而解。

用户请求->等待锁->获取锁->服务调用->发现注解->创建事务->业务处理

@GetMapping(value = "purchase")
public Object purchase() throws TransactionException {
    try {
        lock.lock();
        return demoService.purchase(1, 2, 3);
    } finally {
        // TODO: handle finally clause
        lock.unlock();
    }
}

比如上述代码这样,再调用前直接更改掉,这样服务内的锁也可以去除,把锁换到了接口来。如果有人说,因为是分布式锁不是本地锁,或者我加锁的地方就是要在service内咋办?没关系,seata还有提供一套事务的api创建方式。

public void purchase(String userId, String commodityCode, int orderCount) {
    try {
        lock.lock();
        Product product = productService.getById(commodityCode);
        if (product.getStock() - orderCount  > 0) {
            GlobalTransaction tx = GlobalTransactionContext.getCurrentOrCreate();
            tx.begin(300000, "test-group");
            try {
                LocalDateTime now = LocalDateTime.now();
                Account account = accountService.getByUserId(userId);
                Orders orders = new Orders();
                orders.setCreateTime(now);
                orders.setProductId(product.getId());
                orders.setReplaceTime(now);
                orders.setSum(orderCount);
                orders.setAmount(product.getPrice());
                orders.setAccountId(account.getId());
                product.setStock(product.getStock() - orderCount );
                account.setSum(account.getSum() != null ? account.getSum() + orderCount : orderCount);
                account.setLastUpdateTime(now);
                productService.updateById(product);
                accountService.updateById(account);
                ordersService.save(orders);
                tx.commit();
            } catch (Exception e) {
                // TODO: handle exception
                tx.rollback();
            }
        }
    } finally {
        lock.unlock();
    }
}

改为上述代码,使用api进行提交跟回滚操作即可,这样保证了抢到锁后才进行事务的创建。

问题六:序列化异常问题(已解决)

1.4.x版本在MySQL8.0DATETIME类型转换错误的问题

异常信息

2021-05-16 16:47:32.924  INFO 22268 --- [h_RMROLE_1_8_16] i.seata.rm.datasource.DataSourceManager  : branchRollback failed. branchType:[AT], xid:[172.20.48.1:8091:5485518365424181316], branchId:[5485518365424181322], resourceId:[jdbc:mysql://localhost:3306/seata_order], applicationData:[null]. reason:[Branch session rollback failed and try again later xid = 172.20.48.1:8091:5485518365424181316 branchId = 5485518365424181322 com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `java.time.LocalDateTime` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: (byte[])"{"@class":"io.seata.rm.datasource.undo.BranchUndoLog","xid":"172.20.48.1:8091:5485518365424181316","branchId":5485518365424181322,"sqlUndoLogs":["java.util.ArrayList",[{"@class":"io.seata.rm.datasource.undo.SQLUndoLog","sqlType":"INSERT","tableName":"t_order","beforeImage":{"@class":"io.seata.rm.datasource.sql.struct.TableRecords$EmptyTableRecords","tableName":"t_order","rows":["java.util.ArrayList",[]]},"afterImage":{"@class":"io.seata.rm.datasource.sql.struct.TableRecords","tableName":"t_order"[truncated 14288 bytes]; line: 1, column: 1249] (through reference chain: io.seata.rm.datasource.undo.BranchUndoLog["sqlUndoLogs"]->java.util.ArrayList[0]->io.seata.rm.datasource.undo.SQLUndoLog["afterImage"]->io.seata.rm.datasource.sql.struct.TableRecords["rows"]->java.util.ArrayList[0]->io.seata.rm.datasource.sql.struct.Row["fields"]->java.util.ArrayList[4]->io.seata.rm.datasource.sql.struct.Field["value"])]
2021-05-16 16:47:32.924  INFO 22268 --- [h_RMROLE_1_8_16] io.seata.rm.AbstractRMHandler            : Branch Rollbacked result: PhaseTwo_RollbackFailed_Retryable
2021-05-16 16:47:33.911  INFO 22268 --- [h_RMROLE_1_9_16] i.s.c.r.p.c.RmBranchRollbackProcessor    : rm handle branch rollback process:xid=172.20.48.1:8091:5485518365424181316,branchId=5485518365424181322,branchType=AT,resourceId=jdbc:mysql://localhost:3306/seata_order,applicationData=null
2021-05-16 16:47:33.911  INFO 22268 --- [h_RMROLE_1_9_16] io.seata.rm.AbstractRMHandler            : Branch Rollbacking: 172.20.48.1:8091:5485518365424181316 5485518365424181322 jdbc:mysql://localhost:3306/seata_order
2021-05-16 16:47:33.914 ERROR 22268 --- [h_RMROLE_1_9_16] i.s.r.d.u.parser.JacksonUndoLogParser    : json decode exception, Cannot construct instance of `java.time.LocalDateTime` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: (byte[])"{"@class":"io.seata.rm.datasource.undo.BranchUndoLog","xid":"172.20.48.1:8091:5485518365424181316","branchId":5485518365424181322,"sqlUndoLogs":["java.util.ArrayList",[{"@class":"io.seata.rm.datasource.undo.SQLUndoLog","sqlType":"INSERT","tableName":"t_order","beforeImage":{"@class":"io.seata.rm.datasource.sql.struct.TableRecords$EmptyTableRecords","tableName":"t_order","rows":["java.util.ArrayList",[]]},"afterImage":{"@class":"io.seata.rm.datasource.sql.struct.TableRecords","tableName":"t_order"[truncated 14288 bytes]; line: 1, column: 1249] (through reference chain: io.seata.rm.datasource.undo.BranchUndoLog["sqlUndoLogs"]->java.util.ArrayList[0]->io.seata.rm.datasource.undo.SQLUndoLog["afterImage"]->io.seata.rm.datasource.sql.struct.TableRecords["rows"]->java.util.ArrayList[0]->io.seata.rm.datasource.sql.struct.Row["fields"]->java.util.ArrayList[4]->io.seata.rm.datasource.sql.struct.Field["value"])

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `java.time.LocalDateTime` (no Creators, like default constructor, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
 at [Source: (byte[])"{"@class":"io.seata.rm.datasource.undo.BranchUndoLog","xid":"172.20.48.1:8091:5485518365424181316","branchId":5485518365424181322,"sqlUndoLogs":["java.util.ArrayList",[{"@class":"io.seata.rm.datasource.undo.SQLUndoLog","sqlType":"INSERT","tableName":"t_order","beforeImage":{"@class":"io.seata.rm.datasource.sql.struct.TableRecords$EmptyTableRecords","tableName":"t_order","rows":["java.util.ArrayList",[]]},"afterImage":{"@class":"io.seata.rm.datasource.sql.struct.TableRecords","tableName":"t_order"[truncated 14288 bytes]; line: 1, column: 1249] (through reference chain: io.seata.rm.datasource.undo.BranchUndoLog["sqlUndoLogs"]->java.util.ArrayList[0]->io.seata.rm.datasource.undo.SQLUndoLog["afterImage"]->io.seata.rm.datasource.sql.struct.TableRecords["rows"]->java.util.ArrayList[0]->io.seata.rm.datasource.sql.struct.Row["fields"]->java.util.ArrayList[4]->io.seata.rm.datasource.sql.struct.Field["value"])

解决办法

seata自身的问题,当下有四种解决办法(任选一种即可):

解决方法来源于:LocalDateTime转换异常,springboot版本:2.4.4 · Issue #3620

  1. seata官方修复并更新新版本
  2. https://github.com/seata/seata/pull/3228/files或者看这个pr的做法,通过spi,自定义你的jackson序列化器
  3. 查看你代码的实体类时间属性对应的数据库字段类型如果是datetime改成timestamp
  4. 修改seata的换序列化方式配置中心中配置client.undo.logSerialization=kryoclient端再引入kryo的依赖包(ruoyi-cloud项目放到ruoyi-common-core/pom.xml下即可)

kryo依赖如下(版本自行选择)

<dependencies>
    <dependency>
        <groupId>com.esotericsoftware.kryo</groupId>
        <artifactId>kryo</artifactId>
        <version>2.24.0</version>
    </dependency>
    <dependency>
        <groupId>com.esotericsoftware</groupId>
        <artifactId>kryo</artifactId>
        <version>4.0.2</version>
    </dependency>
    <dependency>
        <groupId>de.javakaffee</groupId>
        <artifactId>kryo-serializers</artifactId>
        <version>0.44</version>
    </dependency>
</dependencies>

结论

这个问题的seata本身存在的问题,https://github.com/seata/seata/issues/3866 已经修复过了,等下个版本(1.5.0)更新的时候升级一下依赖。有需要的话可以自己先加一下kryo序列化方式。

问题七:AT模式全局事务回滚(已解决)

说下背景吧,公司最近打算用seata来保证SpringCloud微服务间的全局事务(AT模式),使用的是seata-server 1.1.0版本。注册方式Eurekaseata-server在测试环境部署了2台!

异常情况:

测试的时候A服务为发起端A->B->C其中B服务在一次方法调用中多次调用C服务更改同一条记录(数据的创建以及更改状态new->wait_pay->pay->success)。于是在A发起的请求结束时B实际调用了3次C服务,导致C服务里有3条undo_log日志,之后B服务发生了异常,C服务(每次都完成了本地事务)进行数据回滚(全局事务undo_log),此时正常情况应当是按照3条记录 1 2 3创建的后先顺序执行回滚,即 3 2 1 这就能保证数据正常回滚!

然而!在多次测试中发现数据回滚有时并不一定是按照 3 2 1 顺序执行,时常会从2开始这就导致2中的记录值和当前数据库值的实际值 有偏差(2中记录修改后的状态为pay,实际数据库里的状态是3中更改后记录的状态success)这就导致了回滚镜像对比发现数据异常,认为是脏数据产生了!无法正确造成回滚。

找原因:

从结果上看数据无法回滚是由于回滚时候undo_log执行的顺序异常导致的,以此切入!跟踪seata-server的代码发现,分支事务回滚时会根据全局事务xidbranch_table中按照记录生成的时间(gmt_create) 正序查询所有分支事件记录放入List中,之后从List里倒序取出,挨个执行回滚!

然后惊奇的发现,每次出现上述回滚异常都是因为有两条或多条branch_table记录的gm_create是相同的,以致于后续回滚查询分支事务的时候无法保证其先后顺序,而后执行回滚的顺序就一样无法保证,才最终导致上述错误!

解决方案:

问题找到了,既然按时间顺序查找不靠谱 那找个靠谱的值来查询不就行了,其中发现branch_id其实相对来说是递增的但是只相对于同一个服务而言。

private static final AtomicLong UUID = new AtomicLong(1000);

/**
 * Generate uuid long
 *
 * @return the long
 */
public static long generateUUID() {
    long id = UUID.incrementAndGet();
    if (id >= getMaxUUID()) {
        synchronized (UUID) {
            if (UUID.get() >= id) {
                id -= UUID_INTERNAL;
                UUID.set(id);
            }
        }
    }
    return id;
}

实际测试环境部署了有2seata-server服务,所以并不能保证两个服务产生的branch_id有什么可靠的关联性,最终考虑下比较简单的方法是在branch_table里增加一个自增的字段,使用该字段代替gmt_create进行上述分支事务查询的排序依据即可

(添加了自增的id,之后更改查询语句如下)

经过更改测试后发现确实解决了上述无法顺序回滚带来的问题!在测试时间如果只是启动一个seata-server服务一般是不会产生上述问题的,时间基本有先后的明显差别!再者branch_id也是明显增加的也可以用作查询排序依据,单多台seata-server的时候就不能保证了!本地重新打包各模块后,在seata-serverlib包下替换修改后的新的模块jar即可!

mvn clean install -DskipTests=true

结论

最新版本1.5.1已经解决异常控制(幂等、空回滚、悬挂)问题。感兴趣的可以看一下seata设计方案中的异常控制

问题八:字段长度不够(已解决)

这个问题比较简单

org.springframework.transaction.TransactionSystemException: JDBC commit failed; nested exception is java.sql.SQLException: io.seata.core.exception.RmTransactionException: Response[ TransactionException[branch register request failed. xid=47.100.164.120:8091:27272433966331937, msg=Data truncation: Data too long for column 'row_key' at row 1] ]
	at org.springframework.jdbc.datasource.DataSourceTransactionManager.translateException(DataSourceTransactionManager.java:435)
	at org.springframework.jdbc.support.JdbcTransactionManager.translateException(JdbcTransactionManager.java:188)
	at org.springframework.jdbc.datasource.DataSourceTransactionManager.doCommit(DataSourceTransactionManager.java:336)
	at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:743)
	at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:711)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:654)
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:407)
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:692)
	at com.ht.account.service.impl.HtAccountBalanceServiceImpl$$EnhancerBySpringCGLIB$$62d1ba27.addBalance(<generated>)
	at com.ht.account.controller.AccountBalanceController.modifyBalance(AccountBalanceController.java:192)
	at com.ht.account.controller.AccountBalanceController$$FastClassBySpringCGLIB$$5aba5833.invoke(<generated>)
Caused by: java.sql.SQLException: io.seata.core.exception.RmTransactionException: Response[ TransactionException[branch register request failed. xid=47.100.164.120:8091:27272433966331937, msg=Data truncation: Data too long for column 'row_key' at row 1] ]
	at io.seata.rm.datasource.ConnectionProxy.recognizeLockKeyConflictException(ConnectionProxy.java:161)
	at io.seata.rm.datasource.ConnectionProxy.processGlobalTransactionCommit(ConnectionProxy.java:252)
	at io.seata.rm.datasource.ConnectionProxy.doCommit(ConnectionProxy.java:230)
	at io.seata.rm.datasource.ConnectionProxy.lambda$commit$0(ConnectionProxy.java:188)
	at io.seata.rm.datasource.ConnectionProxy$LockRetryPolicy.execute(ConnectionProxy.java:333)
	at io.seata.rm.datasource.ConnectionProxy.commit(ConnectionProxy.java:187)
	at org.springframework.jdbc.datasource.DataSourceTransactionManager.doCommit(DataSourceTransactionManager.java:333)
	... 80 common frames omitted
Caused by: io.seata.core.exception.RmTransactionException: Response[ TransactionException[branch register request failed. xid=47.100.164.120:8091:27272433966331937, msg=Data truncation: Data too long for column 'row_key' at row 1] ]
	at io.seata.rm.AbstractResourceManager.branchRegister(AbstractResourceManager.java:69)
	at io.seata.rm.DefaultResourceManager.branchRegister(DefaultResourceManager.java:96)
	at io.seata.rm.datasource.ConnectionProxy.register(ConnectionProxy.java:272)
	at io.seata.rm.datasource.ConnectionProxy.processGlobalTransactionCommit(ConnectionProxy.java:250)
	... 85 common frames omitted

解决方法:

是因为seata库的lock_table表中的字段长度不够引起的,默认值太小,调整为合适大小即可。

参考

posted @ 2022-04-25 16:47  夏尔_717  阅读(6379)  评论(0编辑  收藏  举报