如何使用MATLAB和JDBC加速表检索?

Pau*_*sen 5 sql postgresql matlab jdbc resultset

我正在使用MATLAB调用的JDBC访问PostGreSQL 8.4数据库.我感兴趣的表基本上由不同数据类型的各列组成.他们通过他们的时间戳选择.

由于我想检索大量数据,我正在寻找一种比现在更快的方式来提出请求.


我现在正在做的是:首先我建立一个与数据库的连接并调用它DBConn.下一步是准备一个Select语句并执行它:

QUERYSTRING = ['SELECT * FROM ' TABLENAME '...
WHERE ts BETWEEN ''' TIMESTART ''' AND ''' TIMEEND ''''];

QUERY = DBConn.prepareStatement(QUERYSTRING);
RESULTSET = QUERY.executeQuery();
Run Code Online (Sandbox Code Playgroud)

然后我将columntypes存储在变量COLTYPE中(1表示FLOAT,-1表示BOOLEAN,0表示其余部分 - 几乎所有列都包含FLOAT).下一步是逐列处理每一行,并通过相应的方法检索数据.FNAMES包含表的字段名.

m=0; % Variable containing rownumber

while RESULTSET.next()
  m = m+1;

  for n = 1:length(FNAMES)

    if COLTYPE(n)==1 % Columntype is a FLOAT
      DATA{1}.(FNAMES{n})(m,1) = RESULTSET.getDouble(n);
    elseif COLTYPE(n)==-1 % Columntype is a BOOLEAN
      DATA{1}.(FNAMES{n})(m,1) = RESULTSET.getBoolean(n);
    else
      DATA{1}.(FNAMES{n}){m,1} = char(RESULTSET.getString(n));
    end

  end

end
Run Code Online (Sandbox Code Playgroud)

当我完成我的请求后,我关闭语句和连接.

我没有MATLAB数据库工具箱,所以我正在寻找没有它的解决方案.


我知道请求每个字段的数据非常无效.尽管如此,我还没有找到一种方法来同时获得更多数据 - 例如同一列的多行.有没有办法这样做?你有其他加快请求的建议吗?

And*_*nke 7

摘要

要加快速度,请使用数据库工具箱或自定义Java代码将循环,然后列数据类型转换到Java层.Matlab-to-Java方法调用开销可能正在扼杀你,并且没有办法用普通的JDBC进行块提取(一次调用多行).确保正确使用您正在使用的JDBC驱动程序上的旋钮.然后优化传输昂贵的列数据类型,如字符串和日期.

(注意:我没有使用Postgres,但与其他DBMS一起使用,这也适用于Postgres,因为大多数都是关于它上面的JDBC和Matlab层.)

细节

将循环推送到Java以获取块提取

最简单的方法是将行和列上的循环向下推入Java层,并让它将数据块(例如,每次100或1000行)返回到Matlab层.从Matlab调用Java方法有大量的每次调用开销,并且在M代码中循环JDBC调用会产生(参见MATLAB OOP缓慢还是我做错了? - 完全披露:这是我的答案).如果你从那样的M代码调用JDBC,那么你就会在每一行的每一列上产生开销,这可能是你现在大部分的执行时间.

JDBC API本身不像ODBC那样支持"块游标",因此您需要将该循环放入Java层.使用Oleg建议的数据库工具箱是一种方法,因为它们在Java中实现了它们的低级游标.(可能正是出于这个原因.)但是如果你不能拥有数据库工具箱依赖关系,你可以编写自己的瘦Java层来实现,并从你的M代码中调用它.(可能通过Matlab类与您的自定义Java代码相结合并知道如何与它进行交互.)使Java代码和Matlab代码共享块大小,缓冲Java端的整个块,使用原始数组而不是尽可能使用列缓冲区的对象数组,让M代码批量获取结果集,缓冲基本列数组的单元格数组中的块,然后将它们连接在一起.

Matlab层的伪代码:

colBufs = repmat( {{}}, [1 nCols] );
while (cursor.hasMore())
    cursor.fetchBlock();
    for iCol = 1:nCols
        colBufs{iCol}{end+1} = cursor.getBlock(iCol); % should come back as primitive
    end
end
for iCol = 1:nCols
    colResults{iCol} = cat(2, colBufs{iCol}{:});
end
Run Code Online (Sandbox Code Playgroud)

Twiddle JDBC DBMS驱动程序旋钮

确保您的代码将特定于DBMS的JDBC连接参数公开给您的M代码层,并使用它们.阅读您的特定DBMS的doco并适当地调整它们.例如,Oracle的JDBC驱动程序默认将默认的提取缓冲区大小(JDBC驱动程序中的一个,而不是您正在构建的那个)设置为大约10行,这对于典型的数据分析集大小来说太小了.(每次缓冲区填满时,都会导致数据库往返于网络.)只需将其设置为1,000或10,000行就像打开已设置为"off"的"Go Fast"开关一样.使用样本数据集对您的速度进行基准测试,并绘制结果图以选择适当的设置.

优化列数据类型传输

除了为您提供块提取功能外,编写自定义Java代码还可以为特定列类型进行优化类型转换.在处理了每行和每个单元的Java调用开销之后,您的瓶颈可能是在日期解析并将字符串从Java传递回Matlab.通过将SQL日期类型转换为Matlab datenums(作为Java双精度,带有列类型指示符)缓存日期解析到Java ,可能使用缓存来避免重新计算同一集合中的重复日期.(注意TimeZone问题.考虑Joda-Time.)将任何BigDecimals 转换double为Java端.并且瓶子是一个很大的瓶颈 - 一个炭柱可以淹没几个浮子柱的成本.如果可以的话(通过返回一个大的Java char[]然后使用reshape()),将窄CHAR列作为二维字符而不是像单元格返回,cellstr如果需要,转换到Matlab端.(返回Java String[]转换cellstr效率较低.)并且您可以通过将它们作为"符号"传回来优化低基数字符列的检索 - 在Java端,构建唯一字符串值的列表并将它们映射到数字代码,并返回字符串作为数字代码的原始数组以及数字地图 - >字符串; 在Matlab端将不同的字符串转换为cellstr,然后使用索引将其扩展为完整数组.这样会更快并且节省大量内存,因为写时复制优化将为重复的字符串值重用相同的原始字符数据.或者将它们转换为categoricalordinal,对象而不是cellstrs如果合适的话.如果您使用大量字符数据并具有大型结果集,则此符号优化可能是一个巨大的胜利,因为您的字符串列以大约原始数字速度传输,这实质上更快,并且它减少了celltr的典型内存碎片.(数据库工具箱现在也可以支持这些东西.我几年没有实际使用它.)

之后,根据您的DBMS,您可以通过将DBMS支持的所有数字列类型变量的映射包含在Matlab中的相应数值类型中,并尝试在模式中使用它们或在SQL中进行转换,从而挤出更快的速度.查询.例如,通过像这样的db/Matlab堆栈,Oracle BINARY_DOUBLE可以比正常情况下快NUMERIC一点.因人而异.

您可以考虑通过使用更便宜的数字标识符替换字符串和日期列来优化此用例的模式,可能将其作为外键来分隔查找表以将其解析为原始字符串和日期.可以使用足够的模式知识在客户端缓存查找.

如果你想发疯,你可以在Java级别使用多线程来异步预取和解析单独的Java工作线程上的下一个结果块,如果你有一个大的,可能会并行化每列日期和字符串处理光标块大小,而您正在对前一个块进行M代码级处理.这确实可以解决这个难题,理想情况下,这是一个小小的性能优势,因为您已经将昂贵的数据处理推入了Java层.保存最后一个.并检查JDBC驱动程序doco; 它可能已经有效地为你做了这件事.

如果你不愿意编写自定义的Java代码,您仍然可以通过改变Java方法调用的语法得到一些加速obj.method(...)method(obj, ...).例如getDouble(RESULTSET, n).这只是一个奇怪的Matlab OOP怪癖.但这并不是一场胜利,因为你仍在为每次通话支付Java/Matlab数据转换费用.

另外,请考虑更改代码,以便?在SQL查询中使用占位符和绑定参数,而不是将字符串插入为SQL文本.如果您正在进行自定义Java层,则定义自己的@connection和@preparedstatement M代码类是一种很好的方法.所以它看起来像这样.

QUERYSTRING = ['SELECT * FROM ' TABLENAME ' WHERE ts BETWEEN ? AND ?'];
query = conn.prepare(QUERYSTRING);
rslt = query.exec(startTime, endTime);
Run Code Online (Sandbox Code Playgroud)

这将为您提供更好的类型安全性和更易读的代码,并且还可以减少查询解析的服务器端开销.在仅有少数客户端的情况下,这不会给您带来太多的加速,但它会使编码更容易.

定期(在M代码和Java级别)配置和测试代码,以确保您的瓶颈位于您认为的位置,并查看是否存在需要根据您的数据集大小调整的参数,行数,列数和类型.我还想在Matlab和Java层构建一些检测和日志记录,以便您可以轻松地获得性能测量(例如,总结一下解析不同列类型所花费的时间,Java层中的多少以及多少Matlab层,以及服务器响应的等待时间(由于流水线操作可能不多,但你永远不知道)).如果您的DBMS公开其内部工具,也可以将其拉出来,这样您就可以看到您在服务器端花费的时间.