需求:有一张表9亿多条数据,数据加索引总数据量61GB。考虑到这张表的大部分数据都不会再被使用并且大数据量可能影响整库的性能,所以决定将表里某一个时刻之前的数据备份到一张新表中,待备份完成后将旧表中已经备份的数据删除。由于数据量太大,不适合让DBA直接做备份。

  • 方案1

    main线程分页读取旧表数据,每页200条。每读取一页数据就新建一个线程,将200条数据交给新建的线程去完成insert到新表的操作。

弊端:需要将数据读到内存,然后再写回数据库,涉及到大量的IO操作。所有的数据都需要走网络,对网络带宽及稳定性要求很高。

  • 方案2

    直接使用SQL语句在MySQL端完成select和insert操作,不涉及IO操作。

弊端:在这种情况下,要分页操作就必须使用单线程。

综合考虑整个备份过程的速度与数据量,采用第二种方案。

涉及到的SQL语句如下:

<insert id="backupUniqueNumber" parameterClass="java.util.Map">
INSERT INTO UniqueNumber_backup_201603
(ID,
SerialNumber,
BusinessType,
AddTime
)
SELECT ID, SerialNumber, BusinessType, AddTime FROM UniqueNumber WHERE ID > #lastMaxId# limit #pageSize#
</insert> <!-- 强制走主库 -->
<select id="getLastMaxId" resultClass="java.lang.Integer">
/*+zebra:w*/SELECT MAX(ID) FROM UniqueNumber_backup_201603
</select>

主要的Java代码如下:

public class UniqueNumberBackupBiz {
private static final AvatarLogger logger = AvatarLoggerFactory.getLogger(UniqueNumberBackupBiz.class);
@Autowired
private UniqueNumberDao uniqueNumberDao; public void execute(){
int maxId = 932727664;// 932727664;//UniqueNumber表中2016-04-01 00:00:00数据的id为932727664
int lastMaxId = 0;
int pageSize = 300; try{
lastMaxId = uniqueNumberDao.getLastMaxId();
}catch (Exception e){
//这里出现异常是因为备份表中还没有数据,此时lastMaxId取默认值0. 这个异常只会在第一次运行时出现。
logger.warn(String.format("Get lastMaxId failed, system exit, please run the system manually"));
System.exit(0);
} long startTime = System.currentTimeMillis();
while(lastMaxId < maxId){
logger.info("lastMaxId=" + lastMaxId);
try {
uniqueNumberDao.backupUniqueNumber(lastMaxId, pageSize);
}catch (Exception e){
logger.error("backupUniqueNumber exception:", e);
}
try {
Thread.sleep(50);//防止MySQL压力过大
lastMaxId = uniqueNumberDao.getLastMaxId();
}catch (Exception e){
// logger.error("Thread sleep exception", e);
logger.error("Get lastMaxId failed, system exit, please run the system manually", e);
System.exit(0);
}
}
long endTime = System.currentTimeMillis();
logger.info(String.format("Data backup finished in %d ms", endTime - startTime));
}
}

为了防止对MySQL造成过大的压力,每一次循环中休眠50ms。运行中每秒insert的数据量大约5000条。若取消每次循环休眠50ms,每秒insert数据量大约为30000条。

PS:踩了一个坑,由于MySQL数据库有master slave之分,select操作是走slave库。master库与slave库同步存在一定的时差。之前没有强制select走master库,造成抛出了一些主键重复异常。不过这个异常不会造成多大的影响。后来强制select走主库就没有再抛出异常了。

数据全部从旧表搬到新表之后,需要将旧表中已备份的数据删除。

    <delete id="deleteOldData" parameterClass="java.util.Map">
<![CDATA[
DELETE FROM UniqueNumber
WHERE ID >= #startId# AND ID < #endId#
]]>
</delete> <!-- 强制走主库 -->
<select id="getMinId" resultClass="java.lang.Integer">
/*+zebra:w*/SELECT MIN(ID) FROM UniqueNumber
</select>
    public void deleteOldData(){
int startId = uniqueNumberDao.getMinId();
int pageSize = 1000;
int endId = startId + pageSize;
int maxId = uniqueNumberDao.getLastMaxId();
logger.info(String.format("maxId=%d", maxId));
while(endId < (maxId - 1000)){//保险起见,防止多删
try {
int num = uniqueNumberDao.deleteOldData(startId, endId);
Thread.sleep(50);
logger.info(String.format("startId=%d, endId=%d,%d rows deleted", startId, endId, num));
startId = endId;
endId = startId + pageSize;
}catch (Exception e){
logger.error("Error occurred when deleting data", e);
System.exit(-1);
}
}
}

