Mysql 事务锁等待时间超时


一 问题描述

Lock wait timeout exceeded; try restarting transaction

二 处理过程

  首先假如在生产中遇到这个问题,我们必然是先找到这个循环等待的线程,给他kill了,如下

 然后kill掉957和958

 

三 系统调节

  一般mysql的inodb有个默认事物等待时间,超过这个时间他会自动结束且抛出异常,参数为innodb_lock_wait_timeout,默认50s,修改它即可

四 代码链路追踪

  分析出代码是在同一个表的同一行进行第二次updt操作的时候锁住了,然后也查阅相关资料,发现有的coder说这种情况会发生锁死。然后本人做了个测试

package main

import (
	"database/sql"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"time"
)

var MysqlDb *sql.DB
var MysqlDbErr error

// 初始化链接
//func InitDb() {
func init() {

	m_user := "wbw"
	m_pd := "123456"
	m_host := "127.0.0.1"
	m_pt := "3306"
	m_db := "test_local"

	dbDSN := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s", m_user, m_pd, m_host, m_pt, m_db, "utf8")

	// 打开连接失败
	MysqlDb, MysqlDbErr = sql.Open("mysql", dbDSN)
	//defer MysqlDb.Close();
	if MysqlDbErr != nil {
		panic("数据源配置不正确: " + MysqlDbErr.Error())
	}

	// 最大连接数
	MysqlDb.SetMaxOpenConns(100)
	// 闲置连接数
	MysqlDb.SetMaxIdleConns(20)
	// 最大连接周期
	MysqlDb.SetConnMaxLifetime((30)*time.Second)

	if MysqlDbErr = MysqlDb.Ping(); nil != MysqlDbErr {
		panic("数据库链接失败: " + MysqlDbErr.Error())
	}

}

func main(){
	//ifupdt,err := UpdtCarrierRemark("asd","hah2")
	ifupdt,err := UpdtCarrierRemarkTrx("asd","hatt")
	fmt.Println(ifupdt,err)
}

func UpdtCarrierRemark(asn_id string,remark string) (bool,error){
	_,errs := MysqlDb.Exec("update ls_wms_asn set " +
		"remark=? where (asn_id=?) ",remark,asn_id)
	if errs != nil{
		fmt.Println(errs)
		return false,errs
	}
	return true,nil
}

func UpdtCarrierRemarkTrx(asn_id string,remark string) (bool,error){
	tx, _ := MysqlDb.Begin()
	_,errs := tx.Exec("update ls_wms_asn set " +
		"remark=? where (asn_id=?) ",remark,asn_id)
	if errs != nil{
		fmt.Println(errs)
		tx.Rollback()
		return false,errs
	}

	_,errs2 := tx.Exec("update ls_wms_asn set " +
		"remark_ct=201 where (asn_id=?) ",asn_id)
	if errs2 != nil{
		fmt.Println(errs2)
		tx.Rollback()
		return false,errs2
	}
	tx.Commit()
	return true,nil
}

  做了验证后发现并没有发生锁死的情况,然后猜测可能是并发不够,所以专门又用ab测试了1s钟100次的并发,也未发生锁死。

  所以暂时排除此种原因

五 小失误导致

  原来我代码中用的是MysqlDb.Exec,而非tx.Exec

  当我们开启事务tx, _ := MysqlDb.Begin()后,对db的操作都应该用tx来完成,而非常规的DB单例变量。

  虽然原因找到了,是个简单失误,但是能够简单回顾下事物锁死的处理过程,也算小有收获