记一次gorm事务及调试解决mysql死锁

您所在的位置:网站首页 数据库死锁问题解决 记一次gorm事务及调试解决mysql死锁

记一次gorm事务及调试解决mysql死锁

2024-07-12 01:51| 来源: 网络整理| 查看: 265

前言

之前使用mysql和postgresql是通过sqlc包来使用,这一次使用gorm,还是遇到了很多问题,在此记录以下。

版本问题

gorm大版本更新之后,驱动的引入及实际的用法和老版本会有一些区别,特别是事务、锁的使用以及配置项的修改。 新版本gorm数据库链接方式以及包的名称和老版本相比均有改变,如下:

老版本:

import ( "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" ) db, err := gorm.Open("mysql", "root:xxxx@(127.0.0.1:3306)/db01?&parseTime=True&loc=Local")

而新版本:

import ( "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/schema" ) db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})

老版本的配置项通过db.xx的形式修改:

db.LogMode(true) db.DB().SetMaxIdleConns(10) db.DB().SetMaxOpenConns(100) db.SingularTable(true)

而新版本均通过&gorm.Config{}字段进行修改:

db, err = gorm.Open(mysql.Open(dbLink), &gorm.Config{ SkipDefaultTransaction: true, //开启事务 NamingStrategy: schema.NamingStrategy{ SingularTable: true, },

forupdate的用法改变: 最开始依旧使用老版本的用法来使用,但是一直不成功,然后使用debug打印sql语句,发现生成的sql语句并没有for update相关操作,再去查找资料,发现老版本写法不生效,更换为新的写法。 老版本:

result := tx.Debug().Set("gorm:query_option", "FOR UPDATE").First(&user1, arg.FromUserID)

新版本:

result = tx.Debug().Clauses(clause.Locking{Strength: "UPDATE"}).First(&user2, arg.ToUserID) 数据库死锁的排查解决:

在一个类似于转账的场景,为了保证转账的一致性,在相关的接口中使用了事务来保证转账成功,在最后测试同一个账户有人转出,有人转入时出现死锁; 根据网上查询以及一位大佬博文(http://t.csdn.cn/nAzEf)的思路下,在终端还原了死锁场景。 分别开启两个事务,按照顺序进行update操作:在执行第4步即事务2中的第二条操作时,出现死锁。

事务1: 1 update user set collection_coins = collection_coins - 10 where id = 1537973755; 3 update user set collection_coins = collection_coins + 10 where id = 1338725819; 等待获取锁 事务2: 2 update user set collection_coins = collection_coins - 10 where id = 1338725819; 4 update user set collection_coins = collection_coins + 10 where id = 1537973755;

在这里插入图片描述

查询相关锁的表,能看到在发生死锁的上一步:

分别运行以下两个语句:

select * from information_schema.innodb_locks; select * from information_schema.innodb_lock_waits;

在这里插入图片描述 运行:

SELECT * FROM information_schema.innodb_trx;

可以看到 在这里插入图片描述 可以看到,两个事务均获得了锁,进行了第3个操作时,事务1阻塞,等待事务2释放 锁;如果此时事务2不释放锁,反而执行第4个操作,则会造成事务2请求锁,但此时锁在事务1处,而事务1已经阻塞,无法释放锁,则事务2也会被阻塞,形成死锁。

解决办法则是: 调换4个语句的执行顺序; 如果按照1-4-3-2的顺序来进行则不会造成死锁。 因为在执行1后,事务1获得锁1,此时事务2执行4,事务2请求锁1,此时事务2阻塞; 事务1再执行3,获得锁2;此时事务1执行完毕,commit释放锁1和锁2,事务2获得锁1,语句4执行成功,再执行语句2成功,则不会发生死锁。

换到代码中则是对执行顺序进行排列,利用id为数字格式,可以比大小,则按照大小进行排列即可;



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3