Jdbc 将数据从CloudSql流式传输到数据流

Jdbc 将数据从CloudSql流式传输到数据流,jdbc,google-cloud-dataflow,apache-beam,spotify-scio,Jdbc,Google Cloud Dataflow,Apache Beam,Spotify Scio,我们目前正在探索如何使用apachebeam/googledataflow处理googlecloudsql数据库(MySQL)中的大量数据存储 数据库在一个表中存储大约200GB的数据 我们使用JdbcIO成功地从数据库中读取了行,但到目前为止,这只有在限制查询的行数时才可能实现。否则我们将遇到内存问题。我假设默认情况下,SELECT查询尝试将所有结果行加载到内存中 这方面的惯用方法是什么?批处理SQL查询?结果如何 我们尝试调整执行语句的获取大小,但没有多大成功 这就是我们的JDBC读取设置:

我们目前正在探索如何使用apachebeam/googledataflow处理googlecloudsql数据库(MySQL)中的大量数据存储

数据库在一个表中存储大约200GB的数据

我们使用
JdbcIO
成功地从数据库中读取了行,但到目前为止,这只有在
限制查询的行数时才可能实现。否则我们将遇到内存问题。我假设默认情况下,
SELECT
查询尝试将所有结果行加载到内存中

这方面的惯用方法是什么?批处理SQL查询?结果如何

我们尝试调整执行语句的
获取大小
,但没有多大成功

这就是我们的JDBC读取设置:

JdbcReadOptions(
  connectionOptions = connOpts,
  query = "SELECT data FROM raw_data",
  statementPreparator = statement => statement.setFetchSize(100),
  rowMapper = result => result.getString(1)
)
到目前为止,我还没有找到任何关于sql流的资源

编辑

我将列出我采取的一种查看方法,以便其他人可以学习一些东西(例如如何这样做)。为了增加一点上下文,所讨论的数据库表的结构非常糟糕:它有一个包含JSON字符串的列,
id
列(主键)加上一个
added
modified
列(都是
TIMESTAMP
类型)。在第一次接近时,它没有进一步的指数。该表包含2500万行。因此,这可能更像是一个数据库问题,而不是ApacheBeam/JDBC问题。但尽管如此:

方法1(以上)-查询所有内容 基本上是这样的:

val readOptions = JdbcReadOptions(
  connectionOptions = connOpts,
  query = "SELECT data FROM raw_data",
  rowMapper = result => result.getString(1)
)

context
  .jdbcSelect(readOptions)
  .map(/*...*/)
如果我在查询中添加了一个
限制
,这就行了。但很明显,这是非常缓慢的

方法2-键集分页 这加快了速度,但最终查询时间变长了

方法3-无波束/数据流 这会将结果集逐行流式处理并将行写入文件。对于所有2500万条记录,它运行了大约2个小时。最后如果有人能指出如何使用Beam实现此解决方案,那就太好了


顺便说一句:现在,我有了原始数据作为CSV文件处理与梁是一件轻而易举的事。它大约有80GB的原始数据,可以通过自动缩放等功能在大约5分钟内转换为另一种CSV格式。

我认为JDBCIO由于其固有的局限性(单选)而无法很好地扩展。我不知道MySQL和BEAM是否支持流媒体


您可能可以将数据库转储到数据处理系统更容易处理的位置(例如,csv)。它对您有用吗?

看来MySQL JDBC驱动程序需要一些特殊的措施,以使其不会将整个结果集加载到内存中;e、 我能够在另一个项目中找到解决问题的方法
JdbcIO
将需要执行相同的操作,或者至少可以进行足够的配置以允许用户执行。我提交了这个问题


同时,作为一种解决方法,您可以使用
JdbcIO.readAll()
将查询划分为许多较小的查询,例如,您可以按一系列ID对其进行划分。请注意,它们之间不会强制执行事务一致性-就MySQL而言,它们将是独立的查询。

JdbcIO不限于一个select:您可以使用readAll()执行多个查询,并从PCollection获取参数。我现在将DB转储到CSV,这更便于处理。谢谢你的提示。我会检查这是否解决了问题!不幸的是,数据库的结构非常糟糕。即使使用键集分页,查询也会变得非常慢。您能告诉我们更多信息吗,例如,显示一些代码片段,并告诉我们与CSV相比,它的性能如何?您的经验对JdbcIO的未来用户将非常有价值。我将编辑我的原始问题并提供更多细节!谢谢您所做的还不够-还需要BEAM-3714中的所有其他步骤(指定结果集类型等)。或者,您是否尝试过使用readAll()并对查询进行分区的建议,以便可以并行查询主键的不同范围,并且每个范围不包含太多数据?(似乎没有,因为你最终通过CSV导出-但只是好奇)哦,我明白了。使用LIMIT/OFFSET进行分页不会给您带来任何并行性,因为数据库通常无法在不扫描和丢弃前9页的情况下为您提供第10页结果,因此基本上您的查询会扫描整个数据库N次。您需要按主键的值对其进行分区,例如“从原始数据中选择数据,在X和Y之间添加”,其中您提供的[X,Y]对的总范围包括“添加”的所有可能值。此外,您似乎没有使用JdbcIO.readAll()。我想scio并不明确支持它,但它也不会阻止您使用它,对吗?(您可以对PCollection应用任意束变换,而不仅仅是scio有包装器的那些)我可能可以使用readAll()对于scio。我对scio和Beam/数据流都是新手,这就是为什么我还没有完全意识到这些可能性。尽管回顾过去,我发现最终有效的解决方案(w/o Beam)是我的用例中最明显、最优雅的解决方案。不过,感谢所有的提示和解释!
val queries = List(
  "SELECT data from raw_data LIMIT 5000 OFFSET 0",
  "SELECT data from raw_data LIMIT 5000 OFFSET 5000",
  "SELECT data from raw_data LIMIT 5000 OFFSET 10000"
  // ...
)

context
  .parallelize(queries)
  .map(query => {
      val connection = DriverManager.getConnection(/* */)
      val statement = connection.prepareStatement(query)
      val result = statement.executeQuery()

      makeIterable(result) // <-- creates a Iterator[String]
  })
  .flatten
  .map(/* processing */)
SELECT data FROM raw_data ORDER BY added LIMIT 5000 OFFSET x
val connection = DriverManager.getConnection(/* */)
val statement = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY)
statement.setFetchSize(Integer.MIN_VALUE)

val rs = statement.executeQuery("SELECT data FROM raw_data")

while(rs.next()) {
  writer writeLine rs.getString(1)
}