spark遇到的死锁

本文主要讨论了在Spark Streaming中遇到的数据库死锁问题,详细分析了死锁产生的原因,包括更新语句中索引的使用不当以及多线程事务操作。并提供了两种场景的实例,提出了相应的解决办法,如调整索引、避免使用事务等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数据库基础知识: http://blog.youkuaiyun.com/luyaran/article/details/53502917

 

死锁:如果需要“修改”一条数据,首先数据库管理系统会在上面加锁,以保证在同一时间只有一个事务能进行修改操作。锁定(Locking)发生在当一个事务获得对某一资源的“锁”时,这时,其他的事务就不能更改这个资源了,这种机制的存在是为了保证数据一致性

 错误: Causedby:java.sql.BatchUpdateException: Deadlock found when trying to get lock;tryrestarting transaction 

 

产生死锁的两个必要条件:

1.使用事物

2.where 条件后面 一个是索引,一个不是索引

 

场景一:

sparkStreaming 在foreachportion(相当于多线程) 里批量执行下面语句

updatebi_publish_back_cost set main_man='' where pkg_code='204022' andperformance_time='2017-05-22'

 

解决方法:

1.删掉其中的一个索引就可以了

2.把两个单独索引改为一个整体索引 (最终解决方法)

3.不使用事物

 

其他错误的尝试:dobatch时,每  100 条execute一次,还是锁住

 

场景二:

sparkStreaming 在foreachportion(相当于多线程) 里批量执行下面语句

INSERT INTObi_gamepublic_base_day_kpi(parent_game_id,child_game_id,medium_channel,ad_site_channel,pkg_code,publish_date,new_regi_device_num)VALUES (?,?,?,?,?,?,?) ON DUPLICATE KEY updateos=?,group_id=?,medium_account=?,promotion_channel=?,promotion_mode=?,head_people=?,new_regi_device_num=new_regi_device_num+ VALUES(new_regi_device_num)

产生死锁的原因: child_game_id,medium_channel,ad_site_channel,pkg_code中  child_game_id 和  pkg_code 为 unique key ,执行这样的语句,更新的时候 unique key  就相当于where,后续原因如场景一

解决方法:

1.把5个字段设置为一个整体索引

2.不使用事物插入(最终解决方法)

 

死锁解决方法总结:

1.多线程 批量更新数据库的时候,where后面的限制条件要么是同一索引,要不都不是索引,不要一般是索引,一半不是索引

2.不要使用 多线程事物插入

 

===多线程死锁

public class DeadLocker {

public static void main(String[] args) {

DeadLock t1 = new DeadLock();

t1.setName("线程1");

DeadLock t2 = new DeadLock();

t2.setName("线程2");

t1.flag = true;

t2.flag = false;

t1.start();

t2.start();

}

}

 

class DeadLock extends Thread {

 

public boolean flag = true;

static Object o1 = new Object();// 定义两个公共资源

static Object o2 = new Object();

 

@Override

public void run() {

// TODO Auto-generated method stub

if (flag) {

System.out.println(Thread.currentThread().getName() + "等待o1");

synchronized (o1) {

System.out.println(Thread.currentThread().getName() + "占用o1,等待o2");

synchronized (o2) {

System.out.println(Thread.currentThread().getName() + "占用o2");

}

System.out.println(Thread.currentThread().getName() + "释放o2");

}

System.out.println(Thread.currentThread().getName() + "释放o1");

} else {

System.out.println(Thread.currentThread().getName() + "等待o2");

synchronized (o2) {

System.out.println(Thread.currentThread().getName() + "占用o2,等待o1");

synchronized (o1) {

System.out.println(Thread.currentThread().getName() + "占用o1");

}

System.out.println(Thread.currentThread().getName() + "释放o1");

}

System.out.println(Thread.currentThread().getName() + "释放o2");

}

}

}

 

 

 

 

 

 

 

