1:分散ロック、私が知っている3つの方法があります
A:従来のデータベースグローバルロック
B:redisなどのキャッシュに基づくグローバルロック
C:動物園の飼育係に基づく分散ロック
これら3つの方法の優先順位は、C> B> Aです。会社の構造上、最終的に最初の実装方法が選択されました。したがって、この記事では、データベースの分散ロックについてのみ説明します。関連する知識へのリンクがいくつかあります。自分で確認してください
2:データベースグローバルロックの長所と短所
利点:
シンプルで実装が簡単
短所:
A:データベースがダウンすると、データベースの存在は通常、単一のポイントになります。サービスはご利用いただけません。
B:ロックには有効期限がありません。ロック解除が失敗すると、ロックは常に存在し、サービスが利用できなくなり、スレッドがブロックされます。
C:ロックは非ブロッキングのみで、ロックは再入できません
3:データベースのグローバルロックの解決策
1:シングルポイント?データベースは、より多くのデータベースバックアップを作成できます。
2:有効期限はありませんか?スケジュールされたタスク、時々クリーンアップします。または、ロックがロックされるたびに、予想される有効時間が挿入され、次にロックがロックされるときに、現在の時間が有効時間よりも大きいかどうかが判断され、ロックが無効かどうかが判断されます。
3:ノンブロッキング?別のスレッドを開いてループを取得します
4:非再入国、ロック時にマシン情報とスレッド情報を追加し、次回取得時にこれら2つのフィールドを最初に判断します
4:コード例
@Component
public class ArchiveTask {
private ThreadPoolTaskScheduler scheduler = null;
private ArchiveTaskDAO dao;
private ScheduledFuture<?> future;
private String clearDataDay = "90"; //默认迁移90天前的数据
private String wfmProcedure = null;
public static String stype = "TASK_SCHEDULER";
public static String pkey = "WFM_ARCHIVE_TASK";
public static String lock = "lock"; // 上锁状态
public static String unLock = "unLock"; // 无锁状态
public static String errorLock = "errorLock"; // 发生了错误
public static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private boolean isLock = false;
//private Logger logger = LoggerFactory.getLogger(ArchiveTask.class);
public void init(){
scheduler = new ThreadPoolTaskScheduler();
dao = (ArchiveTaskDAO) BaseDAOFactory.getDAO(ArchiveTaskDAO.class.getName());
wfmProcedure = Optional.ofNullable(dao.getConfigByType("ORDER", "HIS_SAVE_ORDERHIS_BY_ORDERID"))
.orElse("HIS_SAVE_ORDERHIS_BY_ORDERID");
}
public void schedule(){
scheduler.initialize();
future = scheduler.schedule(()->{
// 执行任务
runTask();
}, new Trigger(){
// 先执行 Trigger,在执行 Runnable
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
// TODO Auto-generated method stub
Date nextExecDate = null;
try {
//查询定时计划
Map<String,Object> taskMap = dao.getTaskSchedulerCron();
String cron = MapUtils.getString(taskMap, "cron", "");
if(cron.equals("")) {
return null;
}
// 定时任务触发,可修改定时任务的执行周期。数据库修改即可,不用重启应用。
CronTrigger trigger = new CronTrigger(cron);
nextExecDate = trigger.nextExecutionTime(triggerContext);
//计算时间差
Long timeDiffSecond = (nextExecDate.getTime() - new Date().getTime())/1000;
dao.updateTaskNextTimeDiff(timeDiffSecond.toString());
}catch (Exception e) {
e.printStackTrace();
}
return nextExecDate;
}
});
}
public void stop(){
future.cancel(true);
}
public void reStart(){
future.cancel(true);
schedule();
}
public void test(){
// 不通过定时任务,测试
scheduler.initialize();
scheduler.execute(()->{
runTask();
});
}
private void runTask(){
try{
Map<String,Object> taskMap = dao.getTaskSchedulerCron();
//0) 功能开关,如果关闭则直接跳过
String openStatus = MapUtils.getString(taskMap, "openStatus","");
if(!openStatus.equals("open")){
return;
}
//1)查询锁的有效时间
boolean lockValid = true; // 判断之前的加锁时间是否有效
String timeDiffSecondStr = MapUtils.getString(taskMap, "timeDiffSecond","");
String modifyDay = MapUtils.getString(taskMap, "modifyDay", ""); // 最近修改锁时间
if(modifyDay.equals("")||timeDiffSecondStr.equals("")){
lockValid = false;
}else{
Long timeDiffSecond = Long.parseLong(timeDiffSecondStr);
Date modifyDate = sdf.parse(modifyDay);
Calendar tmpCal = Calendar.getInstance();
tmpCal.setTime(modifyDate);
tmpCal.add(Calendar.SECOND, timeDiffSecond.intValue());
Date curDateForValid = tmpCal.getTime(); //锁的有效时间
Date curDate = new Date();
if(curDate.after(curDateForValid)){
//锁失效
lockValid = false;
}
}
//2) 先判断是否已有其他线程、进程在执行数据迁移任务了,数据库锁
synchronized (ArchiveTask.class) {
int flag = dao.taskSchedulerLock(lockValid);
if(flag < 1){
// 无法加锁,已被其他任务锁住了
return;
}
isLock = true;
}
//3)查询需要迁移的数据
Calendar cal = Calendar.getInstance();
String dayStr = MapUtils.getString(taskMap, "day", clearDataDay);
Integer dayI = Integer.parseInt(dayStr);
cal.add(Calendar.DAY_OF_YEAR, - dayI.intValue());
Date endDate = cal.getTime(); // 截止日期
Map<String,Object> params = new HashMap<String, Object>();
params.put("endDate", endDate);
params.put("iomBaseDataName", iomBaseDataName);
//4) (逻辑处理)
//更新锁状态
dao.updateTaskSchedulerLock(unLock);
isLock = false;
}catch(Exception ex){
ex.printStackTrace();
//如果出错 则更新数据库锁 为 出错状态
dao.updateTaskSchedulerLock(errorLock);
isLock = false;
}
}
public void releaseLock(){
if(isLock==true){
dao.updateTaskSchedulerLock(unLock);
}
}
}