需求:有一张表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. PHP递归生成树形数组

    数据表结构 id   name  pid       ){ foreach($data as $row){ if($row['pid']==$p_id){ $tmp = $this->tree( ...

  2. Eclipse配置PyDev插件来实现python开发环境

    1.安装python解释器(完成的略过) 2.安装PyDev: 首先需要去Eclipse官网下载:http://www.eclipse.org/,Eclipse需要JDK支持,如果Eclipse无法正 ...

  3. 微软注册dll在dotnet开发时起到缓存的作用

    经过试验,我发觉只要是注册了dll之后,会在全局的环境中得到很好的体现,比如无需指定具体物理路径的dll引用,搜索即可引用等,同时也得到一点: 1.会缓存起这个dll先,在不重启电脑的情况,本地物理路 ...

  4. 2016.8.14安装myplayer心得

    安装mplayer时,我有两个os是not found状态,我在其他地方找到后 which mplayer,找到mplayer的配置界面,找到not found的部分,并且从usr/lib中找到相应的 ...

  5. AngularJs ngClass、ngClassEven、ngClassOdd、ngStyle

    这几个都关于样式及类名修改的,所以先把样式代码贴上吧. .red{color:red} .blue{color:blue} 写案例用到的样式就这么简单的两个,下面进入正题. ngClass ngCla ...

  6. Apache 优雅重启 Xampp开机自启 - 【环境变量】用DOS命令在任意目录下启动服务

    D:\xampp\apache\bin\httpd.exe" -k runservice Apache 优雅重启 :httpd -k graceful Xampp开机自启动  参考文献:ht ...

  7. COGS1117

    传送门: 差分约束第一题. 所有的条件无非两种不等式 $d[i]-d[j]>=dist$ $d[i]-d[j]<=dist$ 然后进行变形 $d[i]-d[j]>=dist$     ...

  8. easyUI创建dialog弹框

    1.在当前页面必须有一个DIV <!-- 保证金明细的详情列表显示 --> <div id="dialog-alarm-detail"></div&g ...

  9. vs------各种错误解决方法

    错误:命名空间System.Net中不存在类型或命名空间名“Http”,或工程里面“引用”的文件太少 转载:http://www.asp.net/mvc/mvc4 错误:LD.exe 已退出,代码为- ...

  10. 爬虫5 html下载器 html_downloader.py

    #coding:utf8 import urllib2 __author__ = 'wang' class HtmlDownloader(object): def download(self, url ...