当前位置:首页 > 问答 > 正文

Spark写数据进数据库那点事儿,怎么又快又稳地搞定

(来源:知乎专栏“大数据实战笔记”)直接说重点,Spark往数据库里写数据,想又快又稳,核心就两件事:别把数据库搞垮,别让Spark任务本身失败,这听起来简单,但坑都在细节里。

第一,怎么“稳”?防止把数据库写挂是头等大事。

想象一下,你启动了一个有上千个执行器(Executor)的Spark任务,同时往数据库的同一张表里疯狂插入数据,这就像节假日的高速公路,所有车都想同时挤进同一个收费站,结果就是彻底堵死,数据库连接池爆满,CPU打满,最终可能直接宕机。(来源:个人实践中的惨痛教训)

那怎么办?关键一招是控制并发度

  1. 使用numPartitions参数:这是最重要的一个开关,Spark写JDBC数据库时,默认的并发度可能不适合你的数据库,你可以通过这个参数明确指定用多少个连接(也就是多少个分区)同时去写,比如你的数据库比较弱小,就设成5个或10个;如果数据库很强大,可以设大一些,比如50个,这个数字没有标准答案,需要根据数据库的实际抗压能力来测试调整。(来源:Spark官方文档JDBC部分)
  2. 告别SaveMode.Overwrite的“坑”:如果你选择Overwrite模式,Spark默认会先执行DROP TABLECREATE TABLE,这太危险了,万一任务在中间失败,表就没了,稳妥的做法是加一个参数:.option("truncate", "true"),这样Spark会改用TRUNCATE TABLE来清空数据,而不是删表,属于原子操作,安全得多。(来源:Spark源码分析及JIRA issue讨论)
  3. 做好重试与容错:网络是不稳定的,数据库偶尔也可能抽风,可以在Spark作业层面配置重试策略,比如某个任务失败后自动重试几次,但要注意,如果是数据本身的问题(比如主键冲突),重试也没用,需要先在数据清洗阶段解决。

第二,怎么“快”?在保证稳定的前提下追求速度。

速度上不去,很多时候不是Spark计算慢,而是写数据库的姿势不对。

  1. 批处理才是王道:最最最重要的优化就是批量插入(Batch Insert),千万不要一条一条地插入记录!通过设置.option("batchsize", 10000)这样的参数,Spark会把数据攒够一定数量(比如一万条)后,打成一个批次发给数据库,这极大地减少了网络往返次数和数据库的事务开销,性能提升是数量级的,这个batchsize通常设置在几千到几万之间,需要根据数据库和网络情况调整。(来源:数据库性能优化最佳实践)
  2. 合理设置事务提交间隔:另一个相关的参数是.option("isolationLevel", "NONE"),在允许的情况下(比如数据是追加、不允许脏读的场景),设置这个可以避免Spark在每个批次插入时都开启一个耗时的事务,也能提升速度,但使用这个要谨慎,确保业务能接受。(来源:Stack Overflow上Spark贡献者的回答)
  3. 源头控制:写之前就减少数据量:如果数据源很大,但在写入前能进行有效的过滤、去重、聚合,那么最终需要写入数据库的数据量就会变小,写入时间自然缩短,这叫“从源头解决问题”。
  4. 分区与并行度的平衡艺术:前面说的numPartitions是双刃剑,设得太小,无法利用集群并行能力,速度慢;设得太大,又会压垮数据库,一个常见的做法是,让分区的数量和数据量相匹配,避免出现每个分区数据量过小(比如只有几KB)导致任务调度开销过大,或者每个分区数据量过大(比如几个GB)导致内存溢出(OOM),可以尝试通过repartition()coalesce()在写入前调整分区数。

第三,一些更实际的技巧和避坑指南。

  • 预建索引的时机:如果表需要建索引,一定要在数据写入完成后再建,一边插入数据一边维护索引,会慢得让你怀疑人生。
  • 对付自增主键:如果目标表有自增主键(如AUTO_INCREMENT),要小心,如果Spark的多个任务同时写,可能会引起主键冲突,一种解决方案是使用数据库的序列(Sequence)或者其他分布式ID生成算法(如雪花算法)来在Spark端生成ID,而不是依赖数据库的自增。
  • 监控与观察:快和稳不是一次调优就能永恒的,要密切监控数据库在写入期间的性能指标:CPU使用率、IO等待、网络流量,也要看Spark作业的监控界面,有没有出现数据倾斜(某个任务特别慢)?GC(垃圾回收)时间是否过长?根据这些实时信息动态调整你的参数。

又快又稳的秘诀就是:像对待一个需要呵护的朋友一样对待你的数据库,通过numPartitions控制并发别吓着它,用batchsize批量操作提高效率减轻它的负担,并在整个过程中时刻关注它的“健康状况”(监控),多试几次,你就能找到最适合你当前环境和数据量的那个“黄金参数组合”。(来源:多年大数据平台开发运维经验)

Spark写数据进数据库那点事儿,怎么又快又稳地搞定