使用Slick处理大表失败,出现OutOfMemoryError

时间:2018-04-13 13:28:42

标签: scala slick akka-stream alpakka

我正在使用Akka Streams和Slick查询一个大的MySQL表,但它失败了WebViewClient wvc = new WebViewClient() { /* @Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { if (!progressDialog.isShowing()) { progressDialog.show(); } return super.shouldOverrideUrlLoading(view, url1); } @Override public void onPageFinished(WebView view, String url) { if (progressDialog.isShowing()) { progressDialog.dismiss(); } } */ @Override public WebResourceResponse shouldInterceptRequest(WebView view, String url) { try { DefaultHttpClient client = new DefaultHttpClient(); Utility.printMessage("url inside..." + url); HttpPost httpPost = new HttpPost("http://drive.google.com/viewerng/viewer?embedded=true&url=" + url1); Utility.printMessage("url after..." + "http://drive.google.com/viewerng/viewer?embedded=true&url=" + url1); httpPost.setHeader("x-csrf-token", Utility.getToken(context)); HttpResponse httpReponse = client.execute(httpPost); Header contentType = httpReponse.getEntity().getContentType(); Header encoding = httpReponse.getEntity().getContentEncoding(); InputStream responseInputStream = httpReponse.getEntity().getContent(); String contentTypeValue = null; String encodingValue = null; if (contentType != null) { contentTypeValue = contentType.getValue(); } if (encoding != null) { encodingValue = encoding.getValue(); } return new WebResourceResponse(contentTypeValue, encodingValue, responseInputStream); } catch (IOException e) { //return null to tell WebView we failed to fetch it WebView should try again. return null; } } }; 。似乎Slick正在将所有结果加载到内存中(如果查询仅限于几行,则不会失败)。为什么会这样,解决方案是什么?

OutOfMemoryError

1 个答案:

答案 0 :(得分:2)

来自Slick documentation

  

注意:某些数据库系统可能需要以某种方式设置会话参数以支持流式传输,而不会在客户端的内存中同时缓存所有数据。例如,PostgreSQL需要.withStatementParameters(rsType = ResultSetType.ForwardOnly, rsConcurrency = ResultSetConcurrency.ReadOnly, fetchSize = n)(具有所需的页面大小n)和.transactionally才能进行正确的流式传输。

换句话说,为了防止数据库将所有查询结果加载到内存中,可能需要额外的配置。此配置取决于数据库。 MySQL documentation声明如下:

  

默认情况下,ResultSet完全检索并存储在内存中。在大多数情况下,这是最有效的操作方式,并且由于MySQL网络协议的设计,更容易实现。如果您正在使用具有大量行或大值的ResultSet并且无法在JVM中为所需内存分配堆空间,则可以告诉驱动程序一次将结果流回一行。

     

要启用此功能,请按以下方式创建Statement实例:

stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
              java.sql.ResultSet.CONCUR_READ_ONLY);
stmt.setFetchSize(Integer.MIN_VALUE);
     

只读的只读结果集与提取大小Integer.MIN_VALUE的组合用作驱动程序逐行流式传输结果集的信号。

在Slick中设置上述配置:

import slick.jdbc._

val query =
  sql"select my_text from my_table".as(GetResult(r => r.nextString()))
    .withStatementParameters(
      rsType = ResultSetType.ForwardOnly,
      rsConcurrency = ResultSetConcurrency.ReadOnly,
      fetchSize = Int.MinValue
    )//.transactionally <-- I'm not sure whether you need ".transactionally"

val responses: Source[String, NotUsed] = Slick.source(query)