【博客550】k8s乐观锁机制:控制并发请求与数据一致性

news/2024/5/18 14:04:57/文章来源:https://blog.csdn.net/qq_43684922/article/details/128160129

k8s乐观锁机制:控制并发请求与数据一致性

1、乐观锁与悲观锁

悲观锁

悲观并发控制(又名“悲观锁”,Pessimistic Concurrency Control,缩写“PCC”)是一种并发控制的方法。它可以阻止一个事务以影响其他用户的方式来修改数据。如果一个事务执行的操作读某行数据应用了锁,那只有当这个事务把锁释放,其他事务才能够执行与该锁冲突的操作。

在悲观锁的场景下,假设用户A和B要修改同一个文件,A在锁定文件并且修改的过程中,B是无法修改这个文件的,只有等到A修改完成,并且释放锁以后,B才可以获取锁,然后修改文件。由此可以看出,悲观锁对并发的控制持悲观态度,它在进行任何修改前,首先会为其加锁,确保整个修改过程中不会出现冲突,从而有效的保证数据一致性。但这样的机制同时降低了系统的并发性,尤其是两个同时修改的对象本身不存在冲突的情况。同时也可能在竞争锁的时候出现死锁,所以现在很多的系统例如Kubernetes采用了乐观并发的控制方法。

乐观锁

乐观并发控制(又名“乐观锁”,Optimistic Concurrency Control,缩写“OCC”)是一种并发控制的方法。它假设多用户并发的事务在处理时不会彼此影响,各事务能够在不请求锁的情况下处理各自的数据。在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务又修改了该数据。如果其他事务有更新的话,正在提交的事务会进行回滚。

相对于悲观锁对锁的提前控制,乐观锁相信请求之间出现冲突的概率是比较小的,在读取及更改的过程中都是不加锁的,只有在最后提交更新时才会检测冲突,因此在高并发量的系统中占有绝对优势。同样假设用户A和B要修改同一个文件,A和B会先将文件获取到本地,然后进行修改。如果A已经修改好并且将数据提交,此时B再提交,服务器端会告知B文件已经被修改,返回冲突错误。此时冲突必须由B来解决,可以将文件重新获取回来,再一次修改后提交。
乐观锁通常通过增加一个资源版本字段,来判断请求是否冲突。初始化时指定一个版本值,每次读取数据时将版本号一同读出,每次更新数据,同时也对版本号进行更新。当服务器端收到数据时,将数据中的版本号与服务器端的做对比,如果不一致,则说明数据已经被修改,返回冲突错误。

2、k8s采用乐观锁机制

在Kubernetes集群中,外部用户及内部组件频繁的数据更新操作,导致系统的数据并发读写量非常大。假设采用悲观并行的控制方法,将严重损耗集群性能,因此Kubernetes采用乐观并行的控制方法。Kubernetes通过定义资源版本字段实现了乐观并发控制,资源版本(ResourceVersion)字段包含在Kubernetes对象的元数据(Metadata)中。这个字符串格式的字段标识了对象的内部版本号,其取值来自etcd的modifiedindex,且当对象被修改时,该字段将随之被修改。值得注意的是该字段由服务端维护,不建议在客户端进行修改。

3、k8s如何采用乐观锁机制控制并发请求与数据一致性

Kube-Apiserver可以通过该字段判断对象是否已经被修改。当包含ResourceVersion的更新请求到达Apiserver,服务器端将对比请求数据与服务器中数据的资源版本号,如果不一致,则表明在本次更新提交时,服务端对象已被修改,此时Apiserver将返回冲突错误(409),客户端需重新获取服务端数据,重新修改后再次提交到服务器端。

上述并行控制方法可防止如下的data race:

Client1: GET Foo
Client2: GET FooClient1: Set Foo.Bar = "one"
Client1: PUT Foo Client2</span>: Set Foo.Baz = "two"
Client2: PUT Foo

当未采用并发控制时,假设发生如上请求序列,两个客户端同时从服务端获取同一对象Foo(含有Bar、Baz两个字段),Client#1先将Bar字段置成one,其后Client#2对Baz字段赋值的更新请求到服务端时,将覆盖Client#1对Bar的修改。反之在对象中添加资源版本字段,同样的请求序列将如下:

Client1: GET Foo  //初始Foo.ResourceVersion=1
Client2: GET Foo  //初始Foo.ResourceVersion=1Client 1: Set Foo.Bar ="one"
Client1: PUT Foo  //更新Foo.ResourceVersion=2</span>Client2: Set Foo.Baz = "two"
Client2: PUT Foo  //返回409冲突</span>

Client1更新对象后资源版本号将改变,Client#2在更新提交时将返回冲突错误(409),此时Client#2必须在本地重新获取数据,更新后再提交到服务端。

