数据库归档策略

数据库迁移策略

为备战双11,需要将数据库中的相关表(历史订单)进行归档,以便腾出更多的空间迎接订单的暴增。作者经过尝试,得出自认为最优的解决方案。下面给出数据库归档策略及示例代码。
现有条件:
1.现有两个数据库:db-A 以及 db-B;
2.两个库中有字段相同的表:tba(表中只有字段订单id–rx_id(long型) 有索引);
3.归档库的tba中还有17年整年的归档数据。
4.由于单量隐藏性,rx_id 并不是按照下单时间依次递增的,但是大致趋势是递增的。即:保证今天的最大单号比昨天的最大单号的值大。
5.需要迁移的单量大约300w条。

我们注意到,
1.数据量庞大,在查询的sql中最好是能够利用索引。
2.数据库可利用的索引只有1个。

解决思路:
1.首先,作者通过拿到当归档库表中最大的订单号:rx_id_start。
2.其次,拿到结束时间当天最大的订单号:rx_id_end。
3.通过两个单号,分页查询两个订单号范围内的订单。
4.按分页的数量分批插入归档表。
5.做一个定时job 定时执行。

好处:
1.最大化并且巧妙的利用了索引,加大了检索速度。
2.可做成定时job每天,执行一次,归档的工作可持续。

完整代码:

private static String archive4RxInfoJob = "archive4RxInfoJob";
 /**
     * 缓存结束时间的最大rxId
     */
public static Map<String, Long> currentMaxRxIdMap = new ConcurrentHashMap<>();
//封装的线程池
private ThreadPoolService threadPoolService;
private static int pageSize = 100;

//可定时执行job
public JsfResult<Boolean> archive4RxInfoJob() {
        final Long start = System.currentTimeMillis();
        JsfResult<Boolean> result = new JsfResult<>();
        //开始ump监控
        CallerInfo umpCall = umpUtil.start("medicine-b2c-man.method.DataArchiveExportServiceImpl.archive4RxInfoOneTime");
        try {
            final Date endTm = getEndDay();
            //异步执行
            this.threadPoolService.asynExecuteTask(new Runnable() {
                @Override
                public void run() {
                    int i = 0;
                    while (true) {
                        if (i == 10) {
                            break;
                        }
                        //依次拿到归档表中最大的rx_id
                        Long rxId = dataArchiveDao.getRxInfoByRxIdAndEndTmPage();
                        //拿到生产库表中截止时间的rx_id,本地缓存记录一下
                        Long maxRxIdEndTm = currentMaxRxIdMap.get(archive4RxInfoJob + endTm.getTime());
                        if (maxRxIdEndTm == null) {
                            maxRxIdEndTm = rxReadDao.getMaxRxIdEndDay(endTm);
                            currentMaxRxIdMap.put(archive4RxInfoJob + endTm.getTime(), maxRxIdEndTm);
                        }
                        //查询1000条数据
                        logger.info("archive4RxInfoJob->start->rxId:{},date:{}", rxId, DateUtil.formatDate(endTm, "yyyy-MM-dd HH:mm:ss"));
                        List<RxInfo> resList = rxReadDao.getRxInfoByRxIdPage(rxId, pageSize / 10, maxRxIdEndTm);
                        if (resList == null || resList.size() == 0) {
                            logger.info("archive4RxInfoJob->resList size is 0");
                            return;
                        }
                        //批量插入
                        dataArchiveDao.batchAddRxInfoArchive(resList );
                        logger.info("archive4RxInfoJob->after->rxId:{},date:{},last:{}", rxId, DateUtil.formatDate(endTm, "yyyy-MM-dd HH:mm:ss"), System.currentTimeMillis() - start);
                        i++;
                    }
                }
            });

            result.setMsg(DataArchiveErrorCode.SUCCESS.getDescription());
            result.setCode(DataArchiveErrorCode.SUCCESS.getCode());
            return result;
        } catch (Exception e) {
            result.setCode(BusinessErrorCode.UNKNOWN_ERROR.getCode());
            result.setMsg(BusinessErrorCode.UNKNOWN_ERROR.getDescription());
            return result;
        } 
    }
    private static Date getEndDay() throws Exception {
        //时间范围确定,只导半年之前的数据
        long current = System.currentTimeMillis();//当前时间毫秒数
        long zero = current / (1000 * 3600 * 24) * (1000 * 3600 * 24) - TimeZone.getDefault().getRawOffset();//今天零点零分零秒的毫秒数
        Date endTm = DateUtil.addDays(DateUtil.parseDate(new Timestamp(zero).toString(), "yyyy-MM-dd HH:mm:ss.S"), -180);
        return endTm;
    }

猜你喜欢

转载自blog.csdn.net/u013465194/article/details/83153760
今日推荐