华为云高斯db线上死锁问题

华为云高斯db线上死锁问题

前言

线上高斯db在业务高峰期出现上万条死锁,华为云给出了死锁日志(ps 阿里云是直接可以在控制台看到的)

死锁日志

RECORD LOCKS space id 6534 page no 112806 n bits 568 index idx_a of table `XXX`.`XXXX` trx id 756319989 lock_mode X locks gap before rec
Record lock, heap no 474 PHYSICAL RECORD: n_fields 3; compact format; info bits 0
 0: len 8; hex 80000000000010ba; asc         ;;
 1: len 8; hex 8000000000006b31; asc       k1;;
 2: len 8; hex 80000000002ec2be; asc      .  ;;

日志已经影去公司相关信息,看到明显的gap lock导致的死锁。数据库设置的默认隔离级别是read committed。依稀记得rc级别不会有gap lock的。查询mysql官网
gap lock

Gap locking can be disabled explicitly. This occurs if you change the transaction isolation level to READ COMMITTED. In this case, gap locking is disabled for searches and index scans and is used only for foreign-key constraint checking and duplicate-key checking.

与记忆中的基本一致。我们没有使用外键也没有使用唯一键检查,是不会存在gap lock的。

再次跟华为云确认他们与mysql 8.0行为是一致的。

华为云排查

华为云排查后告知我们数据库默认隔离级别是对的,原因是我们的session 每次都设置了会话的隔离级别为rr。代码中我们都是使用spring 申明式事务做事务,排查后并没有发现使用rr级别。我们跟华为云沟通后,华为云的意思是让我排查下spring的源码,查看下是不是哪里设置了事务的隔离级别。

排查源码

  1. org.springframework.transaction.support.AbstractPlatformTransactionManager[getTransaction(TransactionDefinition definition)]

  2. org.springframework.jdbc.datasource.DataSourceTransactionManager[doBegin(Object transaction, TransactionDefinition definition)]

  3. org.springframework.jdbc.datasource.DataSourceUtils[prepareConnectionForTransaction(Connection con, TransactionDefinition definition)]

  4. com.mysql.cj.jdbc.ConnectionImpl[setTransactionIsolation(int level)]

包和类名中跨号内是方法
这边只贴spring和jdbc中设置隔离级别部分源码

 // 非默认级别的情况下去设置隔离级别
 if (definition != null && definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
            if (logger.isDebugEnabled()) {
                logger.debug("Changing isolation level of JDBC Connection [" + con + "] to " +
                        definition.getIsolationLevel());
            }
            int currentIsolation = con.getTransactionIsolation();
            if (currentIsolation != definition.getIsolationLevel()) {
                previousIsolationLevel = currentIsolation;
                con.setTransactionIsolation(definition.getIsolationLevel());
            }
        }

jdbc

 switch (level) {
                    case java.sql.Connection.TRANSACTION_NONE:
                        throw SQLError.createSQLException(Messages.getString("Connection.24"), getExceptionInterceptor());

                    case java.sql.Connection.TRANSACTION_READ_COMMITTED:
                        sql = "SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED";

                        break;

                    case java.sql.Connection.TRANSACTION_READ_UNCOMMITTED:
                        sql = "SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED";

                        break;

                    case java.sql.Connection.TRANSACTION_REPEATABLE_READ:
                        sql = "SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ";

                        break;

                    case java.sql.Connection.TRANSACTION_SERIALIZABLE:
                        sql = "SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE";

                        break;

                    default:
                        throw SQLError.createSQLException(Messages.getString("Connection.25", new Object[] { level }),
                                MysqlErrorNumbers.SQL_STATE_DRIVER_NOT_CAPABLE, getExceptionInterceptor());
                }

spring默认是不会去设置事务的隔离级别,设置隔离级别是jdbc实现的。所以spring也是没问题的。

最终结论

业务侧并没有问题,只能只华为云的问题了。

最后运维排查发现华为云的数据库代理默认的隔离级别居然是repeatable-read,设备是read-committed,也就是代理层每次会设置事务的隔离级别。坑爹华为云。

你可能感兴趣的:(华为云高斯db线上死锁问题)