假设更新请求的对象中未设置ResourceVersion值,Kubernetes将会根据硬改写策略(可配置)决定是否进行硬更新。如果配置为可硬改写,则数据将直接更新并存入Etcd,反之则返回错误,提示用户必须指定ResourceVersion。

4、k8s的Update和Patch

Update:

对于Update,客户端更新请求中包含的是整个obj对象,服务器端将对比该请求中的obj对象和服务器端最新obj对象的ResourceVersion值。如果相等,则表明未发生冲突,将成功更新整个对象。反之若不相等则返回409冲突错误

基本流程:

1.获取当前更新请求中obj对象的ResourceVersion值,及服务器端最新obj对象(existing)的ResourceVersion值

2.如果当前更新请求中obj对象的ResourceVersion值等于0,即客户端未设置该值,则判断是否要硬改写(AllowUnconditionalUpdate),如配置为硬改写策略,将直接更新obj对象。

3.如果当前更新请求中obj对象的ResourceVersion值不等于0,则判断两个ResourceVersion值是否一致,不一致返回冲突错误(OptimisticLockErrorMsg)。

Patch

相比Update请求包含整个obj对象,Patch请求实现了更细粒度的对象更新操作,其请求中只包含需要更新的字段。例如要更新pod中container的镜像,可使用如下命令:
kubectl patch pod my-pod -p ‘{“spec”:{“containers”:[{“name”:“my-container”,“image”:“new-image”}]}}’

基本流程:

1.首先判断patch类型,根据类型选择相应的mechanism

2.将patch应用到最新获取的服务器端obj上,生成一个已更新的obj,再对该obj继续执行admission chain中的Admit与Validate。最终调用的还是update方法,因此冲突检测的机制与上述Update方法完全一致。

3.调用Update方法执行更新操作。

5、k8s的对象版本控制ResourceVersion和Generation

etcd version机制

ETCD共四种version:
在这里插入图片描述

ResourceVersion:

基于底层etcd的revision机制,资源对象每次update时都会改变,且集群范围内唯一

resourceversion存在哪里:

更新对象时,Kubernetes会比较该resourceVersion和ETCD中对象的resourceVersion,在一致的情况下都会更新,一旦发生更新,该对象的resourceVersion值也会改变。所以,resourceVersion相当于一把锁。
当然,Kubernetes在resourceVersion值的生成上,并没有实现自己的一套管理机制,而是直接使用了ETCD的index。
在ETCD中,会维护一个全局的index,每发生一个操作,该index会加1。每个key都会维护一个modified index,表明该节点最近的一次更改index。所以Kubernetes就是借用了modified index。
那和,既然从ETCD的节点中能获取到resourceVersion(即modified index),那就没必要把resourceVersion存储到ETCD中了。所以存储在ETCD中的对象并没有resourceVersion字段,而是在获取时动态添加resourceVersion字段。

resourceVersion的维护其实是利用了底层存储etcd的Revision机制:

// Get implements storage.Interface.Get.
func (s *store) Get(ctx context.Context, key string, opts storage.GetOptions, out runtime.Object) error {key = path.Join(s.pathPrefix, key)startTime := time.Now()getResp, err := s.client.KV.Get(ctx, key)metrics.RecordEtcdRequestLatency("get", getTypeName(out), startTime)if err != nil {return err}if err = s.validateMinimumResourceVersion(opts.ResourceVersion, uint64(getResp.Header.Revision)); err != nil {return err}if len(getResp.Kvs) == 0 {if opts.IgnoreNotFound {return runtime.SetZeroValue(out)}return storage.NewKeyNotFoundError(key, 0)}kv := getResp.Kvs[0]data, _, err := s.transformer.TransformFromStorage(kv.Value, authenticatedDataString(key))if err != nil {return storage.NewInternalError(err.Error())}return decode(s.codec, s.versioner, data, out, kv.ModRevision)
}

kv.ModRevision看到从ETCD读取了key的ModRevision,继续看下decode函数

// decode decodes value of bytes into object. It will also set the object resource version to rev.
// On success, objPtr would be set to the object.
func decode(codec runtime.Codec, versioner storage.Versioner, value []byte, objPtr runtime.Object, rev int64) error {if _, err := conversion.EnforcePtr(objPtr); err != nil {return fmt.Errorf("unable to convert output object to pointer: %v", err)}_, _, err := codec.Decode(value, nil, objPtr)if err != nil {return err}// being unable to set the version does not prevent the object from being extractedif err := versioner.UpdateObject(objPtr, uint64(rev)); err != nil {klog.Errorf("failed to update object version: %v", err)}return nil
}

到这里已经可出Kubernetes的resourceVersion是利用了底层ETCD kv版本机制。

根据更新资源时是否带有resourceVersion分两种情况:

  • 未带resourceVersion:无条件更新,获得etcd中最新的数据然后再此基础上更新
  • 带有resourceVersion:和etcd中modRevision对比,不一样就提示版本冲突,说明数据已发生修改,当前要修改的版本已不是最新数据。

