检测到Postgres Npgsql.PostgresException死锁

时间:2018-05-30 17:05:42

标签: postgresql npgsql

我们正在开发一个群聊应用程序并使用PostgreSQL来存储聊天消息。

CREATE TABLE public.chatmessage
(
    chatmessageid uuid NOT NULL DEFAULT uuid_generate_v4(),
    text character varying COLLATE pg_catalog."default",
    planid uuid NOT NULL,
    userid uuid NOT NULL,
    createdat timestamp with time zone NOT NULL DEFAULT timezone('utc'::text, now()),
    updatedat timestamp with time zone,
    deleted boolean NOT NULL DEFAULT false,
    viewedallstatus boolean,
    vieweduserids uuid[],
    alloweduserids uuid[],
    CONSTRAINT chatmessage_pkey PRIMARY KEY (chatmessageid)
)

要管理读取状态,我们将已查看参与者的userIds存储在seenuserIds列中。

虽然大量使用与5位或更多参与者的群聊,但我们遇到以下异常,

Npgsql.PostgresException (0x80004005): 40P01: deadlock detected
   at Npgsql.NpgsqlConnector.<DoReadMessage>d__157.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at System.Runtime.CompilerServices.ValueTaskAwaiter`1.GetResult()
   at Npgsql.NpgsqlConnector.<ReadMessage>d__156.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Npgsql.NpgsqlConnector.<ReadMessage>d__156.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at System.Runtime.CompilerServices.ValueTaskAwaiter`1.GetResult()
   at Npgsql.NpgsqlDataReader.<NextResult>d__32.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Npgsql.NpgsqlDataReader.<<NextResultAsync>b__31_0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Npgsql.NpgsqlCommand.<Execute>d__71.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at System.Runtime.CompilerServices.ValueTaskAwaiter`1.GetResult()
   at Npgsql.NpgsqlCommand.<ExecuteNonQuery>d__84.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Npgsql.NpgsqlCommand.<>c__DisplayClass83_0.<<ExecuteNonQueryAsync>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Dapper.SqlMapper.<ExecuteImplAsync>d__37.MoveNext() in C:\projects\dapper\Dapper\SqlMapper.Async.cs:line 646
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)

我觉得既然应用程序同时更新了同一列,那么这个问题就会出现。更新seenuserIds列的查询如下,

"Update ChatMessage Set ViewedUserIds = ViewedUserIds || @UserId Where PlanId = @PlanId And @UserId = Any(AllowedUserIds) And Not (@UserId = Any(ViewedUserIds)) And Deleted = False;" 

我们如何解决这个问题?

1 个答案:

答案 0 :(得分:0)

除非频繁发生,否则死锁是没有问题的。

正确的解决方案是让应用程序不要惊慌,而只是重试数据库事务。

如果死锁频繁发生,您需要调查并解决原因。通常这意味着保持您的交易简短,并始终按特定顺序锁定对象。

如果您需要特定帮助,请阅读PostgreSQL日志文件以找出涉及的查询。您必须了解哪些锁冲突才能确定根本原因。