我在 Oracle 中有一个非常大的表(数亿行,包含数字和字符串),我需要读取该表的所有内容,对其进行格式化并写入文件或任何其他资源。 通常我的解决方案是这样的:
package my.odp;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.lang.Throwable;
import java.sql.*;
public class Main {
public static volatile boolean finished = false;
public static void main(final String[] args) throws InterruptedException {
final ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<String>(10000);
final Thread writeWorker = new Thread("ODP Writer") {
public void run() {
try {
File targetFile = new File(args[0]);
FileWriter fileWriter = new FileWriter(targetFile);
BufferedWriter writer = new BufferedWriter(fileWriter);
String str;
try {
while (!finished) {
str = queue.poll(200, TimeUnit.MILLISECONDS);
if (str == null) {
Thread.sleep(50);
continue;
}
writer.write(str);
writer.write('\n');
}
} catch (InterruptedException e) {
writer.close();
return;
}
}
catch (Throwable e) {
e.printStackTrace();
return;
}
}
};
final Thread readerThread = new Thread("ODP Reader") {
public void run() {
try {
Class.forName("oracle.jdbc.OracleDriver");
Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@//xxx.xxx.xxx.xxx:1521/orcl", "user", "pass");
Statement stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
stmt.setFetchSize(500000);
ResultSet rs = stmt.executeQuery("select * from src_schema.big_table_view");
System.out.println("Fetching result");
while (rs.next()) {
StringBuilder sb = new StringBuilder();
sb.append(rs.getString(1)).append('\t');//OWNER
sb.append(rs.getString(2)).append('\t');//OBJECT_NAME
sb.append(rs.getString(3)).append('\t');//SUBOBJECT_NAME
sb.append(rs.getLong(4)).append('\t');//OBJECT_ID
sb.append(rs.getLong(5)).append('\t');//DATA_OBJECT_ID
sb.append(rs.getString(6)).append('\t');//OBJECT_TYPE
sb.append(rs.getString(7)).append('\t');//CREATED
sb.append(rs.getString(8)).append('\t');//LAST_DDL_TIME
sb.append(rs.getString(9)).append('\t');//TIMESTAMP
sb.append(rs.getString(10)).append('\t');//STATUS
sb.append(rs.getString(11)).append('\t');//TEMPORARY
sb.append(rs.getString(12)).append('\t');//GENERATED
sb.append(rs.getString(13)).append('\t');//SECONDARY
sb.append(rs.getString(14)).append('\t');//NAMESPACE
sb.append(rs.getString(15));//EDITION_NAME
queue.put(sb.toString());
}
rs.close();
stmt.close();
conn.close();
finished = true;
} catch (Throwable e) {
e.printStackTrace();
return;
}
}
};
long startTime = System.currentTimeMillis();
writeWorker.start();
readerThread.start();
System.out.println("Waiting for join..");
writeWorker.join();
System.out.println("Exit:"+ (System.currentTimeMillis() - startTime));
}
有两个线程:一个用于从结果集中获取行,一个用于写入字符串值。测得的加载速度约为 10Mb/s,在我的情况下,我需要将其速度提高 10 倍。 Profiler 显示最耗时的方法是
oracle.jdbc.driver.OracleResultSetImpl.getString()
和
oracle.net.ns.Packet.receive()
你有什么想法可以让 jdbc 更快地加载数据吗? 任何关于查询优化、字符串加载优化、调整 JDBC 驱动程序或使用其他驱动程序的想法,直接使用 oracle JDBC 实现,调整 Oracle 都是值得赞赏的。
更新: 我整理并列出了以下讨论结果:
除了与 Oracle 数据库的连接之外,我无法访问 DBMS 服务器,并且服务器无法连接到任何外部资源。无法应用任何使用服务器或远程文件系统的转储和提取实用程序,也无法在服务器上安装和使用任何外部 java 或 PL/SQL 例程。仅连接以执行查询 - 仅此而已。
我使用了探查器并深入研究了 Oracle JDBC 驱动程序。我发现最昂贵的操作是读取数据,即 Socket.read()。所有字符串字段都表示为一个字符数组,对性能几乎没有影响。通常,我使用探查器检查了整个应用程序,而 Socket.read() 绝对是最昂贵的操作。提取字段、构建字符串、写入数据几乎不消耗任何东西。问题仅在于读取数据。
服务器端数据表示的任何优化都不会产生实际效果。连接字符串和转换时间戳对性能没有影响。
应用程序被重写为具有多个读取器线程,这些线程将就绪数据放入写入器队列。每个线程都有自己的连接,没有使用池,因为它们会减慢提取速度(我使用了 oracle 推荐的 UCP 池,它消耗了大约 10% 的执行时间,所以我放弃了)。结果集 fetchSize 也增加了,因为从默认值 (10) 切换到 50000 会带来高达 50% 的性能增长。
我测试了多线程版本如何与 4 个读取线程一起工作,发现增加读取器数量只会减慢提取速度。 我尝试启动 2 个实例,其中每个实例都有两个读取器,并且都与单个实例同时工作,即双数据提取需要与单个实例相同的时间。不知道为什么会这样,但看起来 oracle 驱动程序有一些性能限制。具有 4 个独立连接的应用程序比具有 2 个连接的 2 个 App 实例运行得慢。 (Profiler 用于确保驱动程序的 Socket.read() 仍然是主要问题,所有其他部分在多线程模式下工作正常)。
我尝试使用 SAS 获取所有数据,它执行相同提取的速度比 JDBC 快 2 倍,两者都使用到 Oracle 的单一连接并且不能使用任何转储操作。 Oracle 确保 JDBC 瘦驱动程序与 native 驱动程序一样快。
也许 Oracle 有其他方法可以通过 ODBC 或其他方式对远程主机执行快速提取?
最佳答案
假设您已经检查了基本的网络内容,如接口(interface)、防火墙、代理,以及数据库服务器的硬件元素。
选项 1:
代替:
Class.forName("oracle.jdbc.OracleDriver");
Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@//xxx.xxx.xxx.xxx:1521/orcl", "user", "pass");
尝试使用:
OracleDataSource ods = new OracleDataSource();
java.util.Properties prop = new java.util.Properties();
prop.setProperty("MinLimit", "2");
prop.setProperty("MaxLimit", "10");
String url = "jdbc:oracle:oci8:@//xxx.xxx.xxx.xxx:1521/orcl";
ods.setURL(url);
ods.setUser("USER");
ods.setPassword("PWD");
ods.setConnectionCachingEnabled(true);
ods.setConnectionCacheProperties (prop);
ods.setConnectionCacheName("ImplicitCache01");
更多详情 here
选项 2:获取大小
正如 Stephen 强烈指出的那样,fetchsize 似乎太大了。
并且,对于 500,000 的提取大小,您的 -Xms 和 -Xmx 是多少。另外,在分析器中,最大堆大小是多少?
选项 3:DB
检查src_schema.big_table_view
这是一个工具还是一个应用系统。如果只是一个工具,你可以 基于数据库系统添加并行度、索引提示、分区等 能力
选项 4:线程
说 n <>
您可以启动 n 编写器线程,每个线程都配置为处理某个桶,例如thread1 处理 0 到 10000,写入 n 不同的文件,一旦所有的文件都完成,post join,最好使用低级操作系统命令将文件合并在一起。
也就是说,所有这些都不应像现在这样是预定义代码。 'n' 并且桶应该在运行时计算。并且创建超过系统支持数量的线程只会搞砸。
选项 5:
代替
select * from src_schema.big_table_view
你可以使用
SELECT column1||CHR(9)||column2||CHR(9).....||columnN FROM src_schema.big_table_view
这避免了创建 500000 个 StringBuilders 和 Strings。 (假设不涉及其他复杂格式)。 CHR(9) 是制表符。
选项 6:
同时,您还可以与您的 DBA 检查任何数据库系统问题,并使用 Oracle support 提出 SR .
关于java - 以极高的速度获取行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25335813/
我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/
有没有办法在这个简单的get方法中添加超时选项?我正在使用法拉第3.3。Faraday.get(url)四处寻找,我只能先发起连接后应用超时选项,然后应用超时选项。或者有什么简单的方法?这就是我现在正在做的:conn=Faraday.newresponse=conn.getdo|req|req.urlurlreq.options.timeout=2#2secondsend 最佳答案 试试这个:conn=Faraday.newdo|conn|conn.options.timeout=20endresponse=conn.get(url
我有一个存储主机名的Ruby数组server_names。如果我打印出来,它看起来像这样:["hostname.abc.com","hostname2.abc.com","hostname3.abc.com"]相当标准。我想要做的是获取这些服务器的IP(可能将它们存储在另一个变量中)。看起来IPSocket类可以做到这一点,但我不确定如何使用IPSocket类遍历它。如果它只是尝试像这样打印出IP:server_names.eachdo|name|IPSocket::getaddress(name)pnameend它提示我没有提供服务器名称。这是语法问题还是我没有正确使用类?输出:ge
我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c
我安装了ruby版本管理器,并将RVM安装的ruby实现设置为默认值,这样'哪个ruby'显示'~/.rvm/ruby-1.8.6-p383/bin/ruby'但是当我在emacs中打开inf-ruby缓冲区时,它使用安装在/usr/bin中的ruby。有没有办法让emacs像shell一样尊重ruby的路径?谢谢! 最佳答案 我创建了一个emacs扩展来将rvm集成到emacs中。如果您有兴趣,可以在这里获取:http://github.com/senny/rvm.el
假设我有这个范围:("aaaaa".."zzzzz")如何在不事先/每次生成整个项目的情况下从范围中获取第N个项目? 最佳答案 一种快速简便的方法:("aaaaa".."zzzzz").first(42).last#==>"aaabp"如果出于某种原因你不得不一遍又一遍地这样做,或者如果你需要避免为前N个元素构建中间数组,你可以这样写:moduleEnumerabledefskip(n)returnto_enum:skip,nunlessblock_given?each_with_indexdo|item,index|yieldit
我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www
我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur
我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我
如何在Ruby中获取BasicObject实例的类名?例如,假设我有这个:classMyObjectSystem我怎样才能使这段代码成功?编辑:我发现Object的实例方法class被定义为returnrb_class_real(CLASS_OF(obj));。有什么方法可以从Ruby中使用它? 最佳答案 我花了一些时间研究irb并想出了这个:classBasicObjectdefclassklass=class这将为任何从BasicObject继承的对象提供一个#class您可以调用的方法。编辑评论中要求的进一步解释:假设你有对象