我们正在开发一个群聊应用程序并使用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;"
我们如何解决这个问题?
答案 0 :(得分:0)
除非频繁发生,否则死锁是没有问题的。
正确的解决方案是让应用程序不要惊慌,而只是重试数据库事务。
如果死锁频繁发生,您需要调查并解决原因。通常这意味着保持您的交易简短,并始终按特定顺序锁定对象。
如果您需要特定帮助,请阅读PostgreSQL日志文件以找出涉及的查询。您必须了解哪些锁冲突才能确定根本原因。