1.当需要从数据库大量导出数据时,同时又需要对对数据逐行进行过滤处理,如果是mysql这样的数据库,选择分页查询到内存再处理导出的话,当数据量超过千万时速度会直线下降,到达几千万条时会下降到难以接受的速度(以上经过多次测试验证),oracle另说...
有没有办法很好的解决这个问题,当然有哈哈
这里简单介绍2种方法:
一是使用远程dump的方式,对权限高要求,因为你可以随时把人家的表乃至库整个都拉下来,速度快,一般的linux系统直接支持mysql dump命令,具体操作如果不会,网上一搜很多说的,不详细说明了。
二是使用fetch游标的方式,速度也很快,可能比dump略慢,但是好处是可以逐条处理数据,并导出
参考代码
1.fetch方式
package com.jd.gp.lzotest;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import au.com.bytecode.opencsv.CSVReader;
public class DumpAndFetch {
private static final Logger logger = LoggerFactory.getLogger(DumpAndFetch.class);
public static void main(String[] args) {
printMemory();
startDump();
startFetch();
printMemory();
}
private static void startFetch() {
Connection conn = null;
Statement ps = null;
ResultSet rs = null;
try {
long start = System.currentTimeMillis();
conn = DBUtil.createConnection("ip", port, "dbname", "username", "password");
ps = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
//这个地方必须这么写,mysql不止fetchSize
ps.setFetchSize(Integer.MIN_VALUE);
logger.error("fetch start.... setFecthSize=" + ps.getFetchSize());
rs = ps.executeQuery("select * from orderdetail");
int count = 0;
while (rs.next()) {
//count++;
/*if(count % 200000 ==0){
logger.info("剩余内存 = " + Runtime.getRuntime().freeMemory()/1024 + "KB");
}*/
}
logger.info("fecch total time = " + (System.currentTimeMillis() - start) / 1000 + "Second");
logger.info("fetch end....count=" + count);
Thread.sleep(5000L);
System.out.println("剩余内存 = " + Runtime.getRuntime().freeMemory()/1024 + "KB");
} catch (Exception e) {
e.printStackTrace();
}
DBUtil.close(conn, ps, rs);
}
2.dump方式
private static void startDump() {
try {
long start = System.currentTimeMillis();
logger.info("dump start.... " );
//路径不能含有空格--替换空格?
Runtime rt = Runtime.getRuntime();
//空格不能乱加,参数不能乱加,否则异常也很难知道
//linux下一般支持mysqldump,所以可以直接这么写,但是在win下面需要指定命令的路径,或者在path环境变量中指定mysqldump命令 "cmd /c C:/Program Files/MySQL/MySQL Server 5.5/bin/mysqldump -uroot.."
Process proc = rt.exec("mysqldump -h192.168.229.69 -umq -pmq test orderdetail --result-file=/root/gp/orderdetail.sql ");
int exitValue = proc.waitFor();//等待执行结束
logger.info("dump total time = " + (System.currentTimeMillis()-start)/1000 + "Second");
logger.info("返回值:" + exitValue);
//readF();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void printMemory(){
// 可使用内存
long totalMemory = Runtime.getRuntime().totalMemory()/1024 ;// kb;
// 剩余内存
long freeMemory = Runtime.getRuntime().freeMemory()/1024 ;// kb;
// 最大可使用内存
long maxMemory = Runtime.getRuntime().maxMemory()/1024 ;// kb;
logger.info("可使用内存 = " + totalMemory + "KB");
logger.info("剩余内存 = " + freeMemory + "KB");
logger.info("最大可使用内存 = " + maxMemory + "KB");
}
public static void readF() throws Exception {
File f = new File("/root/gaopeng/orderdetail.sql");
CSVReader reader = new CSVReader(new InputStreamReader(new FileInputStream(f), "UTF-8"));
String[] items = null;
int line = 0;
while((items = reader.readNext()) != null){
line++;
if(line % 400000 == 0) {
for(String s : items)
System.out.print(s);
System.out.println();
}
}
reader.close();
}
}
以下是转来的:参考学习
- 等待命令执行结束用waitFor(),其返回值就是命令的返回值。
- 如果出现程序执行被挂起,没有任何反应的情况,是由于没有读取命令子进程的正常输出流或错误输出流导致缓冲区被占满,进程被锁住。这个时候需要把输出流中的内容给读出来。最好的做法是使用两个线程,分别同时读取正常输出流和错误输出流。
- 执行Windows平台上的命令时使用cmd.exe /C,如cmd.exe /C dir。
- 记得关闭命令子进程的输出流,通过Process.getOutputStream().close(),这样不会导致命令子进程被锁住。
仿照上面文章中,写了一个简单的例子。
import java.io.InputStream;
import java.io.InputStreamReader;
private InputStream ins;
this.ins = ins;
}
try {
BufferedReader reader = new BufferedReader(
new InputStreamReader(ins));
String line = null;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (Exception e) {
e.printStackTrace();
}
}
String[] cmd = new String[] { "cmd.exe", "/C", "wmic process get name" };
try {
Process process = Runtime.getRuntime().exec(cmd);
new Thread(new StreamDrainer(process.getInputStream())).start();
new Thread(new StreamDrainer(process.getErrorStream())).start();
process.getOutputStream().close();
System.out.println("返回值:" + exitValue);
} catch (Exception e) {
e.printStackTrace();
}
一 exec()方法有很多重载版本,常用的方式是将所有命令以一个String对象传递给exec() ; 另一种方式是将cmd命令的各个部分包装成String [] ,然后将此数组传递给exec() !
二 在windows上,文件名或者文件夹命中有空格时,可以采用将该名称放入双引号内的办法来避免出错!
PS:windows中的程序很少会被使用cmd来启动,大家已经很习惯于双击了,所以常用程序不一定有统计的命名,这一点不同于linux上的vi等! 因此,请大家一定要熟悉windows cmd下的一个特殊命令“ start”;另外,cmd在windows的很多版本里,其实就是 cmd.exe !
1 调用记事本(或系统默认的文本程序)打开简单文本文件:
Runtime.getRuntime().exec("cmd.exe /c start filename.txt ");2 调用ms word打开文本文件:
Runtime.getRuntime().exec("cmd.exe /c start winword.exe filename.doc ");3 打开一个windows目录:
Runtime.getRuntime().exec("cmd.exe /c start dirname ");4 打开一个url :
Runtime.getRuntime().exec("cmd.exe /c start http://www.google.com ");5 打开默认的邮件程序并给xxx发送邮件;
Runtime.getRuntime().exec("cmd.exe /c start mailto:[email protected] ");
6 执行程序并捕获其标准输出:
Process p = Runtime.getRuntime().exec("start .\\xxx.exe"); p.waitfor();
注:此法亦可用来解决调用程序后其CMD黑色窗口不自动关闭的问题! 同时,如果被调用程序的执行需要消耗一段时间,则应该在exec()之后调用其返回的Process 对象的waitfor()方法来等待该程序的执行。例如:
Process p = Runtime.getRuntime().exec("start .\\xxx.exe"); p.waitfor();
再强调一点:
start 后面的命令如果包含空格,请一定使用双引号,并且必须紧跟在 start 后多添加一个以双引号引起来的参数作为start的title ; 因为start命令会把其后的第一个使用双引号的参数作为新cmd窗口的title !!!