删除任务执行完之后可能还剩下不到1000条数据没删完,需要手动执行delete语句来删除剩下的部分。

Java备份约9亿条数据的更多相关文章

  1. PostgreSQL中COUNT的各条件下(1亿条数据)例子

    test=# insert into tbl_time1 select generate_series(1,100000000),clock_timestamp(),now(); INSERT 0 1 ...

  2. 亿条数据在PHP中实现Mysql数据库分表100张

    当数据量猛增的时候,大家都会选择库表散列等等方式去优化数据读写速度.笔者做了一个简单的尝试,1亿条数据,分100张表.具体实现过程如下: 首先创建100张表: $i=0; while($i<=9 ...

  3. net.sz.framework 框架 ORM 消消乐超过亿条数据排行榜分析 天王盖地虎

    序言 天王盖地虎, 老婆马上生孩子了,在家待产,老婆喜欢玩消消乐类似的休闲游戏,闲置状态,无聊的分析一下消消乐游戏的一些技术问题: 由于我主要是服务器研发,客户端属于半吊子,所以就分析一下消消乐排行榜 ...

  4. 1亿条数据在PHP中实现Mysql数据库分表100张

    当数据量猛增的时候,大家都会选择库表散列等等方式去优化数据读写速度.笔者做了一个简单的尝试,1亿条数据,分100张表.具体实现过程如下: 首先创建100张表: $i=0; while($i<=9 ...

  5. oralce 超过1亿条数据的数据库表清理实践

    2018-08-18 16:58 无腿鸟 阅读(331) 评论(0) 编辑 收藏 问题:当一个表的数据量超过一亿条,要删除其中的5000w条,如何处理. 如果直接使用delete语句,会涉及到到大量的 ...

  6. java向数据库插入N条数据

    为了测试mysql的索引,要向数据库先插入上万条数据,然后再测试.手动插入太麻烦,写了一段代码. 先上代码: package action; import java.sql.Connection; i ...

  7. 演讲:对 2000 多亿条数据做一次 group by 需要多久?

    http://2017.qconbeijing.com/presentation/646?utm_source=weibo&utm_medium=infoq&utm_campaign= ...

  8. java 批量插入10万条数据

    for (int i = 0; i < 100000; i++) { dbHelper.insert("INSERT aaa(name) Values ('1')"); } ...

  9. 过千万、亿条数据的mysql表更新 mysql 线程状态

    分段更新 UPDATE question SET `status`=1 WHERE status!=1 LIMIT 3000;UPDATE answer SET `status`=1 WHERE st ...

随机推荐

  1. wordpress 缩略图功能函数 the_post_thumbnail

    很多 WordPress 主题,特别是那些杂志型的主题,会给每篇日志加上一张缩略图,这种展现方式一般用在首页,可能单独出现,或者和日志摘要一起.但是目前位置没有一个标准的方法去实现日志缩略图,很多主题 ...

  2. perl 哈希 连接符

    #!/usr/bin/perl -w use strict; my $test_1 = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; my $test_2 ...

  3. [分类算法] :SVM支持向量机

    Support vector machines 支持向量机,简称SVM 分类算法的目的是学会一个分类函数或者分类模型(分类器),能够把数据库中的数据项映射给定类别中的某一个,从而可以预测未知类别. S ...

  4. 【poj3254】 Corn Fields

    http://poj.org/problem?id=3254 (题目链接) 题意 给出一块n*m的田地,有些能够耕种,有些不能.要求将牛两两不相邻的放在田中,牛的个数至少为1个.问有多少种放法. So ...

  5. ListView优化-通用ViewHolder编写备份

    ViewHolder.java package cn.edu.bzu.util; import android.content.Context; import android.util.SparseA ...

  6. POJ2195 Going Home

    Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 22091   Accepted: 11156 Description On ...

  7. java 和 mysql 获取周 星期 的第一天 最后一天 或者 月的 日期(字符串转日期,日期转字符串,日期加减)

    获取周的第一天,最后一天 System.out.println(getStartEndDate("2016-05-01", 1)); 获取星期的第一天和最后一天 System.ou ...

  8. linux 基础 shell脚本命令

    #########shell脚本命令#### 1.diff diff file file1 ####比较两个文件的不同 -c ####显示周围的行 -u ####按照一格式统一输出生成补丁 -r ## ...

  9. Struts学习总结-02 类型转换

    一 类型转换 input.jsp <%@ page language="java" import="java.util.*" pageEncoding=& ...

  10. IP地址、子网掩码、网关、DNS的关系

      什么是IP地址所谓IP地址就是给每个连接在Internet上的主机分配的一个32bit地址.按照TCP/IP协议规定,IP地址用二进制来表示,每个IP地址长32bit,比特换算成字节,就是4个字节 ...