etcdv3.6源码流程---compact

总括:

因为bbolt数据库的内容中:key=(revision,version),value=(elemkey,elemvalue),treeIndex中key=elemkey,value=[]generation,而generation包含一个key的所有版本,也就是说treeIndex中的value就是bbolt中的key。因为treeIndex中包含所有的elemkey,所以压缩的思路就是先遍历整个treeIndex来进行压缩:没有被删除的elemkey就用最后一次写入的值来覆盖,然后把该elemkey对应的(revision,version)作为key保存到一个map keep中,被删除的key则不会保存到该map keep中,然后遍历数据库中所有key,如果该key即(revision,version)不在map keep中,那么就从数据库中删除该key,如果在,则跳过,然后就完成了整个压缩操作。总结就是compact一共两步,第一步压缩treeIndex,第二步压缩数据库。

一点杂记:

1:压缩treeIndex和压缩数据库是两个分开的操作。
2:除了compact(defrag操作不知道)操作,所有其他的修改操作比如put/delete都是一个只增不减的过程。
3:put/delete操作也分两步,这两步也是分开的。第一步是往treeIndex中增加数据。第二步是把事务提交到txnbuffer。
4:compact对数据库的修改和put/delete一样,都是把操作丢到batchTxnBuffere就认为成功了
5:读写可以并发,即一边读数据库,一边把修改操作丢到batchTxnBuffere,但是在提交batchTxnBuffere之前会等待所有的读完成之后才会继续提交

debug流程:

1:启动参数添加--auto-compaction-retention=1
2:修改代码,把等待间隔缩小,否则有些流程可能不会走到或者要隔很久才会走到,因为他内部单位是小时

流程:

他是单独开了一个线程每隔一段时间就compact一下


