我认为这是一个常见的问题,但我还没有找到任何解决方案,也许我没有在谷歌上正确地搜索这个问题。总之,我有一个在表中插入多行的过程(在同一个事务中的许多其他事情中),但是这个过程是在多个线程和多个服务器中执行的。
TABLE: COVERAGES
COLUMNS: COV_Id, COV_Description
描述是唯一的,但不作为数据库(旧版)中的约束,我想避免插入重复的描述。我已经隔离了搜索并插入到一个独立的事务中,我想在选择之前锁定表,如果它不存在,则在“保存”之后释放它。
我想要这样的东西(高水平):
{
this.Lock(Coverage); // Lock table Coverages to avoid select out of this transaction
Coverage coverage = session.QueryOver<Coverage>().Where(g => g.Description == description).Take(1).SingleOrDefault();
if (coverage == null)
{
this.Save(new Coverage { Description = description });
}
return coverage;
};
我不能使用 C# 的锁定指令,因为该过程在多个服务器中执行,并且我不能使用 NHibernate 的锁定指令,因为当我没有结果时,我恰恰想阻止。
对于SqlServer和Oracle,我使用的是NHibernate 3.3。
您可以使用< code>IsolationLevel用事务锁定您的表。可序列化。
using (var t = session.BeginTransaction(IsolationLevel.Serializable))
{
var coverage = session.QueryOver<Coverage>()
.Where(g => g.Description == description)
.Take(1).SingleOrDefault();
if (coverage == null)
{
coverage = new Coverage { Description = description };
session.Save(coverage);
}
t.Commit();
return coverage;
}
为了限制锁争用,这要求您的表在Description
上有一个索引,并且该索引实际上由读取查询使用。否则,它将锁定整个表,而不是仅锁定“附近”Description
值。点击此处阅读更多信息。有关比.Net Framework更完整的官方文档,请阅读此处和此处。
如果两个或多个进程(或线程)试图执行并发的冲突插入1,它们都将遇到死锁。除了一个将作为死锁受害者回滚。剩下的一个将继续。
死锁发生在插入上,而不是在选择上。所有进程都将挂在插入件上,所有进程都结束回滚,除了一个。这可确保不会插入重复项。
这意味着用于处理的完整代码更加详细。
while (true)
{
using (var session = sessFactory.OpenSession())
{
try
{
using (var t = session.BeginTransaction(IsolationLevel.Serializable))
{
var coverage = session.QueryOver<Coverage>()
.Where(g => g.Description == description)
.Take(1).SingleOrDefault();
if (coverage == null)
{
coverage = new Coverage { Description = description };
session.Save(coverage);
}
t.Commit();
// Breaks the loop by the way.
return coverage;
}
}
catch (GenericADOException ex)
{
// SQL-Server specific code for identifying deadlocks
var sqlEx = ex.InnerException as SqlException;
if (sqlEx == null || sqlEx.Number != 1205)
throw;
// Deadlock, just try again by letting the loop go on (eventually
// log it).
}
}
}
注意:< br> 1。根据DB锁定的范围,不仅仅是关于要插入的实际值的冲突。强烈建议使用适当的索引来缩小这个范围。如果没有,可能会导致整个表被锁定,从而导致并发插入不同值的能力非常差。
我终于在数据库上实现了信号量来解决这个问题。正如我在上面与 Frédric 的“讨论”中提到的,我需要在 select 处锁定线程以避免重复插入,可序列化隔离级别锁定在 INSERT 上,并在 SQL Server 上的并发调用中调用插入时引发死锁异常。通过其他方式在 Oracle 上抛出错误 08177。00000 - “无法序列化此事务的访问”,或者继续等待另一个事务的结束,插入稍后复制的值(请参阅下面的示例 SQL)。
所以解决方案是这样的:
public Coverage CreateCoverageSessionIsolated(string description, out bool isNew)
{
Coverage coverage = null;
bool _isNew = false;
this.ExecuteOnNewSession((session) =>
{
this.semphoresDao.LockSemaphore(session, "SMF_COVERAGES");
coverage = session.QueryOver<Coverage>()
.Where(g => g.Description == description)
.Take(1)
.SingleOrDefault();
_isNew = coverage == null;
if (coverage == null)
{
coverage = new Coverage { Description = description };
this.Save(coverage);
}
});
isNew = _isNew;
return coverage;
}
我对实际代码进行了一点修改,以更好地理解。
我已经尝试过,并且在SQL Server和Oracle上运行良好。
编辑:为了检查可序列化事务的解决方案不适合我,我在两个并行事务上使用简单的SQL代码,逐级并行执行:
BEGIN TRAN; -- ONLY FOR SQL
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
SELECT COV_ID FROM COVERAGES WHERE COV_DESCRIPTION = 'testcov';
INSERT INTO COVERAGES (COV_DESCRIPTION) VALUES ('testcov');
COMMIT;
问题内容: 某些数据库功能(例如和)很容易受到死锁的影响,因为数据库未指定将使用哪种锁定顺序。我发现有两次 讨论暗示此行为不是SQL标准指定的,更不用说具体的实现了。因此,我在假设我们无法控制锁定顺序的情况下进行操作(至少,这样做并不明显)。 如果我们不能依赖锁定顺序,应该如何避免数据库死锁? 如果我们不应该避免僵局(您将不得不非常努力地说服我),那么我们应该怎么做? 这个问题与数据库无关,所以请
我有一个非常简单的表,有3列,我需要一个尽可能轻量级的查询,只有当列有新值时才能插入。 如何编写sql查询来实现这一点?我在网站上已经看到了一些例子,但它们都被我无法理解的更复杂的查询(一些涉及子查询)所混淆。 似乎有不同的方法来做这件事,我需要找到最轻量级的一个,这样我就可以在循环中重复它,一次插入多个标签,而不会给服务器带来太大的压力。
我有一个严重的问题,我没有弄清楚。我有一个表名叫做“结果”。我想要实现的是,当我插入新记录时,它会正确地插入数据库。 在这个水平上,我的脚本运行良好。但是我想第二次点击添加新结果记录时,如果之前输入了数据,那么它将显示我的数据,如果我想更新我的数据,我可以。如果以前没有输入数据,那么我会将数据插入数据库。我成功地限制用户输入重复数据,但我没有成功地在同一页上显示数据。 我有一个显示测试页面,当我点
我有一个简单的项目,该项目将个人信息提供给数据库(sqlite),现在我想避免插入重复值(按名称),我不能做什么?谢谢你们
问题内容: 我有一个包含3列的表格- id(pk),pageId(fk),名称。我有一个PHP脚本,它将大约5000条记录转储到表中,其中大约一半是重复的,具有相同的pageId和名称。pageId和名称的组合应该是唯一的。当我遍历php中的脚本时,防止重复项被保存到表中的最佳方法是什么? 问题答案: 第一步是在表上设置唯一键: 然后,当有重复项时,您必须决定要做什么。你应该: 忽略它? 覆盖先前
如何避免在PHP MYSQLi中插入重复记录?这是我的剧本: