update语句关联的锁机制与利用数据库构造分布式锁
一、update会锁表吗?
1.oracle中,update会形成行锁
update tb_user set phone=11 where name=”c1”;修改,先别commit事务。
重新开另一个窗口,执行select * from tb_user where name = “c1”,可以进行查询。但执行update tb_user set phone=11 where name=”c1”;命令会显示一直在执行中。当第一个update tb_user set phone=11 where name=”c1”;更新语句提交commit后,第二条语句才会执行。
2.oracle利用该机制实现分布式锁
select * from tb_user where name = “C1” for update;
使用在储存过程中。后续再进行提交commit指令。多个实例需要等待第一个实例完成命令后才能进行操作。
3.java利用该机制实现分布式锁
package work;
import com.alibaba.fastjson.JSON;
import java.util.*;
public class OracleSuoStudy {
// dao层
private OracleStudy oracleStudy;
public void beginSuo(){
try {
// 当前线程名字
String lockValue = Thread.currentThread().getName();
// 得到一个批次号
String batchNo = UUID;
if(this.lock("studySuo",new Date(System.currentTimeMillis() + 1000*60*10),lockValue,'machine1')){
// 将批次号为空的批次更新为UUID,更新的时候保证只有一台实例在运行更新
oracleStudy.updateBatch(UUID);
int seconds = 500 + Integer.decode(UUID.randomUUID().toString());
//执行业务代码
// 根据批次号,将这批数据查出来
List<T> list = oracleStudy.queryDataByBatchNo(UUID);
} esle {
log.info("获取锁失败");
Thread.sleep(seconds);
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 解锁,只能自己解开自己的锁
oracleStudy.unlock("studySuo","machine1");
}
}
private synchronized boolean lock (String configID, Date aviableTime,String lockValue,String owner){
Map map = new HashMap();
map.put("configID",configID);
map.put("aviableTime",aviableTime);
map.put("lockValue",lockValue);
map.put("owner",owner);
// 第一条SQL 查询锁
Long c = oracleStudy.querySuo(map);
//第二条SQL 解超时锁
log.info("先尝试解超时锁" + oracleStudy.updateSwitch(map) + ","+ JSON.toJSONString(map));
boolean flag = false ;
if (c.intValue() == 0){
// 第三条SQL创建一个新锁
oracleStudy.createSuo(map);
flag = true;
} else {
// 第四条SQL,查询锁的状态
String value = oracleStudy.querySuoStatus(map);
if (value.equals("1")){
// 已经加锁
flag = false;
} else {
// 第五条SQL 尝试加锁
flag = oracleStudy.updateSuostatus(map);
}
}
return flag;
}
}
Dao层SQL代码
---查看是否存在锁
@Select("select count(1) from config_info where config_type = 'djssJobType' and config_id = 'smsSendLock'")
一、querySuo
---尝试解开超时锁
@update("update config_info set config_value = '0' ,updated_date = sysdate where config_type = 'dissJobType' and config_id = 'smsSendLock' and (updated_date < sysdate -1 or Avilable_date < sysdate)")
二、updateSwitch
---第一次执行的时候需要创建锁
@insert("insert into config_info (config_type,config_id,config_value,available_date) values ('djssJobType','smsSendInitLock','1','6000000' || sysdate)")
三、createSuo
----查看锁的状态
@select("select config_value from config_info where config_type = 'djssJobType' and config_id = 'smsSendInitLock'")
四、querySuoStatus
---若锁的状态没有加锁,则对其进行加锁操作
@update("update config_info set config_value = '1',updated_date = sysdate,available = '时间',owner = 'machine1' where config_type = 'djssJobType' and config_value = '0' and config_id = 'smsSendInitLock'")
五、updateSuostatus
@update("update config_info set config_value = '0',updated_date = sysdate where config_type = 'djssJobType' and onwer = 'machine1' and config_id = 'smsSendInitLock'"
)
六、unlock解锁操作
2.不带索引
运行命令:begin;开启事务,然后运行命令:update tb_user set phone=11 where name=”c1”;修改,先别commit事务。
再开一个窗口,直接运行命令:update tb_user set phone=22 where name=”c2”;会发现命令卡住了,但是当前面一个事务通过commit提交了,命令就会正常运行结束,说明是被锁表了。
2.带索引
create index index_name on tb_user(name);
然后继续如1里面的操作,也就是一个开启事务,运行update tb_user set phone=11 where name=”c1”;先不提交
然后另一个运行update tb_user set phone=22 where name=”c2”;发现命令不会卡住,说明没有锁表
但是如果另一个也是update tb_user set phone=22 where name=”c1”;更新同一行,说明是锁行了
3.总结
当更新条件有索引时,update会锁行。当更新条件无索引时,update会锁表。
4.扩展查看数据的事务设置
mysq由于默认是开启自动提交事务,所以首先得查看自己当前的数据库是否开启了自动提交事务。
命令:select @@autocommit;
如果结果是1,则说明数据库是自动提交,如果是0则说明数据库没有开启自动提交。