etcdserver.NewServer #创建一个etcdserver
  if num := cfg.AutoCompactionRetention; num != 0:            #需要配置auto-compaction-retention才会开启compact,默认是不开启
    v3compactor.New #创建一个compact对象
    v3compactor.Periodic.Run                                   #启动compact线程,内部一个死循环,每隔一段时间就执行一次compaction
      for{ #定时循环
        sleep
        mvcc.readView.Rev                                      #获取compact开始时etcd系统当前最新的版本号即currentRev,保存为变量compactRev
                                                               #currentRev+1就是下一个待提交的版本号
                                                               #不知道为什么他还要专门创建一个readTx,这么麻烦,为什么不直接读这个字段就行了?
        etcdserver.EtcdServer.Compact
          etcdserver.EtcdServer.processInternalRaftRequestOnce #因为compact操作会改变集群状态,所以要走一遍raft流程,
                                                               #记录一条compact日志到wal日志,然后etcdserver apply这条日志
                                                               #因为改变数据库的操作有很多,比如delete、put、compact,
                                                               #所以etcdserver apply的时候会根据data字段中请求的类型来进行一个dispatch操作

          # compact的raft流程同put,因为走raft流程时,raft是不关心日志内容的,
          # 所以put、delete、compact等都用的同一套流程,同一套代码,
          # 只有在apply的时候需要根据日志类型来进行一个dispatch操作,所以这里就直接跳到apply了
          ......raft 流程,与put一模一样, 略......                                                                        ,
          return
      }
      another thread 1{                                              #就是etcdserver进行apply的流程,只不过dispatch时是compact
        etcdserver.EtcdServer.run
            for
              select:
                case ap <- applyc
                  ......略去了一大串apply调用,直接来到dispatch......
                  apply.uberApplier.dispatch                         #根据req类型来调用对应的方法
                    case r.Compaction:                               #如果这条日志是一个compact请求
                      apply.applyV3.Compaction
                        apply.applierV3backend.Compaction
                          mvcc.store.Compact
    	                    s.mu.Lock()                              #源码注释中说对于事务加读锁,对于非事务如compact/defrag则加写锁
                                                                     #还没完全搞懂  
                                                                   
                            mvcc.store.checkPrevCompactionCompleted  #检查上一次compact操作是否完成,etcd compact的时候会往数据库里写一些数据,
                                                                     #所以compact之前读一下这些数据,即读一下scheduledCompactRev和finishedCompactRev,
                                                                     #如果读到的这两个值相同说明上一次compact操作已经完成,可再次compact
                                                                     #scheduledCompactRev表示已经调度的最新的compact任务对应的revision
                                                                     #finishedCompactRev表示已完成的最新的compact任务对应的revision
                                                                     #因为他是fifo调度,因为先触发的copmact任务的revision必定小于后出发的revision                                                      
                                                                     #所以只需要判断这两个值是否相等就可以判断之前的compact任务是否完成了

                            mvcc.store.updateCompactRev              #往bbolt数据库写入本次compact对应的版本号,即写入scheduedCompactRev=本次compactRev
                            s.mu.Unlock                              #释放数据库写锁

                            mvcc.store.compact                       #执行压缩
                              schedule.NewJob                        #compact是作为job异步执行的,所以有可能上次还没结束就又来了一个新的compact请求
                                mvcc.store.scheduleCompaction        #1:先从treeIndex中删除,然后再遍历数据库中所有key,
                                                                     #删除所有不在压缩后的treeIndex中的(revision,version)
                                                                     #删除范围为revision属于1~compactRev的版本

                                  mvcc.treeIndex.Compact             #treeIndex包含所有elemKey,所以先遍历treeIndex的所有elemkey,
                                                                     #针对每个elemKey,删除他所有在compactRev之前的版本
                                                                     #来得到最终剩下的所有(revision,version)
                                                                     #如果key所有版本都没了,那就从treeIndex删除这个key

                                    mvcc.keyIndex.compact            #合并elemkey的generation,
                                                                     #如果key没被删除则保留最后一个(revision,version),如果被删除则删除
                                    btree.BTreeG[T].Delete           #如果该key被删除了,就从treeIndex中删除这个key

                                  for{                               #压缩完treeindex就开始压缩数据库,一次删除最多删除n个数据
                                                                     #!!!一次事务删除多少条数据也是可以配置的
                                                                     #他就是一个for循环,一批一批的删,当删完1批,释放锁,开始删下一批之前
                                                                     #这个时候是可以commit put/delete事务和rage的,个人猜测是避免一次性删除太多
                                                                     #导致put/delete长时间阻塞(个人猜测,不确定)

                                    backend.batchTx.LockOutsideApply #compact数据库之前禁止继续提交事务(主要是put/delete)
                                                                     #compact操作和其他put/delete一样,都会竞争这个batchTxnBuffer
                                                                     #此时treeIndex是没有锁的,所以put/delete/range是可以正常读取和修改treeIndex
                                                                  
                                      backend.batchTx.lock           #apply的时候会修改batchTxBuffer,会先申请batchTx锁
                                                                     #compact的时候会锁住buffer,从而禁止compact期间提交apply事务

                                    backend.batchTx.UnsafeRange      #读出所有key即(revision,version),因为此时已经获取了事务锁,
                                                                     #不用担心其他人修改,所以unsafe读
                                    if key not in keep               #如果该key不在合并后的treeIndex中,那么就从数据库中删除该key
                                      batchTxBuffered.UnsafeDelete   
                                   
                                    if cur<batchNum :                #如果本次处理的小于batch大小,说明所有的都删除了,可以结束本次compact
                                      mvcc.UnsafeSetFinishedCompact  #往数据库写一条数据即finishedCompactRev=compactRev,标记本次compact已经完成
                                      backend.batchTx.Unlock         #解锁
                                      return                         #结束本次compact。
                                                                     #!!此处没有forceCommite,也就是最后这一批对应的事务没有强制提交就返回了
                                                                     #!!因为和put/delete一样,compact把数据删除操作丢到txnbuffer就不管了
                                                                     #这个txnbuffer同一时刻只能有一个事务修改
                                                                     #由另一个线程定时commited。总的来说:txnbuffer就是一个缓存,任何事务修改操作,
                                                                     #只要把操作丢到这个txnbuffer后,该事务就可以认为该操作成功了,就可以直接返回了
                                                                     #备注:batchTxnBuffered和batchTx以及bbolt事务之间的关系我还云里雾里,还不懂
                                                                     #只知道batchTxnBuffered里有一个batchTx,然后所有操作都是转发给batchTx
                                                                     #笔记:写事务在开始之前会调用s.mu.RLock加读锁,然后把操作丢到txnbuffere的时候
                                                                     #对txnbuffer加锁,然后返回,然后释放s.mu.RUnlock,
                                                                     #这样从加读锁到释放读锁就标记着一个事务的完成,如果加了读锁,但是还没有释放读锁
                                                                     #说明数据库此时还有事务没有完成,在开始一次新的compact之前会对数据库加写锁
                                                                     #直到所有已有的事务完成才能成功加写锁,所以他就是用这个s.mu来判断是否有事务还没有完成
                                                                     #这个s.mu只有在triggersnapshot/compact中才会加写锁,其他地方都是加读锁(defrag不知道)
                                                                    

                                    else:                            #如果达到了CompactionBatchLimit,则立即提交本次事务,
                                                                     #因为配置文件中配置了允许单次事务compact的最大条数
                                      backend.backend.ForceCommit    
                                        backend.batchTxBuffered.Commit
                                          backend.batchTx.lock
                                            backend.batchTxBuffered.commit
                                              backend.readTx.Lock     #因为我们前面创建读事务的时候会给readTx加读锁,所有这里获取写锁会阻塞
                                                                      #直到所有读事务都完成,释放读锁后,这里才可以成功加写锁
                                                                      #前面已经调用LockOutsideApply禁止了其他写事务提交,所以这里不用担心并发写
                                                                      #读事务会先访问treeIndex然后才去数据库读,treeIndex内部会加锁,所以是并发安全的
                                                                      #compact是先压缩treeIndex,然后再去压缩数据库,如果压缩完treeIndex之后
                                                                      #在compact提交写事务前,来了读事务,那么读事务会先获取treeIndex,然后再去读数据库
                                                                      #compact会因获取不到readTx的写锁而阻塞在这里,
                                                                      #所以在compact提交前和提交后都可以安全的读,但是compact中不可以
                                          backend.batchTx.unlock

                                  }

      }

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

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

