データベースに基づく分散ロックを覚えておいてください

1:分散ロック、私が知っている3つの方法があります

A:従来のデータベースグローバルロック

B:redisなどのキャッシュに基づくグローバルロック

C:動物園の飼育係に基づく分散ロック

これら3つの方法の優先順位は、C> B> Aです。会社の構造上、最終的に最初の実装方法が選択されました。したがって、この記事では、データベースの分散ロックについてのみ説明します。関連する知識へのリンクがいくつかあります。自分で確認してください

分散ロックのいくつかの実装方法

Redis分散ロックの正しい実装

分散ロックと実装(2)-ZooKeeperの実装に基づく

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);
		}
	}
}

おすすめ

転載: blog.csdn.net/u013591094/article/details/109301317