微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

mysql – “FOR UPDATE”v / s“LOCK IN SHARE MODE”:允许并发线程读取锁定行的更新“状态”值

我有以下场景:

>用户X从位置lc1登录到应用程序:将其命名为Ulc1
>用户X(已被黑客攻击,或者他的一些朋友知道他的登录凭证,或者他只是从他的机器上的其他浏览器登录等等.你明白了)从位置lc2同时登录:call它是Ulc2

我正在使用一个主要的servlet:
– 从数据库获取连接
– 将autocommit设置为false
– 执行通过app层的命令:如果全部成功,则在“finally”语句中将autocommit设置为true,然后关闭连接.否则,如果发生异常,则回滚().
在我的数据库(mysql / innoDb)中,我有一个“历史”表,其中包含行列:
id(主键)|用户名|日期|主题|锁定

“已锁定”列认值为“false”,它用作标记特定行是否被锁定的标志.
每行都是特定于用户的(您可以从用户名栏中看到)

所以回到场景:
– > Ulc1发送命令以从日期“D”和主题“T”的db更新其历史.

– > Ulc2发送相同的命令,以便在同一时间从同一日期“D”和相同主题“T”的数据库更新历史记录.

我想实现一个MysqL / innoDB锁定系统,它将启用任何到达的线程进行以下检查:

此行的列“锁定”是否为真?

>如果为true,则向用户返回“他已经从其他位置更新相同数据”的消息
>如果不是真(即未锁定):将其标记为已锁定并更新,然后重置锁定为false,一旦完成.

这两种MysqL锁定技术中的哪一种,实际上允许第二个到达的线程读取锁定列的“更新”值来决定采取的wt动作?我应该使用“FOR UPDATE”还是“LOCK IN SHARE MODE”?
这个场景解释了我想要完成的事情:
– Ulc1线程首先到达:列“已锁定”为false,将其设置为true并继续更新进程
– 当Ulc1的事务仍处于进行状态时,Ulc2线程到达,即使该行通过innoDb功能被锁定,它也不必等待,但实际上读取的列的“新”值为“true”,因此实际上不必等到Ulc1事务提交读取“锁定”列的值(无论如何此时此列的值已经被重置为false).

我对这两种类型的锁定机制不是很熟悉,到目前为止我所理解的是LOCK IN SHARE MODE允许其他事务读取锁定行,而FOR UPDATE甚至不允许读取.但是这个读取是否有更新的值?或者第二个到达的线程必须等待第一个线程提交然后读取值?

有关哪种锁定机制用于此方案的任何建议都值得赞赏.此外,如果有更好的方法来“检查”行是否已被锁定(除了使用真/假列标志),请让我知道它.
谢谢


(基于@Dathazer的答案的Jdbc伪代码示例)

表:[id(主键)|用户名|日期|主题|锁定]

connection.setautocommit(false);
//transaction-1
PreparedStatement ps1 = "Select locked from tableName for update where id="key" and   locked=false);
ps1.executeQuery();

//transaction 2
PreparedStatement ps2 = "Update tableName set locked=true where id="key";
ps2.executeUpdate();
connection.setautocommit(true);// here we allow other transactions threads to see the new value

connection.setautocommit(false);
//transaction 3
PreparedStatement ps3 = "Update tableName set aField="Sthg" where id="key" And date="D" and topic="T";
ps3.executeUpdate();

// reset locked to false
 PreparedStatement ps4 = "Update tableName set locked=false where id="key";
ps4.executeUpdate();

//commit
connection.setautocommit(true);

解决方法:

LOCK IN SHARE MODE将允许第二个线程读取值,但实际值将是查询之前(读取提交)或事务(可重复读取)开始之前(因为MysqL使用多版本;以及必须看到第二个事务是由隔离级别定义的).因此,如果在读取时未提交第一个事务,则将读取旧值.

在你的场景中,最好有1个事务用select for update锁定记录,另一个用于记录,在commit / rollback第三个用于解锁记录.

select for update的第二个线程事务将等待第一个完成,然后将读取实际值并决定不继续其他事务,而是通知用户该记录已被锁定.

为避免死锁,请确保使用唯一索引执行select for update.

示例代码

connection.setautocommit(false);
//transaction-1
PreparedStatement ps1 = "Select locked from tableName for update where id="key" and   locked=false);
ps1.executeQuery();

//transaction 2
PreparedStatement ps2 = "Update tableName set locked=true where id="key";
ps2.executeUpdate();
connection.setautocommit(true); // here we allow other transactions / threads to see the new value

connection.setautocommit(false);
//transaction 3
PreparedStatement ps3 = "Update tableName set aField="Sthg" where id="key" And date="D" and topic="T";
ps3.executeUpdate();

// probably more queries

// reset locked to false
 PreparedStatement ps4 = "Update tableName set locked=false where id="key";
ps4.executeUpdate();

//commit
connection.setautocommit(true);

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