相关文章

UDP 的报文结构

一.UDP的报文结构 1.UDP的简单介绍 UDP是传输层协议&#xff0c;它是无连接,不可靠传输,面向数据报,全双工 1.无连接&#xff1a;UDP是一种无连接的传输协议&#xff0c;通信双方不需要在发送数据之前建立连接。相比之下&#xff0c;TCP是面向连接的协议&#xff0c;在传输数…

【除了协程还有哪些方式可以实现异步编程】

在Unity中&#xff0c;除了使用协程实现异步编程外&#xff0c;还有以下几种方法&#xff1a; 异步加载资源&#xff1a; 使用UnityWebRequest类进行异步加载资源&#xff0c;这在加载网络资源或动态加载资源时非常有用。 using UnityEngine; using UnityEngine.Networking;…

【Linux】进程控制 之 进程创建 进程终止 进程等待 进程替换

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前正在学习c和算法 ✈️专栏&#xff1a;Linux &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章有啥瑕疵&#xff0c;希望大佬指点一二 如果文章对…

每日一博 - 闲聊架构设计中的多级缓存设计

文章目录 方法论概述客户端缓存应用层缓存服务层缓存缓存设计的注意事项总结 思维导图戳这里 方法论概述 从客户端到服务层&#xff0c;缓存的应用广泛而重要。通过合理的缓存设计&#xff0c;能够有效地提高系统的性能并降低延迟。 客户端缓存 在客户端层面&#xff0c;浏览…

LLM2Vec介绍和将Llama 3转换为嵌入模型代码示例

嵌入模型是大型语言模型检索增强生成(RAG)的关键组成部分。它们对知识库和用户编写的查询进行编码。 使用与LLM相同领域的训练或微调的嵌入模型可以显著改进RAG系统。然而&#xff0c;寻找或训练这样的嵌入模型往往是一项困难的任务&#xff0c;因为领域内的数据通常是稀缺的。…

基于AT89C51单片机的温度上下限自动控制检报警设计

点击链接获取Keil源码与Project Backups仿真图: https://download.csdn.net/download/qq_64505944/89247694?spm=1001.2014.3001.5501 C 源码+仿真图+毕业设计+实物制作步骤+06 题 目 基于单片机的温度检测调节系统设计 姓 名 学 号 专业班级 指导教师 年 月 日 任务书 …

Nginx 从入门到实践(2)——Rewrite重写

Nginx Rewrite Rewrite重写 Nginx Rewriteurl组成说明Rewrite基本概述Rewrite使⽤场景rewrite优点 Rewrite配置语法location匹配概述 if指令if 判断指令语法nginx以及if 判断可使用的全局变量 set命令return指令 url组成说明 https://cn.bing.com/search?qNginxRewrite&P…

udp/tcp回显网络编程

udp DatagramSocket 用于接收和发送udp数据报 构造方法&#xff1a; DatagramSocket():创建一个UDP数据报套接字的Socket&#xff0c;绑定到本地上 一个随机可用端口上&#xff0c;一般用于客户端DatagramSocket(int port):创建一个UDP数据报套接字的Socket&#xff0c;绑定到…

Proxmox VE 8 用SDN隔离用户网络

作者&#xff1a;田逸&#xff08;formyz&#xff09; 最新发布的Proxmox VE&#xff08;以下简称PVE&#xff09; 8在Web管理后台集成了易于操作的SDN&#xff08;软件定义网络&#xff09;功能插件&#xff0c;其实质是对不同的PVE用户指定不同的网络&#xff0c;进行逻辑隔离…