Generation:

初始值为1,随Spec内容的改变而自增

func (deploymentStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {deployment := obj.(*apps.Deployment)deployment.Status = apps.DeploymentStatus{}deployment.Generation = 1pod.DropDisabledTemplateFields(&deployment.Spec.Template, nil)
}func (deploymentStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {newDeployment := obj.(*apps.Deployment)oldDeployment := old.(*apps.Deployment)newDeployment.Status = oldDeployment.Statuspod.DropDisabledTemplateFields(&newDeployment.Spec.Template, &oldDeployment.Spec.Template)// Spec updates bump the generation so that we can distinguish between// scaling events and template changes, annotation updates bump the generation// because annotations are copied from deployments to their replica sets.if !apiequality.Semantic.DeepEqual(newDeployment.Spec, oldDeployment.Spec) ||!apiequality.Semantic.DeepEqual(newDeployment.Annotations, oldDeployment.Annotations) {newDeployment.Generation = oldDeployment.Generation + 1}
}

6、ResourceVersion在list-watch机制中的使用

ResourceVersion字段在Kubernetes中除了用在上述并发控制机制外,还用在Kubernetes的list-watch机制中。Client端的list-watch分为两个步骤,先list取回所有对象,再以增量的方式watch后续对象。Client端在list取回所有对象后,将会把最新对象的ResourceVersion作为下一步watch操作的起点参数,也即Kube-Apiserver以收到的ResourceVersion为起始点返回后续数据,保证了list-watch中数据的连续性与完整性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_229615.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

分布滞后线性和非线性模型(DLNM)分析空气污染(臭氧)、温度对死亡率时间序列数据的影响...

全文下载链接 http://tecdat.cn/?p23947 分布滞后非线性模型&#xff08;DLNM&#xff09;表示一个建模框架&#xff0c;可以灵活地描述在时间序列数据中显示潜在非线性和滞后影响的关联。该方法论基于交叉基的定义&#xff0c;交叉基是由两组基础函数的组合表示的二维函数空间…

【STM32学习(1)】详解STM32时钟体系

一、8051和stm32时钟体系结构区别 HSE&#xff1a;外部高速的振荡时钟&#xff08;8MHZ&#xff09; HSI&#xff1a;内部高速的振荡时钟&#xff08;16MHZ&#xff09; LSI&#xff1a;内部低速的振荡时钟&#xff08;32KHZ&#xff09; LSK&#xff1a;外部低速的振荡时钟&a…

开发一个简单的http模板之序章

流程 1.当通过开发HTTP模块来实现产品功能时&#xff0c;是可以完全享用Nginx的优秀设计所带来的、 与官方模块相同的高并发特性的。不过&#xff0c;如何开发一个充满异步调用、无阻塞的HTTP模块 呢2. 需要把程序嵌入到Nginx中&#xff0c;也就是说&#xff0c;最终编译出的二…

C++——隐式类型转换

隐式类型转换&#xff08;构造函数的隐式调用&#xff09; 先看一下隐式类型转换如何发生吧&#xff1a; #include <iostream> using namespace std;class point {public:int x,y;point(int x0, int y0):x(x),y(y) {} }void displayPoint(const point &p) {cout &l…

Clock and Jitter

1、Jitter定义 定义1&#xff08;SONET规范&#xff09;&#xff1a;抖动可以定义为数字信号在重要时点上偏离理想时间位置的短期变化。 2、Total Jitter表征方式 2.1、周期抖动&#xff08;Period Jitter&#xff09;&#xff0c;与理想时钟无关&#xff0c;不累积 Period …

(AVL)平衡二叉树

还是照旧&#xff0c;本篇主要讲一下代码实现&#xff0c;AVL相关的定义什么的这里不多赘述。 AVL树就是为了解决bst树出现了“线性”的问题&#xff0c;而发明的。什么是线性的就是一棵bst树全都只有左子树或者全都只有右子树&#xff0c;能想象来吧。 目录 LL型调整(左旋) …

axios.defaults.baseURL的三种配置方法

axios.defaults.baseURL的三种配置方法目录概述需求&#xff1a;设计思路实现思路分析1.少2.2.动态获取请求地址3.3.采用配置文件参考资料和推荐阅读Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,m…

Spring学习:二、Bean的管理

4. Bean的管理 ​ Spring的基本Bean管理包括Bean配置&#xff0c;Bean实例化和Bean的依赖注入。这些管理可以通过手工编码的方式把每个Bean注册到容器中&#xff0c;也可以通过properties文件和xml文件配置Bean和Bean之间的依赖关系。通常我们的配置方式是XML作为配置文件。 …

DNS查询流程

&#x1f468;‍&#x1f4bb;个人主页&#xff1a; 才疏学浅的木子 &#x1f647;‍♂️ 本人也在学习阶段如若发现问题&#xff0c;请告知非常感谢 &#x1f647;‍♂️ &#x1f4d2; 本文来自专栏&#xff1a; 计算机网络 ❤️ 支持我&#xff1a;&#x1f44d;点赞 &#…

李宏毅《DLHLP》学习笔记6 - 语言模型

视频链接&#xff1a;https://www.youtube.com/watch?vdymfkWtVUdo&listPLJV_el3uVTsO07RpBYFsXg-bN5Lu0nhdG&index8&ab_channelHung-yiLee 课件链接&#xff1a;https://speech.ee.ntu.edu.tw/~tlkagk/courses/DLHLP20/ASR3.pdf 1. Language Model LM的作用是预…

FFmpeg二次开发

本文主要讲解 FFmpeg 的二次开发&#xff0c;ffmpeg.exe 的命令行功能特别强大&#xff0c;很多需求都能直接用命令行实现&#xff0c;但是总有一些需求用 命令行实现不太好做。 而你实现那些特殊需求&#xff0c;通常需要把 ffmpeg.exe 里面的某部分代码抄过来&#xff0c;本…

阿里云新用户活动:云服务器ECS 新购、升级报价出炉了!

阿里云新人特惠&#xff0c;阿里云新用户新购升级立享满减&#xff0c;新购升级云服务器ECS &#xff0c;购买热门产品 s6/u1/c6/g6/r6/c7/g7/r7指定配置&#xff0c;可享折上折&#xff01;从未购买过云服务器ECS或者轻量应用服务器的用户一次性可领取3张优惠券。优惠券适用于…

VS Code快速实现Git PR操作

注意&#xff1a;建议先学习git的基本操作。 安装插件 下图中红圈标记的插件都安装好。 Fork上游仓库 在网页上点击你想要fork的仓库&#xff0c;点击fork 然后该仓库就会fork到你的github账户下面&#xff0c;如下图。 现在可以在你账户下面的repo&#xff08;我们称为下…

Allegro如何移动器件操作指导

Allegro如何移动器件操作指导 Allegro上可以任意移动器件,具体操作如下 选择Edit-move Find选择Symbols Point根据需要选择 Sym Origin是抓取器件的原点 Body center是抓取器件的中心 User Pick可以自定义抓取的原点,在移动整个模块的并且旋转的时候常用的命令 Sym Pin#设…

【抓包工具】win 10 / win 11:WireShark 下载、安装、使用

目录 一、WireShark 下载 二、WireShark 安装 &#xff08;1&#xff09;双击运行安装程序 &#xff08;2&#xff09;Choose Components&#xff1a;选择组件 &#xff08;3&#xff09;Additional Tasks&#xff1a;附加任务 &#xff08;4&#xff09;Choose lnstall …

Pikachu靶场全关攻略(超详细!)

一、靶场搭建 准备工具 phpstudy**pikachu靶场下载地址&#xff1a;**https://github.com/zhuifengshaonianhanlu/pikachu 搭建过程 将靶场文件夹放到phpstudy的www目录 进入pikach文件夹的inc目录&#xff0c;修改靶场配置文件config.inc.php&#xff0c;设置数据库账号密…

微服务框架 SpringCloud微服务架构 10 使用Docker 10.6 容器命令练习

微服务框架 【SpringCloudRabbitMQDockerRedis搜索分布式&#xff0c;系统详解springcloud微服务技术栈课程|黑马程序员Java微服务】 SpringCloud微服务架构 文章目录微服务框架SpringCloud微服务架构10 使用Docker10.6 容器命令练习10.6.1 直接开干10 使用Docker 10.6 容器…

Stable Diffusion 2.0 来了

Stable Diffusion 一经发布&#xff0c;就立刻在业界掀起巨大的波浪。我个人后知后觉&#xff0c;直到 Stable Diffusion V1.4 版本发布&#xff0c;才接触 Stable Diffusion (之前使用的是 Disco Diffusion)。这段时间&#xff0c;SD 团队也没闲着&#xff0c;很快就发布了 V2…

HTML学生个人网站作业设计 明星易烊千玺介绍(HTML+CSS) web前端开发技术 web课程设计 网页规划与设计

&#x1f389;精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

2022年第十一届认证杯数学中国数学建模国际赛小美赛:C 题 对人类活动进行分类 建模方案及代码实现

2022年第十一届认证杯数学中国数学建模国际赛小美赛&#xff1a;C 题 对人类活动进行分类 建模方案及代码实现 1 题目 人类行为理解的一个重要方面是对日常活动的识别和监控。可穿戴活动识别系统可以在许多关键领域提高生活质量&#xff0c;如门诊监测、居家康复、跌倒检测等。…