我做了这个测试,从两个线程中选择一个我之前创建的行:
CREATE TABLE `customers` (
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`),
) ENGINE=InnoDB
var t1 = new Thread(new ThreadStart(delegate()
{
using (var conn = new MySqlConnection("Server=localhost;Database=test;Uid=root;Pwd=test;"))
{
conn.Open();
using (var trans = conn.BeginTransaction())
{
using (var cmd = new MySqlCommand("select id from customers where id = 8534 FOR UPDATE;", conn, trans))
{
using (var reader = cmd.ExecuteReader())
{
Console.WriteLine("Enter t1: " + reader.Read());
Thread.Sleep(2000);
Console.WriteLine("Exit t1");
}
}
}
};
}));
var t2 = new Thread(new ThreadStart(delegate()
{
using (var conn = new MySqlConnection("Server=localhost;Database=test;Uid=root;Pwd=test;"))
{
conn.Open();
using (var cmd = new MySqlCommand("select id from customers where id = 8534", conn))
{
Console.WriteLine("Enter t2: " + cmd.ExecuteScalar());
Console.WriteLine("Exit t2");
}
}
}));
t1.Start();
Thread.Sleep(400);
t2.Start();
t1.Join();
t2.Join();
我得到的结果是:
Enter t1: True
Enter t2: 8534
Exit t2
Exit t1
线程1中的FOR UPDATE是否应该阻止线程2在释放事务之前读取该行?
答案 0 :(得分:5)
线程1中的
FOR UPDATE
是否应该阻止线程2在释放事务之前读取该行?
没有
但是它会阻止线程2写入这一行(或者用FOR UPDATE
子句读取它。)
在默认事务隔离级别(即REPEATABLE READ
)中,SELECT
语句不会对它们读取的行放置任何锁定。
要锁定SELECT
语句,您应该指示它锁定(使用FOR UPDATE
,LOCK IN SHARE MODE
或将读者的事务隔离级别设置为SERIALIZABLE
。)
来自docs:
一致性读取是
InnoDB
处理SELECT
和READ COMMITTED
隔离级别REPEATABLE READ
语句的默认模式。一致读取不会对其访问的表设置任何锁定,因此其他会话可以在对表执行一致读取的同时自由修改这些表。
和
InnoDB
对INSERT INTO ... SELECT, UPDATE ... (SELECT)
和CREATE TABLE ... SELECT
等select语句使用一致读取,如果FOR UPDATE
未指定LOCK IN SHARE MODE
或innodb_locks_unsafe_for_binlog
}选项已设置且事务的隔离级别未设置为SERIALIZABLE
。因此,在从所选表读取的行上没有设置锁。否则,InnoDB使用更强的锁,SELECT
部分的行为类似READ COMMITTED
,其中每个一致的读取,即使在同一事务中,也会设置和读取自己的新快照。