我正在寻找一个从Java 6应用程序中处理数据库死锁的好策略; 可能会有几个并行线程同时写入同一个表.如果数据库(Ingres RDMBS)检测到死锁,它将随机杀死其中一个会话.
考虑到以下要求,处理死锁情况的可接受技术是什么?
到目前为止,我提出的策略是这样的:
short attempts = 0;
boolean success = false;
long delayMs = 0;
Random random = new Random();
do {
try {
//insert loads of records in table 'x'
success = true;
} catch (ConcurrencyFailureException e) {
attempts++;
success = false;
delayMs = 1000*attempts+random.nextInt(1000*attempts);
try {
Thread.sleep(delayMs);
} catch (InterruptedException ie) {
}
}
} while (!success);
Run Code Online (Sandbox Code Playgroud)
它可以以任何方式改进吗?例如,等待固定数量(幻数)秒.是否有不同的策略可以产生更好的结果?
注意:将使用几种数据库级技术来确保死锁在实践中非常罕见.此外,应用程序将尝试避免调度同时写入同一个表的线程.上述情况只是"最糟糕的情况".
注意:插入记录的表被组织为堆分区表并且没有索引; 每个线程都会在其自己的分区中插入记录.

这是场景:
我有一个名为MarketDataCurrent(MDC)的表,它有实时更新的股票价格.
我有一个名为'LiveFeed'的进程,它读取从线路流出的价格,排队插入,并使用'批量上传到临时表然后插入/更新到MDC表'.(BulkUpsert)
我有另一个进程,然后读取此数据,计算其他数据,然后使用类似的BulkUpsert存储过程将结果保存回同一个表中.
第三,有许多用户运行C#Gui轮询MDC表并从中读取更新.
现在,在数据快速变化的那一天,事情运行得非常顺利,但是在市场营业时间之后,我们最近开始看到数据库中出现越来越多的死锁异常,现在我们每天看到10-20个.这里要注意的重要一点是,当值不变时会发生这些.
以下是所有相关信息:
表格定义:
CREATE TABLE [dbo].[MarketDataCurrent](
[MDID] [int] NOT NULL,
[LastUpdate] [datetime] NOT NULL,
[Value] [float] NOT NULL,
[Source] [varchar](20) NULL,
CONSTRAINT [PK_MarketDataCurrent] PRIMARY KEY CLUSTERED
(
[MDID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Run Code Online (Sandbox Code Playgroud)
-

我有一个Sql Profiler跟踪运行,捕获死锁,这就是所有图形的样子.

过程258被重复地称为以下'BulkUpsert'存储过程,而73正在调用下一个:
ALTER proc [dbo].[MarketDataCurrent_BulkUpload]
@updateTime datetime,
@source varchar(10)
as
begin transaction
update c with (rowlock) set LastUpdate = getdate(), Value = t.Value, …Run Code Online (Sandbox Code Playgroud) deadlock sql-server-2005 bulk primary-key database-deadlocks
在MySQL + InnoDB中,假设我有一个表,并且两个线程都执行“ SELECT ... FOR UPDATE”。假设两个SELECT语句最终都选择多行,例如它们两个最终都选择行R42和R99。这可能会陷入僵局吗?
我在想这种情况:第一个线程尝试锁定R42,然后锁定R99,第二个线程尝试锁定R99,然后锁定R42。如果我不走运,两个线程将陷入僵局。
当事务锁定多个表中的行时(通过诸如UPDATE或SELECT ... FOR UPDATE之类的语句),但顺序相反,则会发生死锁。...
为了减少死锁的可能性,...在SELECT ... FOR UPDATE和UPDATE ... WHERE语句中使用的列上创建索引。
这暗示在我的情况(单表)中,我不会死锁,可能是因为MySQL自动尝试按主键的顺序锁定行,但是我想确定一下,并且我无法在行中找到合适的位置。文档,告诉我确切的情况。
以下是"SHOW ENGINE INNODB STATUS"的几行回应;
TRANSACTIONS
------------
Trx id counter 58EC54C6
Purge done for trx's n:o < 58EC54C3 undo n:o < 0
History list length 2420
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 58EC51E6, not started
MySQL thread id 520131, OS thread handle 0x7f0db930e700, query id 24011015 108.89.56.87 xyz
---TRANSACTION 58EC527E, not started
MySQL thread id 520061, OS thread handle 0x7f0dbb596700, query id 24011370 108.89.56.87 xyz
---TRANSACTION 58EC53AC, not started
MySQL thread id 520065, OS thread handle 0x7f0dbb28a700, query id 24012094 …Run Code Online (Sandbox Code Playgroud) 我使用 SQL Server 已经很长时间了,当表之间存在逻辑连接时,我总是使用 FK 和索引
例子:
MyTable1
{
ID BIGINT IDENTITY (1, 1) NOT NULL,
SomeData NVARCHAR(255) NOT NULL,
MyFK BIGINT NULL -- this is a FK to MyTable2.ID
}
MyTable2
{
ID BIGINT IDENTITY (1, 1) NOT NULL,
SomeData NVARCHAR(255) NOT NULL
}
Run Code Online (Sandbox Code Playgroud)
现在问题来了,当我在 MyTable1 上执行一些更新 MyFK 的批量更新操作,同时对 MyTable2 执行插入语句时,我们会挂起直到发生超时或更新完成并释放锁。
据我所知,在插入具有 FK 的表时,数据库引擎需要锁定相关表以验证 FK,这就是问题的根源。
我试图解决问题的事情:
删除了表http://msdn.microsoft.com/en-us/library/ms184286%28v=sql.105%29.aspx上的锁升级选项
将索引上的锁更改为基于行而不是基于页面 http://msdn.microsoft.com/en-us/library/ms189076%28v=sql.105%29.aspx
这两种解决方案都导致我陷入僵局和糟糕的性能。
当我删除 FK 时,一切正常,但存在数据损坏的风险。
问题:
sql-server foreign-keys sql-server-2008-r2 database-deadlocks
我执行此存储过程时遇到死锁:
-- Delete transactions
delete from ADVICESEQUENCETRANSACTION
where ADVICESEQUENCETRANSACTION.id in (
select TR.id from ADVICESEQUENCETRANSACTION TR
inner join ACCOUNTDESCRIPTIONITEM IT on TR.ACCOUNTDESCRIPTIONITEMID = IT.id
inner join ACCOUNTDESCRIPTION ACC on IT.ACCOUNTDESCRIPTIONID = ACC.id
inner join RECOMMENDATIONDESCRIPTION RD on ACC.RECOMMENDATIONDESCRIPTIONID = RD.id
inner join RECOMMENDATION REC on REC.id = RD.RECOMMENDATIONID
inner join ADVICESEQUENCE ADV on ADV.id = REC.ADVICESEQUENCEID
where adv.Id = @AdviceSequenceId AND (@RecommendationState is NULL OR @RecommendationState=REC.[State])
);
Run Code Online (Sandbox Code Playgroud)
这是表的架构:

这是死锁图:

因此,当我检索ressource节点的associatedobjid时,我确定它是主键和表AdviceSequenceTransaction的索引:
SELECT OBJECT_SCHEMA_NAME([object_id]), * ,
OBJECT_NAME([object_id])
FROM sys.partitions
WHERE partition_id …Run Code Online (Sandbox Code Playgroud) 我相信SELECTSQL Server 中的每个语句都会导致放置共享锁或密钥锁.但它会在交易中放置相同类型的锁吗?共享锁或密钥锁是否允许其他进程读取相同的记录?
例如,我有以下逻辑
Begin Trans
-- select data that is needed for the next 2 statements
SELECT * FROM table1 where id = 1000; -- Assuming this returns 10, 20, 30
insert data that was read from the first query
INSERT INTO table2 (a,b,c) VALUES(10, 20, 30);
-- update table 3 with data found in the first query
UPDATE table3
SET d = 10,
e = 20,
f = 30;
COMMIT;
Run Code Online (Sandbox Code Playgroud)
此时我的select语句仍会创建共享锁或密钥锁,还是会升级为独占锁?其他事务是否能够从table1读取记录,还是所有事务都会等到我的事务被提交,然后其他事务才能从中进行选择?
在一个应用程序中它是否因为将select语句移到事务之外而只是将插入/更新保留在一个事务中?
我在我的应用程序中偶尔遇到死锁,因为两个事务需要更新相同的行但顺序不同(例如,事务 A 更新行X然后是Y,而事务 B 更新行Y然后X)。
由于各种原因,解决避免这种死锁的传统方法(锁定或以一致的顺序更新行)并不理想。
由于我尝试执行的更新在其他方面是幂等的且与顺序无关,那么简单地在应用程序级别捕获这些偶尔的死锁并重试事务是否安全和合理?
例如:
def process_update(update):
attempt = 0
while attempt < 10:
try:
execute("SAVEPOINT foo")
for row in update:
execute("UPDATE mytable SET … WHERE …", row)
execute("RELEASE SAVEPOINT foo")
break
except Deadlock:
execute("ROLLBACK TO SAVEPOINT foo")
attempt += 1
raise Exception("Too many retries")
Run Code Online (Sandbox Code Playgroud)
这是一个合理的想法吗?或者是否有与 Postgres 的死锁检测相关的成本可能使其变得危险?
我最初的问题是更新SQL数据库时经常遇到死锁。通过一些研究,我发现我可以定义一个自定义DbConfiguration并使用它来定义一个DbExecutionStrategy,它指示Entity Framework在x毫秒和y次重复出现某些错误后自动重试。大!
因此,按照https://msdn.microsoft.com/zh-cn/data/jj680699上的指南,我构建了正在使用的自定义DbConfiguration,但是似乎忽略了关联的DbExecutionStrategy。
最初,我的整个DbConfiguration被忽略了,但是我发现这是因为我在app.config中引用了它,并使用DbConfigurationType属性[DbConfigurationType(typeof(MyConfiguration))]装饰了我的实体构造函数。现在,我仅使用app.config,至少正在调用我的自定义配置。
以最简单的形式,我的自定义配置如下所示:
public class MyConfiguration : DbConfiguration
{
public MyConfiguration()
{
System.Windows.MessageBox.Show("Hey! Here I am!"); //I threw this in just to check that I was calling the constructor. Simple breakpoints don't seem to work here.
SetExecutionStrategy("System.Data.SqlClient", () => new MyExecutionStrategy(3, TimeSpan.FromMilliseconds(500)));
}
}
Run Code Online (Sandbox Code Playgroud)
我的app.config中引用了我的自定义DbConfiguration,如下所示:
<entityFramework codeConfigurationType="MyDataLayer.MyConfiguration, MyDataLayer">
...
</entityFramework>
Run Code Online (Sandbox Code Playgroud)
我的自定义DbExecutionStrategy的构建方式如下:
private class MyExecutionStrategy : DbExecutionStrategy
{
public MyExecutionStrategy() : this(3, TimeSpan.FromSeconds(2))
{
System.Windows.MessageBox.Show($"MyExecutionStrategy instantiated through default constructor.");
}
public MyExecutionStrategy(int maxRetryCount, TimeSpan maxDelay) : base(maxRetryCount, …Run Code Online (Sandbox Code Playgroud) 这个问题可以被视为对我的评论的后续跟进可以两个并发但相同的DELETE语句导致死锁吗?.
我想知道my_status在以下语句中行是否按升序锁定:
SELECT 1 FROM my_table ORDER BY my_status FOR UPDATE;
Run Code Online (Sandbox Code Playgroud)
https://www.postgresql.org/docs/9.5/static/sql-select.html上有一句有趣的评论说:
SELECT在READ COMMITTED事务隔离级别运行的命令以及使用ORDER BY和锁定子句可能无序地返回行.这是因为ORDER BY首先应用.该命令对结果进行排序,但可能会阻止尝试获取一个或多个行的锁定.一旦SELECT疏导,一些排序列值可能已被修改,导致出现被淘汰的顺序(尽管它们是为了在原有的列值而言)的行.例如,可以通过将FOR UPDATE/SHARE子句放在子查询中来根据需要解决这个问题Run Code Online (Sandbox Code Playgroud)SELECT * FROM (SELECT * FROM mytable FOR UPDATE) ss ORDER BY column1;
我不确定这是否能回答我的问题.所有这些都是ORDER BY首先应用的,并且您需要放入FOR UPDATE子查询来解决副作用,如果在此期间更改了订单列的值,实际输出顺序可能会有所不同.换句话说,放入FOR UPDATE子查询可确保在订购之前发生锁定.
但这并没有真正告诉我们这些行是否实际上是按照ORDER BY条款确定的顺序锁定的?
sql-server ×4
sql ×3
deadlock ×2
mysql ×2
postgresql ×2
algorithm ×1
bulk ×1
c# ×1
foreign-keys ×1
ingres ×1
innodb ×1
java ×1
locks ×1
primary-key ×1
scenarios ×1
transactions ×1