将 TransactionScope 与 MySQL 和读锁结合使用
我有以下情况:
如果有一个带有 InnoDB 表的 MySQL 数据库,我用它来存储唯一的数字。 我启动一个事务,读取值(例如 1000471),将该值存储在另一个表中并更新增量值(100472)。 现在我想避免其他人在我的事务运行时读取该值。
如果我使用普通 MySQL,我会这样做:
Exceute("LOCK tbl1 READ");
执行("从 tbl1 选择 ...");
执行(“插入到tbl2”);
执行(“解锁表”);
但由于我使用 SubSonic 作为 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
的帮助system.transactions.isolationlevel
我想要达到的效果可以用 IsolationLevel.ReadCommited 来实现,但我仍然可以从事务外部读取该行(如果我尝试更改它,我会获得锁定,因此事务正在工作)
有人有建议吗? TransactionScope 是否可以实现读锁
I have the following situation:
If have a MySQL db with a InnoDB table which I use to store unique numbers.
I start a transaction, read the value (eg. 1000471), store this value in another table and update the incremented value (100472). Now I want to avoid that somebody else even reads the value while my transaction is running.
If I would use plain MySQL I would do something like this:
Exceute("LOCK tbl1 READ");
Execute("SELECT ... from tbl1");
Execute("INSERT into tbl2");
Execute("UNLOCK TABLES");
but since I am using SubSonic as a DAL and the code should be independent from mysql, I have to use the TransactionScope.
My code:
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();
}
According to the help of TransactionOptions
system.transactions.isolationlevel
The effect I want to reach could be implemented with IsolationLevel.ReadCommitted, but I can still read the row from outside the transaction (If I try to change it, I get a lock, so the transaction is working)
Does anybody has a suggestion? Is a read lock even possible with TransactionScope
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
如果有人感兴趣,这就是 TransactionOptions 如何影响 MySql:
可以说我有两种方法。
Method1 启动一个事务,从表中选择一行,增加值并更新表。
方法2是一样的,但是在select和update之间我添加了1000ms的睡眠。
现在假设我有以下代码:
如果没有事务,就会发生这种情况:
thread2 启动,读取值 5,然后休眠,
线程1启动,读取值5,将值更新为6,
thread2 也将值更新为 6。
效果:我有两次唯一的号码。
我想要什么:
thread2 启动,读取值 5,然后休眠,
thread1 启动,尝试读取值,但获得锁并休眠,
thread2将值更新为6,
thread1 继续,读取值 6,将值更新为 7
这就是如何使用 TransactionScope 启动事务:
甚至可以管理分布式事务。 如果抛出异常,则永远不会调用 ts.Complete,并且作用域的 Dispose() 部分将回滚事务。
以下概述了不同的 IsolationLevels 如何影响事务:
抛出 NotSupportedException - 不支持混沌隔离级别
事务不会互相干扰(两次相同的读取,不好)
事务不会相互干扰(两次相同的读取,不好)
事务不会互相干扰(两次相同的读取,不好)
抛出 MySqlException - 尝试获取锁时发现死锁; 尝试在更新期间重新启动事务
抛出 MySqlException - SQL 语法有错误; 检查与您的 MySQL 服务器版本相对应的手册,了解在 Connection.Open() 期间第 1 行 '' 附近使用的正确语法
抛出 MySqlException - 尝试获取锁时发现死锁; 尝试在更新期间重新启动事务
抛出 MySqlException - 尝试获取锁时发现死锁; 尝试在更新期间重新启动事务
If anyone is interested, this is how TransactionOptions affect MySql:
Lets say I have two methods.
Method1 starts a transaction, selects a row from my table, increments the value and updates the table.
Method2 is the same, but between select and update I added a sleep of 1000ms.
Now imagine I have the following code:
Without transactions this would happen:
thread2 starts, reads the value 5, then sleeps,
thread1 starts, reads the value 5, updates the value to 6,
thread2 updates the value to 6, too.
Effect: I have the unique number two times.
What I want:
thread2 starts, reads the value 5, then sleeps,
thread1 starts, trys to reads the value, but get a lock and sleeps,
thread2 updates the value to 6,
thread1 continues, reads the value 6, updates the value to 7
That's how to start transaction with the TransactionScope:
That can even manage distributed transactions. If an Exception is thrown ts.Complete is never called and the Dispose() Part of the Scope rolls back the transaction.
Here's an overview how the different IsolationLevels affect the transaction:
Throws a NotSupportedException - Chaos isolation level is not supported
The transactions do not interfere each other (two identical reads, bad)
The transactions do not interfere each other (two identical reads, bad)
The transactions do not interfere each other (two identical reads, bad)
Throws a MySqlException - Deadlock found when trying to get lock; try restarting transaction during Update
Throws a MySqlException - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1 during Connection.Open()
Throws a MySqlException - Deadlock found when trying to get lock; try restarting transaction during Update
Throws a MySqlException - Deadlock found when trying to get lock; try restarting transaction during Update
我的第一个猜测是使用
SELECT FOR UPDATE
,经过快速搜索后,我找到了一个关于锁定读取。如果我理解正确的话,这与使用的隔离级别无关。 请注意 - 隔离级别仅说明当前事务如何受到其他事务更改的影响。 它没有告诉其他交易可以做什么。 然而,更高的隔离级别需要更多限制性的锁。
My first guess was to use
SELECT FOR UPDATE
and after a quick search I found a page about locking reads in the MySQL 5 reference.If I understand correctly this is independent from the isolation level used. And take care - the isolation level just tells how the current transaction is affected by changes in other transactions. It does not tell what other transactions can do. However, more restricting locks are required for higher isolation levels.
由于我没有找到一种方法来锁定一行以使用 InnoDB 和 TransactionScope 进行读取(我可能是错的),这应该可以工作:
如果我同时运行两个事务(没有 TransactionOptions)并且一个完成,另一个则无法完成,因为“死锁”异常。
根据 MySQL 文档,预计会出现死锁并重新启动事务。
如果您设置:
对于您的事务,您不会遇到死锁异常,但是,就我而言,这会导致重复的唯一编号,这更糟糕。
As I didn't find a way to lock a row for reading with InnoDB and TransactionScope (I may be wrong) this should work:
If I run two Transactions simultaniously (without TransactionOptions) and one finishes, the other cannot compleate because of a "Deadlock" Exception.
Instead of avoiding this exception it seems to be best practice, according to the MySQL documentation, to expect a deadlock and restart the transaction.
if you set:
for your transaction you don't get the deadlock exception, but, in my case, this would result in a dublicate unique number, which is worse.
由于 SubSonic 似乎不支持 SELECT ... FOR UPDATE ,并且在我看来使用隔离级别将是一种误用 - 那么拥有一个 用户定义函数,它返回一个新的 id? 该函数将使用 SELECT ... FOR UPDATE 读取 tbl1 中的当前值,更新该行并返回该值。
在您插入新值的应用程序代码中,您只需使用:
Since SubSonic doesn't seem to support
SELECT ... FOR UPDATE
and using isolation levels would be a misuse in my opinion - what about having a user defined function, which returns a new id? This function would read the current value from tbl1 with aSELECT ... FOR UPDATE
, udpate the row and return the value.In your application code where you insert a new value you would just use:
好吧,我决定使用“后门”,现在使用内联查询:
我同时启动了 10 个线程,它按预期工作。 感谢鲁道夫森的“选择更新”提示。
Ok, I decided to use the "backdoor" and now use a inline query:
I started 10 threads simultaniously and it works as expected. Thx to rudolfson for the "SELECT FOR UPDATE" hint.