需求:有一张表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. JAVA对文件类型的校验

    通常,在WEB系统中,上传文件时都需要做文件的类型校验,大致有如下几种方法: 1. 通过后缀名,如exe,jpg,bmp,rar,zip等等. 2. 通过读取文件,获取文件的Content-type来 ...

  2. EF异常:“System.InvalidOperationException”类型的未经处理的异常在 mscorlib.dll 中发生

     实体框架System.Data.Entity.SqlServer提供者类型”. SqlProviderServices EntityFramework. 的在应用程序配置文件注册状态"置疑 ...

  3. Yii2命名规则

    module id /controller id/action id的规则PostManagerController=>post-manageractionAddValue =>add-v ...

  4. [bzoj1009][HNOI2008]GT考试

    Description 阿申准备报名参加考试,准考证号为位数,他不希望准考证号上出现不吉利的数字. 他的不吉利数学有位,不出现是指中没有恰好一段等于. 可以为. Input 第一行输入.接下来一行输入 ...

  5. js-this的用法,来自阮一峰老师的文章

    1. 随着函数使用场合的不同,this的值会发生变化.但是有一个总的原则,那就是this指的是,调用函数的那个对象. 或者说,this指向当前执行的函数的所有者. 2.情况一:纯粹的函数调用 这是函数 ...

  6. Leetcode 343. Integer Break

    Given a positive integer n, break it into the sum of at least two positive integers and maximize the ...

  7. javascript向上向下取整

    alert(Math.ceil(25.9)); alert(Math.ceil(25.5)); alert(Math.ceil(25.1)); alert(Math.round(25.9)); ale ...

  8. JeeSite环境搭建及运行和打包(master20161117)

    涉及的软件: 1.phpStudy(主要用MySql) 2.maven3(用于依赖包,下面我将上传已经下载好所有依赖包的版本,保证运行正常) 具体操作: 0.前言 由于GitHub上的Release版 ...

  9. Jenkins 1.x权限配置(兼容2.x)

    说明: 1.x版本的jenkins默认是没有任何权限限制,任何人都可以访问. 需要用到的插件: [Role-based Authorization Strategy] 具体配置: 0.设置Jenkin ...

  10. Android成长日记-使用ToggleButton实现灯的开关

    案例演示 此案例实现思路:通过ToggleButton控件,ImageView控件实现 ---xml代码: <!-- textOn:true textOff:falase[s1] --> ...