进行大规模数据迁移时,使用多线程,利用改进二分法,近似均等切分任务
来源:互联网 发布:nymex天然气 数据 编辑:程序博客网 时间:2024/05/22 15:45
楼主在进行数据迁移时遇到一个问题,即进行数据迁移时,需要用到多线程,提高写入速度,每个线程可以使用id来进行分割。例如假如有100条数据,id范围为0-99。分割为5个线程,可以这样:
线程1: id:0-20
线程2: id:20-40
线程3: id:40-60
线程4: id:60-80
线程5: id:80-99
但是实际情况往往是id分布并不均匀,假如有id范围为:0-100的数据总共有51条数据,0-50范围内可能有50条数据,50-100之间可能只有1条数据,此时按照上述方法会导致两个线程基本没有任何任务就跑完数据了,导致任务分布不均,降低了线程利用率。
该问题可以使用二分法来解决,我们先假定第一次均分点就在中间位置,50.利用数据库工具的cout操作统计出该范围内的数据量。楼主使用的是elasticsearch,能够在毫秒级别统计出响应结果(如果count操作十分耗时,不建议采用该方法)。
代码如下:
public class Task { private long minId; private long maxId; public Task(long minId, long maxId) { this.minId = minId; this.maxId = maxId; } public long getMinId() { return minId; } public void setMinId(long minId) { this.minId = minId; } public long getMaxId() { return maxId; } public void setMaxId(long maxId) { this.maxId = maxId; }}
public class EsTaskSheduleImpl implements TaskSheduleInterface { public static final int THREAD_NUM = ESConfigure.THREAD_NUM; //相等 private static final int COMPARE_EQ = 0; //大于 private static final int COMPARE_GT = 1; //小于 private static final int COMPARE_LE = -1; public EsTaskSheduleImpl(String indexName, String typeName, String splitFieldName, Long start, Long end) { this.indexName = indexName; this.typeName = typeName; this.splitFieldName = splitFieldName; this.start = start; this.end = end; } public EsTaskSheduleImpl() { } //要求Threa_NUM必须为2的指数倍 private static int getSplitTime() { Double result = Math.log(THREAD_NUM) / Math.log(2); return result.intValue(); } public List<Task> getSplitTasks() { List<Task> taskList = new ArrayList<Task>(); long totalSize = EsStatisticsTool.getCount(splitFieldName,indexName,typeName,start, end); if(totalSize<=1000)//1000条数据以内使用单线程即可 { Task task=new Task(start,end); taskList.add(task); }else { taskList=getAverageSplitTasks(); } return taskList; } /** *作用: 获取均匀近似等分的任务 * splitPointsTreeSet 为有序、元素唯一的分割点集合,最开始有两个点,当要切分为2等份时,只需要切1次,4等份2次,8等份3次 * 对应关系即2^splitTime=Thread_NUM 因此Thread_Num必须为2的指数倍 * @return */ private List<Task>getAverageSplitTasks() { List<Task> taskList = new ArrayList<Task>(); EsTaskSheduleImpl taskShedule = new EsTaskSheduleImpl(); TreeSet<Long> splitPointsTreeSet = new TreeSet(); splitPointsTreeSet.add(start); splitPointsTreeSet.add(end); //获取要切分次数 final int splitTime = getSplitTime(); for (int i = 0; i < splitTime; i++) { Long[] array = splitPointsTreeSet.toArray(new Long[splitPointsTreeSet.size()]); int arrayEnd = array.length - 1; for (int j = 0; j < arrayEnd; j++) { Long middle = taskShedule.findSimilarMedianByBinarySearch(array[j], array[j + 1]); splitPointsTreeSet.add(middle); } } Long[] array = splitPointsTreeSet.toArray(new Long[splitPointsTreeSet.size()]); for (int i = 0; i < array.length - 1; i++) { taskList.add(new Task(array[i], array[i + 1])); } return taskList; } /** * 使用非递归二分查找的方式,在两条数据之间,查找数据量均分的近似点 * * @return */ public long findSimilarMedianByBinarySearch(long low, long high) { //标识位置,任何比较都是与起始位置的比较 long start = low; long totalSize = EsStatisticsTool.getCount(splitFieldName,indexName,typeName,low, high); long halfSize = totalSize >> 1; long middle = -1; while (low <= high) { //中间位置计算,low+ 最高位置减去最低位置,右移一位,相当于除2.也可以用(high+low)/2 middle = low + ((high - low) >> 1); long actualValue = EsStatisticsTool.getCount(splitFieldName, indexName, typeName, start, middle); int compareValue = this.compare(actualValue, halfSize); //与最中间的数字进行判断,是否相等,相等的话就返回对应的数组下标. if (compareValue == COMPARE_EQ) { return middle; //如果小于的话则移动最低层的"指针" } else if (compareValue == COMPARE_LE) { low = middle + 1; } else { high = middle - 1; } } return middle; } /** * 判断是否找到均分点的依据,当误差不大于2%,或者数据量相差不到200时,认为达到找到均分点,返回0,超出该范围则认为 * 尚未找到均分点,返回1,或者-1 * * @param actualValue * @param idealValue * @return */ private int compare(long actualValue, long idealValue) { //差值 long space = actualValue - idealValue; //差值绝对值 long absoluteSpace = Math.abs(space); //误差百分比 long errorRatio = (absoluteSpace * 100 / idealValue); if ((errorRatio <= 2) || (absoluteSpace <= 200)) return COMPARE_EQ; else if (space > 0) return COMPARE_GT; else return COMPARE_LE; }}
线程2: id:20-40
线程2: id:20-40
0 0
- 进行大规模数据迁移时,使用多线程,利用改进二分法,近似均等切分任务
- 大规模数据运行时,可以考虑使用多线程处理!
- 使用SqlbulkCopy进行数据迁移
- 使用RMAN进行数据迁移
- 数据进行切分
- 请教各位大侠:利用数据管道进行数据迁移时碰到的问题。
- 利用expdp/impdp数据泵对oracle进行数据迁移
- 利用函数进行二分法查找
- 使用SqlBulkCopy进行大批量数据迁移
- 使用SqlBulkCopy进行数据大批量的迁移
- 使用SqlBulkCopy进行大批量数据迁移
- 使用Kettle进行数据迁移(ETL)
- 使用Kettle进行数据迁移(ETL)
- 使用python进行数据迁移案例
- 使用hadoop进行大规模数据的全局排序
- 使用hadoop进行大规模数据的全局排序
- 使用hadoop进行大规模数据的全局排序
- 使用hadoop进行大规模数据的全局排序
- 并查集
- CRC校验码
- 从Android运行时出发,打造我们的脱壳神器
- AlloyTouch与three.js 3D模型交互
- iOS-Pod bundle format unrecognized, invalid, or unsuitable 错误
- 进行大规模数据迁移时,使用多线程,利用改进二分法,近似均等切分任务
- gacutil regasm注册不了的问题
- Fatal Error] dom_demo_01.xml:1:10: 在处理指令目标和数据之间需要有空格
- 使用J2SE API读取Properties文件的六种方法
- TestNG运行报错:Error: A JNI error has occurred
- Alfred配置
- webpack打包时出现的错误
- leetcode-wordBreak
- 关于Android权限