java定时同步两台mysql_java代码实现定时增量同步完整⽅案import java.lang.management.ManagementFactory;
import java.util.List;
import urrent.atomic.AtomicBoolean;
import org.springframework.util.CollectionUtils;
import com.hengyunsoft.data.sync.client.IIncrementDataSync;
import com.hengyunsoft.data.sync.po.CommonKVQuery;
/**
* 此类主要是实现同步的主体框架,偏向于算法实现,具体的使⽤什么协议进⾏通讯,请看 {@link RestTemplateDataSyncImpl}
*
* @author 我很好[1064319393@qq]
* @version 1.0.0
*
这是基于是单线程来执⾏同步 千万不允许多线程执⾏ 多线程执⾏太难写了 放弃吧
* 这⾥我们考虑有时间因素、以及mysql mvcc⾮锁定读的因素。
* 时间因素我们这样解决:1、以主服务器的时间为准。⽽⾮本地时间。本地时间快与慢不会影响同步功能
* 2、主服务器可以调整它的时间,可以向前(改⼩时间)或向后(改⼤时间)调整,程序都是⽀持的。
* 假设我们的同步时间是1⼩时同步⼀次,假设事物最⼤持续时间是5分钟(就是没有事物可以超过5分钟⽽不结束的)。假设当前主服务器时间
* 是: 08:00 , 那么上次同步时间是:06:55,下次同步时间是07:55
* 2.1、向前调整:假设向前调整5⼩时,那么是 03:00 , 当我们同步的时候,获取主服务器的时间是 X ( 03:00 <= X <= 04:00) 因为我们的同步时间间隔是1⼩时
本来无一物 何处惹尘埃
* 假设X=03:56 , 那么min(07:55,03:56-01:00-00:05)=02:51 那么我们此次的同步时间将会是:02:51,那么是正确同步时间。
* 下次认为的上次同步时间将会是:02:51。
* 2.2、向后调整:假设向后调整5⼩时,那么是13:00, 当我们同步的时候,获取主服务器的时间是:Y (13:00 <= Y <= 14:00) 同理。
* 假设Y=13:56,那么min(07:55,13:56-01:00-00:05)=07:55 那么我们此次的同步时间将会是:07:55,那么是正确同步时间。
* 下次认为的上次同步时间将会是:13:56-01:00-00:05 = 12:51 这个时候时间就⼀下进步了很多了(跟上主服务器的时间步伐)。
*
* 由于我想要完全避免使⽤本地时间进⾏计算(因为线上的时间不准确导致时间会被管理员调来调去的)。
*
* 同步时间间隔完全可以通过前⼀次执⾏时刻与此次执⾏时刻计算出来。但是避免使⽤获取系统时间的⽅式(危害⼤)。
* 通过获取虚拟机已经运⾏时间(毫秒): 有个好处是不随着系统时间的改变⽽改变。他是记录虚拟机运⾏了多久
* RuntimeMXBean().getUptime()
*
* 是id类型
*/
新射雕侠传public abstract class BaseDataSyncImpl implements IIncrementDataSync {
//同步时间间隔 可以稍微⼤点(⽐真实在定时任务的执⾏中的间隔⼤,但是千万别⼩于他,等于定时任务执⾏间隔最好) private volatile long sync_time_interval_in_milsecond ;
/**
* 上次同步的时候,jvm虚拟机运⾏时间
*/
private volatile long up_sync_jvm_run_milsecond = -1;
private volatile long this_sync_jvm_run_milsecond ;
//事物处理最长时间 建议同步时间间隔⼤于此时间
private long tx_time_out_in_milsecond = 5*60*1000;
//下次同步时间
private volatile Long next_sync_time = null;
//本次同步时间
private volatile Long this_sync_time = null;
private int deleteLimit = 1000;
/
**
* 当做锁的功能,记录是否在进⾏同步。只允许⼀个线程进⾏同步。
*/
private AtomicBoolean runing = new AtomicBoolean(false);
/**
* 执⾏总体架构
*/
@Override
public final void startSync() {
//单线程同步
if(!runingpareAndSet(false, true)) {
/
/正在同步中,不能够同时进⾏两次同步,就是加锁操作
return ;
}
sync_time_interval_in_milsecond = getSyncTimeIntervalByJvmruntiem();
try {
//获取同步时间 与主服务器商定同步时间
long nowSyncTime = getSyncTime();
//清楚同步标记 准备开始同步数据,只需要清楚要被同步部分数据的同步标记,
//就是更新时间在nowSyncTime之后的那部分数据
cleanSyncFlag(nowSyncTime);
//开始数据同步
syncDatas(nowSyncTime);
//同步数据仅仅解决更新与插⼊的问题 这⾥去解决删除的问题
//有些表不会存在删除操作,这⾥对那些不需要删除的表直接跳过
if(isNeedDel)
syncDel();
//这个放到最后 怕事物回滚 ⽽时间没有被回滚 导致下次同步时,next_sync_time不正确updateNextSyncTime();
} catch (Exception e) {
e.printStackTrace();
} finally {
//允许下次的同步 解锁操作
runing.set(false);
}
}
/**
* 计算这次同步与上次同步的时间间隔
* @return
*/
private long getSyncTimeIntervalByJvmruntiem() {
//获取虚拟机已经运⾏时间(毫秒)
//有个好处是不随着系统时间的改变⽽改变。他是记录虚拟机运⾏了多久。
this_sync_jvm_run_milsecond = RuntimeMXBean().getUptime(); if(up_sync_jvm_run_milsecond == -1) {
//启动后的第⼀次同步,
return this_sync_jvm_run_milsecond ;
}
return this_sync_jvm_run_milsecond - up_sync_jvm_run_milsecond;
}
/**
* 清楚同步标记 准备开始同步数据
* 只需要清楚要被同步部分数据的同步标记,就是更新时间在nowSyncTime之后的那部分数据* @param nowSyncTime
*/
protected abstract void cleanSyncFlag(long nowSyncTime);
//⼀定要在最后来更新这个时间
protected void updateNextSyncTime(){
long next_sync_time = this_sync_time + sync_time_interval_in_milsecond;
//先保存下次同步时间
saveDbNext_sync_time(next_sync_time);
//在更新内存时间 怕保存数据库失败⽽回滚,内存数据与数据库数据不⼀致
<_sync_time = next_sync_time;
up_sync_jvm_run_milsecond = this_sync_jvm_run_milsecond;
}
/**
* 同步删除操作,将在主服务器中删除的数据也在本地进⾏删除
*/
private void syncDel() {
/
/1、本地取全部id集合的摘要 MD5,以及记录数 拿去远程⽐较,相等则啥都不做CommonKVQuery abstractAndCount = getLocalIdsAbstractAndCount();
boolean isMach = isMachMasterServer(abstractAndCount);
if(isMach) {
return ;
}
//清楚本地删除标记 准备进⼊删除
cleanLocalDelFlag();
//2、把本地的数据按照id进⾏分页拿到远程去对⽐,没有则拿回来进⾏删除 。
List> ids = null;
List> delIds = null;
do {
ids = updateLocalIdsToDeleteFlagAndGet(deleteLimit);
if(CollectionUtils.isEmpty(ids)) {
提高笔记本显卡性能break ;
}
delIds = getNeedDelIdsFromMasterServer(ids);
if(!CollectionUtils.isEmpty(delIds)) {
一二年级运动会加油稿deleteLocalByIds(delIds);
}
} while (ids.size() == deleteLimit);
}
/**
* 清楚本地删除标记 准备进⼊同步删除操作
*/
protected abstract void cleanLocalDelFlag() ;
/**
华为智能助手* 删除本地的数据 通过id集合
* 不需要删除的,则⽆需实现
* @param delIds
*/
protected abstract void deleteLocalByIds(List> delIds);
/**
* 去远处匹配 出需要删除的id集合
清晰的反义词* @param ids
* @return
*/
protected abstract List> getNeedDelIdsFromMasterServer(List ids);
/**
* 分页获取本地id集合,并且将返回的id集合都标记为已加⼊删除查询,下次查询将不再查出。* 不需要删除的,则⽆需实现
* @param pageRequest
* @return
*/
protected abstract List> updateLocalIdsToDeleteFlagAndGet(int limit);
/**
* 去主服务器匹配摘要及记录数。
* 匹配维度是⼆维: 1. 数据⾏数 2.主键摘要
* @param abstractAndCount
* @return
*/
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论