将要上市的自动驾驶新书《自动驾驶系统开发》中摘录各章片段 4

第十三章 车联网 数字化设备正变得越来越普遍并且相互联系。这些设备向数字生态系统智能部分的演进创造了迄今为止尚未解决安全问题的新颖应用。一个特定的例子是车辆&#xff0c;随着车辆从简单的交通方式发展到具有新的感知和通讯功能的智能实体&#xff0c;就成为智能城市的…

屏蔽罩材质和厚度对屏蔽效能的影响

​ 一&#xff0e;屏蔽效能的影响因素 屏蔽效能的影响因素主要有两个方面&#xff1a;屏蔽材料的特性和厚度&#xff1b;如下图所示&#xff0c;电磁波经过不同媒介时&#xff0c;会在分界面形成反射&#xff0c;穿过界面的电磁波一部分被反射回去&#xff0c;这部分能量损失…

偶然发现了Python的一个BUG。。。

一般情况下&#xff0c;dict(id1, **{id: 1})这句代码应该报TypeError。但如果在捕获了其他异常的情况下&#xff0c;再来执行这句代码&#xff0c;却是会报KeyError&#xff0c;如下图&#xff1a; Python3.10和Python3.9也能复现该情况&#xff0c;正当我摩拳踩掌&#xff0c…

百度下拉框负面信息如何删除?

百度头条360等搜索引擎&#xff0c;作为人们获取信息的主要途径之一。然而&#xff0c;一些知名的企业或个人可能会面临在搜索的下拉框中出现负面信息的问题&#xff0c;这可能对其声誉和形象造成不良影响。小马识途营销顾问根据自身从业经验&#xff0c;针对这类情况提出以下建…

【精品毕设推荐】基于JSP物流信息网的设计与实现

点击免费下载原文及代码、PPT 摘要 本文讲述了基于JSP物流信息网的设计与实现。该系统使用java语言开发&#xff0c;使系统具有更好的平台性和可扩展性。 该系统实现了用户登录、注册、查询快递信息、快递公司注册成为合作伙伴以及系统管理员对信息进行管理等功能。系统的主…

LeetCode 234.回文链表

题目描述 给你一个单链表的头节点 head &#xff0c;请你判断该链表是否为 回文链表 。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,2,1] 输出&#xff1a;true示例 2&#xff1a; 输入&#xff…

解决windows中的WSL Ubuntu子系统忘记root密码和用户密码问题

1、以管理员身份运行PowerShell 2、在powershell中执行wsl.exe --user root wsl.exe --user root如果出现了上面的报错&#xff0c;则需要运行步骤3、4&#xff0c;然后在执行步骤5改密码&#xff0c;如果没有出错&#xff0c;请直接跳到第5步改密码操作&#xff01;&#xff…

一分钱不花从HTTP升级到HTTPS

HTTP升级到HTTPS是一个涉及安全性和技术实施的过程&#xff0c;主要目的是为了提升网站数据传输的安全性&#xff0c;防止数据被窃取或篡改。以下是一些关于从HTTP升级到HTTPS的技术性要点和步骤概述&#xff0c;结合上述信息资源&#xff1a; 一、理解HTTPS的重要性 HTTPS (…

微信IDE vscode插件:获取插件位置,并打开文件

背景 有没有觉得在微信开发工具里面添加一些插件可以很方便。因为微信IDE的编辑本身是依赖vscode开发&#xff0c;所以编写vscode插件自然可以在微信IDE使用。这样做好处就是可以满足到自己一些开发使用习惯。 1.获取插件的目录位置 那么如何获取插件里面的目录&#xff0c;…

【精】hadoop、HIVE大数据从0到1部署及应用实战

目录 基本概念 Hadoop生态 HIVE hdfs(hadoop成员) yarn(hadoop成员) MapReduce(hadoop成员) spark flink storm HBase kafka ES 实战 安装并配置hadoop 环境准备 准备虚拟机 安装ssh并设置免密登录 安装jdk 安装、配置并启动hadoop 添加hadoop环境变量&…

STM32F1之FLASH闪存

目录 1. 简介 2. 闪存模块组织 3. FLASH基本结构 4. FLASH解锁 5. 使用指针访问存储器 6. 程序存储器全擦除 7. 程序存储器页擦除 8. 程序存储器编程 9. 选项字节 1. 简介 STM32F1系列的FLASH包含程序存储器、系统存储器和选项字节三个部分&#xff0c;通过…
最新文章