C# 使用TransactionScope处理MySQL和读锁
我有以下情况: 如果我有一个MySQL数据库和一个InnoDB表,我用它来存储唯一的数字。 我启动一个事务,读取值(例如1000471),将该值存储在另一个表中,并更新递增的值(100472)。现在我想避免在我的事务运行时,其他人甚至读取该值 如果我使用纯MySQL,我会这样做: 例外(“锁定tbl1读取”)C# 使用TransactionScope处理MySQL和读锁,c#,mysql,subsonic,transactions,transactionscope,C#,Mysql,Subsonic,Transactions,Transactionscope,我有以下情况: 如果我有一个MySQL数据库和一个InnoDB表,我用它来存储唯一的数字。 我启动一个事务,读取值(例如1000471),将该值存储在另一个表中,并更新递增的值(100472)。现在我想避免在我的事务运行时,其他人甚至读取该值 如果我使用纯MySQL,我会这样做: 例外(“锁定tbl1读取”) 执行(“从tbl1中选择…”) 执行(“插入tbl2”) 执行(“解锁表”) 但是由于我使用亚音速作为DAL,并且代码应该独立于mysql,所以我必须使用TransactionScope
执行(“从tbl1中选择…”)
执行(“插入tbl2”)
执行(“解锁表”) 但是由于我使用亚音速作为DAL,并且代码应该独立于mysql,所以我必须使用TransactionScope 我的代码:
TransactionOptions TransOpt = new TransactionOptions();
TransOpt.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
TransOpt.Timeout = new TimeSpan(0, 2, 0);
using (TransactionScope ts = new TransactionScope(TransactionScopeOption.RequiresNew, TransOpt))
{
// Select Row from tbl1
// Do something
ts.Complete();
}
根据TransactionOptions的帮助
我想要达到的效果可以通过IsolationLevel.ReadCommitted实现,但我仍然可以从事务外部读取行(如果我尝试更改它,我会得到一个锁,因此事务可以工作)
有人有什么建议吗?使用TransactionScope是否也可以进行读取锁定?我的第一个猜测是使用
选择进行更新,在快速搜索之后,我在MySQL 5参考中找到了一个关于的页面
如果我理解正确,这与使用的隔离级别无关。注意——隔离级别只是告诉当前事务如何受到其他事务更改的影响。它不告诉其他事务可以做什么。但是,更高的隔离级别需要更多的限制锁。我的第一个猜测是使用SELECT for UPDATE
,在快速搜索之后,我在MySQL 5参考中找到了一个关于的页面
如果我理解正确,这与使用的隔离级别无关。注意——隔离级别只是告诉当前事务如何受到其他事务更改的影响。它不告诉其他事务可以做什么。但是,更高的隔离级别需要更多的限制锁。因为我没有找到一种方法来锁定一行,以便使用InnoDB和TransactionScope进行读取(我可能错了),这应该可以工作:
如果我同时运行两个事务(没有TransactionOptions),其中一个事务完成,那么另一个事务由于“死锁”异常而无法完成
根据MySQL的说法,最好的做法不是避免这种异常,而是期望出现死锁并重新启动事务
如果设置:
TransactionOptions TransOpt = new TransactionOptions();
TransOpt.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
对于您的事务,您不会得到死锁异常,但在我的情况下,这将导致一个复杂的唯一数字,这更糟糕。由于我没有找到一种方法来锁定一行,以便使用InnoDB和TransactionScope进行读取(我可能错了),这应该可以工作:
如果我同时运行两个事务(没有TransactionOptions),其中一个事务完成,那么另一个事务由于“死锁”异常而无法完成
根据MySQL的说法,最好的做法不是避免这种异常,而是期望出现死锁并重新启动事务
如果设置:
TransactionOptions TransOpt = new TransactionOptions();
TransOpt.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted;
对于您的事务,您不会遇到死锁异常,但在我的情况下,这将导致一个复杂的唯一数字,更糟糕。如果有人感兴趣,这就是TransactionOptions对MySql的影响:
假设我有两种方法
Method1启动一个事务,从我的表中选择一行,增加值并更新表
方法2是相同的,但在选择和更新之间,我增加了1000毫秒的睡眠时间
现在假设我有以下代码:
Private Sub Button1_Click(sender as Object, e as System.EventArgs) Handles Button1.Click
Dim thread1 As New Threading.Thread(AddressOf Method1)
Dim thread2 As New Threading.Thread(AddressOf Method2)
thread2.Start() // I start thread 2 first, because this one sleeps
thread1.Start()
End Sub
如果没有事务,则会发生这种情况:
thread2启动,读取值5,然后休眠,
thread1启动,读取值5,将值更新为6,
thread2也将该值更新为6
效果:我有两次唯一的数字
我想要的:
thread2启动,读取值5,然后休眠,
thread1启动,尝试读取值,但获得锁并休眠,
thread2将值更新为6,
thread1继续,读取值6,将值更新为7
这就是如何使用TransactionScope启动事务:
TransactionOptions Opts = new TransactionOptions();
Opts.IsolationLevel = IsolationLevel.ReadUncommitted;
// start Transaction
using (TransactionScope ts = new TransactionScope(TransactionScopeOption.RequiresNew, Opts))
{
// Do your work and call complete
ts.Complete();
}
它甚至可以管理分布式事务。如果抛出异常,则永远不会调用ts.Complete,并且作用域的Dispose()部分回滚事务
以下是不同隔离级别如何影响事务的概述:
- 隔离级别。混乱
抛出NotSupportedException-不支持混沌隔离级别
- 隔离级别。重新提交
事务不会相互干扰(两次相同的读取,错误)
- IsolationLevel.ReadUncommitted
事务不会相互干扰(两次相同的读取,错误)
- 隔离级别。可重复读取
事务不会相互干扰(两次相同的读取,错误)
- 隔离级别.可序列化
尝试获取锁时抛出MySqlException-Deadlock;在更新期间尝试重新启动事务
- 隔离级别。快照
抛出MySqlException-您的SQL语法中有错误;检查与您的MySQL服务器版本对应的手册,了解在连接过程中在第1行的“”附近使用的正确语法。Open()
- 隔离级别。未指定
尝试获取锁时抛出MySqlException-Deadlock;在更新期间尝试重新启动事务
- 未设置交易选项
尝试获取锁时抛出MySqlException-Deadlock;在更新期间尝试重新启动事务
如果有人感兴趣,这就是TransactionOptions对MySql的影响:
假设我有两种方法
Method1启动一个事务,从我的表中选择一行,增加值并更新表
方法2相同,但在选择
// start transaction
using (TransactionScope ts = new TransactionScope(TransactionScopeOption.RequiresNew))
{
using (SharedDbConnectionScope scope = new SharedDbConnectionScope(DB._provider))
{
try
{
Record r = new InlineQuery().ExecuteAsCollection<RecordCollection>(
String.Format("SELECT * FROM {0} WHERE {1} = ?param FOR UPDATE",
Record.Schema.TableName,
Record.Columns.Column1), "VALUE")[0];
// do something
r.Save();
}
}
}