<think>好的,我现在需要帮助用户理解在容器化部署中使用spark-shell和kubectl命令启动Spark应用程序的区别及适用场景。首先,我需要回顾用户提供的引用内容,看看有没有相关的信息可以利用。用户提供的引用中,引用[1]解释了SparkDriver的作用,引用[2]给出了一个使用SparkContext的Python示例,引用[3]提到Kubernetes集群的安装和核心概念,引用[4]涉及Docker镜像的创建和上传,引用[5]讨论了Kubernetes调度与YARN的差异。这些信息可能对比较两种启动方式有帮助。 用户的问题是关于在容器化部署中使用spark-shell和kubectl启动Spark的区别和适用场景。我需要先明确这两种方法的具体操作步骤以及各自的优缺点。首先,spark-shell通常用于交互式开发,而kubectl是管理Kubernetes集群的命令行工具,用于部署Pod等资源。接下来,根据引用[5],Kubernetes在调度Spark任务时可能存在资源竞争和死锁的问题,而YARN有更成熟的调度策略。这可能影响到使用kubectl启动时的资源管理效率。而spark-shell可能在容器化环境中需要特定的配置才能连接到Kubernetes集群,比如设置master为k8s的API地址。然后,引用[4]提到了构建和推送Docker镜像的步骤,这可能与两种启动方式所需的镜像有关。使用kubectl可能需要预先构建包含Spark和必要依赖的镜像,而spark-shell可能需要本地环境与容器环境的协调。另外,引用[2]中的代码示例展示了如何在本地模式下运行Spark应用,但在容器化环境中,可能需要调整配置,比如设置master为Kubernetes集群的地址,并确保依赖项正确打包到镜像中。现在需要整理两种方法的优劣。spark-shell适合交互式调试和开发,但可能在容器化部署中不够灵活,需要手动处理资源分配和集群配置。而kubectl直接管理Pod,更适合生产环境,能够利用Kubernetes的动态资源调度和自动扩缩容,但可能需要更复杂的配置和镜像准备。 适用场景方面,spark-shell可能在测试和开发阶段更方便,而kubectl适合自动化部署和规模化生产环境。同时,需要考虑资源管理和调度策略的不同,如引用[5]提到的Kubernetes的调度策略可能导致的问题,这时候可能需要调整资源配置或使用特定的调度策略来优化。最后,生成相关问题时要围绕用户可能遇到的后续问题,比如如何配置spark-shell连接Kubernetes,或者如何优化kubectl提交任务时的资源参数。</think>### 容器化环境中 spark-shell 与 kubectl 启动 Spark 的对比分析 #### 一、核心差异对比 | **维度** | **spark-shell** 启动方式 | **kubectl** 启动方式 | |----------------|--------------------------------------------------|-----------------------------------------------| | **交互性** | 提供 REPL 交互环境,适合调试与数据探索[^2] | 仅支持批量提交任务,无交互界面 | | **资源管理** | 依赖 Spark 自身资源分配逻辑 | 通过 Kubernetes 原生调度策略动态分配资源[^5] | | **部署模式** | 需手动配置容器网络与镜像依赖 | 直接利用 Kubernetes Pod 编排能力[^3] | | **适用阶段** | 开发测试阶段 | 生产环境部署 | #### 二、具体实现方法 ##### 1. **spark-shell 启动方式** ```bash # 在容器内启动 spark-shell 并连接 Kubernetes 集群 spark-shell \ --master k8s://https://<api-server>:6443 \ --conf spark.kubernetes.container.image=spark-k8s:3.5.0 \ --conf spark.executor.instances=3 ``` * **优势**: - 实时查看 RDD 转换操作结果(如 `rdd.collect()`[^2]) - 快速验证数据逻辑 * **劣势**: - 需要预先配置容器网络策略 - 无法利用 Kubernetes 的自动扩缩容特性 ##### 2. **kubectl 启动方式** ```bash # 提交 Spark 作业为 Kubernetes Job kubectl apply -f - <<EOF apiVersion: batch/v1 kind: Job metadata: name: spark-pi spec: template: spec: containers: - name: spark image: spark-k8s:3.5.0 command: ["spark-submit"] args: --master k8s://https://kubernetes.default.svc --class org.apache.spark.examples.SparkPi /opt/spark/examples/jars/spark-examples_2.12-3.5.0.jar restartPolicy: Never EOF ``` * **优势**: - 天然集成 Kubernetes 存储卷、服务发现等特性[^3] - 支持通过 Horizontal Pod Autoscaler 自动扩缩容 * **劣势**: - 调试日志需通过 `kubectl logs` 间接获取 - 启动延迟较高(需创建 Pod 等资源) #### 三、性能特征对比 $$ \text{响应时间} = \begin{cases} O(1) & \text{spark-shell 已预热} \\ O(\text{Pod 创建时间}) & \text{kubectl 冷启动} \end{cases} $$ $$ \text{资源利用率} = \frac{\text{实际使用 vCPU}}{\text{分配 vCPU}} \times 100\% $$ - spark-shell 通常资源利用率较低(保留空闲资源供交互使用) - kubectl 方式可通过 `spark.executor.cores` 精准控制资源[^1] #### 四、典型场景选择建议 | **场景** | **推荐方式** | **理由** | |---------------------------|-------------------|-----------------------------------------------| | 数据科学家探索性分析 | spark-shell | 即时反馈计算结果[^2] | | 生产环境定时批处理任务 | kubectl | 利用 K8s CronJob 实现调度[^3] | | 需要挂载持久化存储的任务 | kubectl | 直接使用 PVC 卷声明[^3] | | 开发阶段 API 接口验证 | spark-shell | 快速执行 `spark_context.parallelize()`[^2] |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值