就是“一起帮”发布求助时出的问题,业务逻辑是:1、生成一个帮帮币记录;2、将该帮帮币记录Id挂到求助人身上——这些都应该是在同一个事务中完成的。
但是,就偶尔会报这种错。本地和ite环境都死活复现不了,生产环境好几次了(很频繁,但不绝对)
看到这样的说法:
如果在事务中执行了一条不满足条件的update语句,则执行全表扫描,把行级锁上升为表级锁,多个这样的事务执行后,就很容易产生死锁和阻塞。
但前面的Insert语句必然是已经执行成功了的呀? 生产的`MoneyLog_Id` 就是 71吧?13:00:38 UPDATE `User` SET `MoneyLog_Id`=102 WHERE (`Id` = 1) AND (`MoneyLog_Id` = 71) Error Code: 1205. Lock wait timeout exceeded; try restarting transaction 51.047 sec
SET SESSION sql_mode='ANSI';INSERT INTO `AidMoneyLogs`( `BanlanceOfAvailable`, `BanlanceOfFrozen`, `BanlanceOfDebit`, `AmountOfExchange`, `AmountOfFrozen`, `AmountOfDebit`, `Kind`, `Comment`, `CreateTime`, `Owner_Id`) VALUES ( 9, 41, 40, -30, 30, 0, 2, @gp1, @gp2, 1); SELECT `Id` FROM `AidMoneyLogs` WHERE row_count() > 0 AND `Id`=last_insert_id() -- @gp1: '因求助<a class="text-info" href="/P/35">本地的修改,发布到生成环境,没有产生任何变化</a>被冻结' (Type = String, IsNullable = false, Size = 66) -- @gp2: '2023/11/10 12:09:48' (Type = DateTime, IsNullable = false) -- Executing at 2023/11/10 12:09:48 +08:00 -- Completed in 1 ms with result: EFMySqlDataReader UPDATE `User` SET `MoneyLog_Id`=102 WHERE (`Id` = 1) AND (`MoneyLog_Id` = 71) -- Executing at 2023/11/10 12:09:48 +08:00 -- Failed in 30505 ms with error: Fatal error encountered during command execution.
--- 2023年11月30日补充 ---
好像是transaction isolation level 默认是repeatable read的原因。
非常感谢豪猪不挡道的协助。但
问题应该在这里:
public class AidEndDbContext : ActionFilterAttribute, IExceptionFilter
{
public BaseService service { get; set; }
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
if (!filterContext.IsChildAction)
{
service.Commit();
之前我们的架构是每一个Action(OnActionExecuted)结束的时候就提交事务,但这一次是在View呈现完成之后(OnActionExecuted())提交事务。
在代码的复制粘贴的过程中,忘记了用:
if (!filterContext.IsChildAction)确保只有当一个完整的View/Action(而不是PartialView/ChildAction)呈现结束后,才提交事务。
所以就会造成:
builder.RegisterAssemblyTypes(typeof(BaseService).Assembly).InstancePerRequest().AsSelf(); builder.RegisterType(typeof(MysqlDbContext)).InstancePerRequest().As<DbContext>();但该连接的事务对象如果还没来得及释放(transaction != null),就会悲剧了……
using (DbContextTransaction transaction = dbContext.Database.CurrentTransaction)
{
if (transaction != null)