我有以下场景:
- 用户 X 从位置 lc1 登录到应用程序:调用它Ulc1
- 用户 X(已被黑客攻击,或者他的某个朋友知道他的登录凭据,或者他只是从他计算机上的不同浏览器登录,等等。你明白了)从位置 lc2 同时登录:调用它Ulc2
我正在使用一个主 servlet:
- 从数据库池获取连接
- 将自动提交设置为 false
- 执行穿过应用程序层的命令:如果全部成功,则在“finally”语句中将 autocommit 设置为 true,然后关闭连接。否则如果发生异常,则回滚()。
在我的数据库(mysql/innoDb)中,我有一个“历史”表,其中包含行列:
id(主键)|用户名|日期 |主题 |锁定
“locked”列的默认值为“false”,它用作标记特定行是否被锁定的标志。
每行都特定于一个用户(正如您从用户名列中看到的那样)
回到场景:
-->Ulc1 发送命令至update他的历史记录来自日期“D”和主题“T”的数据库。
-->Ulc2 发送same命令到update数据库的历史记录same日期“D”和same主题“T”位于完全相同的时间。
我想实现一个 mysql/innoDB 锁定系统,该系统将使到达的任何线程都可以执行以下检查:
该行的列“锁定”是否正确?
- 如果为 true,则向用户返回一条消息“他已经从另一个位置更新相同的数据”
- 如果不是 true(即未锁定):将其标记为锁定并更新,然后在完成后将锁定重置为 false。
这两种 mysql 锁定技术中的哪一种实际上将允许第二个到达的线程读取锁定列的“更新”值来决定要采取的 wt 操作?
我应该使用“用于更新” or “锁定共享模式”?
这个场景解释了我想要实现的目标:
- Ulc1 线程先到达:列“locked”为 false,将其设置为 true 并继续更新过程
- Ulc2 线程在 Ulc1 的事务仍在处理中时到达,即使该行通过 innoDb 功能锁定,它也不必等待,但实际上读取了锁定列的“新”值,即“true”,等等事实上不必等到 Ulc1 事务提交来读取“锁定”列的值(无论如何,到那时该列的值已经被重置为 false)。
我对这两种类型的锁定机制不是很有经验,到目前为止我所理解的是 LOCK IN SHARE MODE 允许其他事务读取锁定的行,而 FOR UPDATE 甚至不允许读取。但是这个读取是否得到了更新后的值?或者第二个到达的线程必须等待第一个线程提交然后读取该值?
任何关于在这种情况下使用哪种锁定机制的建议都是值得赞赏的。
另外,如果有更好的方法来“检查”该行是否已被锁定(除了使用真/假列标志之外),请告诉我。
谢谢
SOLUTION
(基于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);