前言

书接上文,大数据大陆上的“王权”之争都终于告一段落,“国王”、“领主”以及“议会”之间的关系终于尘埃落定。尘归尘,土归土,DATA都是二百五,现在是时候集中精力好好处理下DATA了。这个重任就落到了“领主”身上,“国王”以及“议会”(可能不存在)负责“指挥”以及“调度”。

命令到来,“领主”们表示鸭梨山大,因为他们见证过曾经的巨型帝国如“ORACLE”、“MYSQL”以及“POSTGRESQL”是怎么死于DATA的钢铁洪流的,就好像虎式坦克对战钢铁洪流T-34一样,双拳难敌四手,最终被活活拖死了。于是“国王”召集御前会议,讨论如何应敌。经过“领主”们七嘴八舌的一顿讨论,终于形成了几个方案,剩下的就是确定哪个方案被选中。一场轰轰烈烈的大决战即将拉开帷幕....

正文

先决条件

竟然要确定应敌方案,首先就要先摸清楚自己的家底,主要包含下面几种资源:

  • 固定数量存放DATA的仓库(硬盘)

  • 固定数量处理DATA的工人(线程)

  • 固定数量快速处理DATA的机械(内存)

  • 若干条运输DATA的道路(磁盘IO和网络IO)

    主要资源就这些,剩下就看怎么用了

战役目标

在上述资源下,能尽快且稳定的处理DATA,包括存储DATA,操作DATA以及查看DATA

具体方案

战役目标可以拆分成三部分:

  • 存储DATA:主要考虑存储DATA的速度以及拉DATA的车什么时候能离开

  • 查看DATA:主要考虑要支持DATA的快速查询,包含单个DATA的查询,以及多个DATA的顺序查询

  • 操作DATA:主要考虑修改以及删除DATA的速度,以及处理准确性

B公爵方案

B公爵提出了一个方案,主要内容如下:

存储DATA

B公爵的方案中存储DATA的方法:

  • 使用一种阵法,能将所有DATA存储起来,使用从上到下的结构(可以理解为上下级关系的组织架构图);

  • 在该结构中,每一层的DATA都是有序的且是全结构唯一的;

  • 一个或者几个DATA放在一个小格子里(节点),多个同级别的小格子达到一定大小后放在一个仓库(磁盘Page页的一部分)里,多个仓库组成一个区域(磁盘的Page页)。仓库之间保存了各个DATA之间的关系;

  • 每个小格子里面有N个DATA,该小格子就有N+1个“随从”盒子。每个“主人”都有两个“随从”,左面的“随从”比“主人”小;右边的随从比“主人”大;

  • 还有一个额外的条件,就是这个阵法必须保持整体的平衡,否则这个阵就会垮掉(多路平衡树)。

查看DATA

B公爵的方案中查询DATA的方法:

  • 从最上面的顶点开始查找,如果匹配,就直接返回结果了;

  • 如果不匹配,和顶点比较大小,大的话往右边找,小的话往左边找,直到最底层为止;

  • 如果最底层仍然没有找到匹配的,就说明不存在这个DATA。

操作DATA

B公爵的方案中操作DATA的方法:

  • 按照上面查找DATA的方法找到DATA后,对DATA进行放入(新增)、替换(修改)或者扔掉(删除)操作。

B公爵的方案看起来不错,参考上面的图理解下这个结构。这个方案比排列一排查找速度快了不少,尤其当DATA多的时候。但是这个方案是不是还有些可以优化的地方呢?大家可以想一想,这里先卖个关子。然后我们看看B公爵的哥哥B+公爵的方案。看看姜是老的辣还是长江后浪拍前浪前浪死在沙滩上。

B+公爵方案

B+公爵的方案其实和他的弟弟B公爵有点像,在B公爵的基础上做了点改造,那我们来一起看看效果,是时候表演真正的技术了。

存储DATA

B+公爵的方案中存储DATA的方法:

  • 使用一种阵法,能将所有DATA存储起来,使用从上到下的结构(可以理解为上下级关系的组织架构图),这点和B公爵的方案一致。

  • 在该结构中,除了最底层是真正的数据外,上层的都是目录(索引)。最底层的DATA都是有序的且是全结构唯一的。

  • 除了上下层之间的关系外,最底层真实DATA之间还有一个顺序的关联关系。

  • 一个或者几个DATA(目录也是一样的规则)放在一个小格子里(节点),多个同级别的小格子达到一定大小后放在一个仓库(磁盘的Page页的一部分)里,多个仓库组成一个区域(磁盘的Page页)。仓库之间保存了各个DATA或者目录之间的关系。

  • 每个小格子里面有N个DATA或者目录,下面就有N个目录仓库或者N个DATA仓库,可以认为是每一层和下一层都是一对多的关系。

  • 下层的仓库对应的上层的索引是该仓库中最小的或者最大的DATA或者目录。

  • 还有一个额外的条件,就是这个阵法必须保持整体的平衡,否则这个阵就会垮掉(多路平衡树)。

查看DATA

B+公爵的方案中查询DATA的方法:

  • 第一种和B公爵的一样,从上往下查找,由于B+公爵的方案所有真实的DATA都在最底层,所以每次的查找也必然会走到最底层;

  • 第二种是B+公爵独有的查询方法,就是如果一次顺序查询多个DATA,在最小的DATA找到后,顺着底层的顺序关系接着往下找就可以了,而在这种场景下B公爵的方案只能一个一个的从上往下找。

操作DATA

B+公爵的方案中操作DATA的方法:

  • 按照上面查找DATA的方法找到DATA后,对DATA进行放入(新增)、替换(修改)或者扔掉(删除)操作。这个和B公爵的方案是一样的。

这就是这B+公爵的方案,参考上面的图再理解下。现在是时候回答刚才卖关子的问题了,到底哪个方案更好些?现在看来显然是姜还是老的辣,B+公爵的方案是在B公爵的基础上优化得来的。优点主要集中在下面两点:

  • 由于B+公爵的方案中除了底层存储的是真实的DATA外,其余都是目录。查询的时候是需要将区域放入快速处理DATA的机械(内存)。但是这种机械能处理的容量是有限的。所以在这种情况下,如果单个单元的大小越小,那一次性放入到机械中的数量就越多,查到DATA的概率就越大,查询效率就越高。在这方面,B+公爵的方案显然更占优势,毕竟目录本身比DATA小很多。

  • 另一个原因就是查询多个数据时,当B+公爵的方案查到最小DATA后,顺着底层DATA之间关系直接去找其他DATA,而B公爵的方案只能一个接着一个的按部就班的查询。效率差别还是很大的

两个B(下面简称2B)树详解

故事暂时告一段落,先来总结一波,记一波笔记:上面的B+公爵的方案其实就是著名的B+树,而B公爵的方案其实就是著名的B树。B+树是在B树的基础上优化而来的。下面来总结一波,提炼下要点,结合上面的图让大家彻底明白B树和B+树的区别:

  • B树所有的节点都是数据节点,构成了一棵多路平衡树;而B+树除了最底下一层是数据节点外,其余的都是索引节点,除了主体是个多路平衡树外,底层的DATA节点构成了一个链表。

  • B树只有在根节点上有一个头指针供查询;B+树有两个头指针,除了根节点的头指针外,在底层最小的数据节点上还有一个头指针供查询。

  • 查询单个值时,B树的时间复杂度不稳定,可能根结点就是想要的结果,也可能到叶节点才能找到结果,所以时间复杂度最差为logN;而B+树由于前面都是索引,只能在最底层找到数据,所以时间复杂度稳定为logN。

  • 乍看起来好像B树占点优势,但是遍历树的时候需要将硬盘的数据加载到内存中进行查找。而内存是有限的,针对B+树来说,由于上层都是索引,所以大小比真实数据小很多,一次性加载到内存中的数据量也大,找到结果的概率也大。而在实际的环境中,数据树一般都是巨大的,所以快速站到数据的位置更多的是看内存中查找数据的速度,经过测试在这点上,B+树的表现往往比B树好。

  • 查询多个值时,B+树在找到最小的那个数据后,由于数据有序,顺着底层数据的链表往下直接找即可;而B树只能迭代一个一个的去找,效率显然不如B树。

上面总结了下B树和B+树的特点以及区别,作为下面内容开展的对比。其他知识点就先不展开了,不是本文的主要内容,大家感兴趣的可以去自学下。下面我们真正的主角就要登场了。

LSM公爵的方案

上面2B对决中B+赢了,各个国王舒了一口气,以为终于找到了对付DATA的方法。

但是不久后发现,这两个方案其实早已被各大关系型数据库大陆上的巨型帝国使用了。数据量不大的时候被证明是个很好的方案,但是数据量变大后,这个方案就束手无策了,虽然各大关系型数据库大陆上的巨型帝国都想办法优化了该方案并组织了一波抵抗,但仍旧无法抵消DATA快速增长带来的压力,被DATA的钢铁洪流平推了,这也是各大关系型数据库大陆上的巨型帝国在面对海量DATA时被“灭国”的原因。

各个国王沉默了,2B也一脸懵逼,所有领主不知所措....到底谁能力挽狂澜于既倒,扶之大厦于将倾?这时候我们的主角LSM公爵出现了。

此处先说一下2B的方案为什么不适合当前大数据中DATA的处理:

  • 对于DATA的存放,需要先找到DATA应该被存放在哪里,然后再放进去,这种为了维持结构平衡以及便于后续查询的处理方式带来的坏处就是DATA存放效率受到很大的影响。DATA数量小的时候问题不大,数量大的时候基本上就存放不过来了

  • 大数据大陆上各个领主的仓库(文件系统)为了支持大量处理数据,都加了一个约束条件,即存放了DATA后不允许进行替换了,只能顺着往后继续存放(append-only),所以在这种场景下两个B的方案也没办法落地。

下面看看LSM公爵的方案:注:由于原始的LSM公爵方案比较抽象,我们接下来讲解的是优化过后可以可以实际使用的方案。

存储DATA

LSM公爵的方案中存储DATA的方法:

  • 首先该方案将快速处理DATA的机械(内存)划分成两部分,一部分负责操作DATA(memtable),一部分负责查询DATA(block cache)

  • 为了安全起见,在DATA存放的时候除了在memtable中存放,也会在特殊的仓库中放一个备份(预写日志WAL),防止该处理站受到攻击(服务器宕机)或者处理站员工放假(节点下线)时DATA不会丢失

  • DATA最后还是会从快速处理DATA的机械中处理完毕后放到仓库(sstable)里,存放的过程中,为了方便后续查看DATA以及对不同的DATA进行不同的分类处理,DATA按照一定的规则以仓库为单位分多层存放。新来的DATA放在最上层,当DATA满足一定条件后,就会进到下一层,这个过程叫做合并(compact)

  • 所有的DATA在进入处理站后就会被排好顺序。而且除了memtable以及仓库最上层DATA外,所有的DATA都在所在的层级全层有序且全层唯一

  • 为了在仓库中能快速知道一个DATA是否存在,每个仓库还配备了一个目录(数据范围)以及一个校验器(Bloom Filter)。目录可以帮助确定要找的DATA可不可能在这个仓库内,不在的话,直接就略过;校验器则告诉你你要找的DATA到底在不在这个仓库内

查看DATA

LSM公爵的方案中查询DATA的方法:

  • 首先先去memtable中看看DATA是不是存在,如果存在则直接返回结果

  • 如果memtable没有,则会去block cache中查看DATA是否存在,如果存在直接返回结果

  • 如果上述两个地方都没有,则开始查看区域内各个层级的仓库中是否有DATA,如果存在就直接返回结果

  • 在每一层级查询DATA的方式是首先根据每个仓库的目录查看DATA可能存在于哪个仓库;找到这个仓库,然后使用校验器看看DATA是不是在这个仓库中。在的话直接返回结果即可,不在的话接着查找下一层级直到最后一级

  • 此处的校验器有一个特点,为了能快速的确认DATA是不是存在,所以他有一定的误差率,其特点是如果他说DATA不在仓库里,那DATA就肯定不在仓库里;但是如果他说DATA在仓库里,有很小的概率DATA不在仓库里。问题来了,结果不准确咋办?该咋办咋办,不予理睬即可,直接去拿DATA,根据结果去判断,如果DATA是空,那就意味着结果不存在,接着往下一级找即可。没有任何影响,只是多找了一次(多一次磁盘IO),但是瑕不掩瑜,这多一次的无用功带来的好处就是很多次DATA不存在是不用进来直接找DATA的无数次无用功。

  • 另外由于每个DATA可能不止一条数据(下面操作DATA中会详细介绍),所以获取DATA的时候默认会获取到最新的一条数据。

  • 最后,不管哪一级查询到了需要的DATA,该DATA都会被放入block cache中存放,方便下次来查找。由于block cache大小也是有限的,达到最大大小后,会根据规则进行DATA淘汰。

操作DATA

LSM公爵的方案中操作DATA的方法:

  • 上面说过了大数据大陆的仓库为了支持大量处理数据,都加了一个约束条件,即存放了DATA后不允许进行替换了,只能顺着往后继续存放(append-only),所以该方案所有的操作DATA的策略都和这个约束有关。

  • 首先,DATA被运来后,直接放在memtable(active memtable)中并被排好顺序,然后再把DATA备份到特殊的仓库中,拉DATA来的队伍就可以回去禀告处理成功了,剩下的工作就由处理站自己完成并保证DATA操作肯定成功。

  • memtable在被一部分工人不断的放入DATA后变得越来越大,到了一个设定的大小后,当前的memtable就不能再往里面写了,就要打包往仓库运了(immutable memtable),然后再拿一个新的memtable继续放新的DATA,保证处理DATA的流程不断

  • 还有一波工人在观察有没有打包好的memtable,有的话,直接运到仓库第一层(flush)。

  • 接下来每一层的管理员都在不断的观察自己这一层的DATA是不是需要往下一层放,并且处理上一层送下来的DATA,并将它们按规则合并完并放在各个仓库里(compact),然后更新各个仓库的目录以及校验器。

  • 最后说一下该方案怎么处理替换以及扔掉DATA的场景,该方案没办法像2B方案那样先找到DATA然后在进行操作,而是将替换以及扔掉DATA的操作变成两条存放DATA的操作。这样子数据操作就简化为存放操作了。最后在DATA合并(compact)的时候进行处理,将多条数据变成一条数据(修改)或者零条数据(删除)。

综上,LSM公爵的方案和2B方案相比,其实就是牺牲了部分读取DATA的性能,通过顺序的存放大大增加了存放DATA的性能。在大数据处理DATA的场景,显然处理DATA的速度和能力(吞吐量)更重要一些,否则DATA的钢铁洪流被把你碾压的连渣儿都不剩。所以在这个层面上,2B输的无话可说。

LSM树详解

下面在总结一波,作为文章的收尾:

LSM树(Log-Structured-Merge-Tree)的虽然被称为是一棵树,事实上,LSM树并不像B+树、红黑树一样是一颗严格的树状数据结构,它其实是一种类似于树状的分层存储结构。目前HBase,LevelDB,RocksDB这些NoSQL存储都是采用的LSM树。

LSM树的核心特点是利用顺序写来提高写性能,但因为分层(此处分层是指的分为内存和文件两部分)的设计会稍微降低读性能,但是通过牺牲小部分读性能换来高性能写,使得LSM树成为非常流行的存储结构。

LSM tree结构

下面详细说一下LSM tree的结构:

从上面的故事以及图中,可以看到LSM树有以下五个重要组成部分:

1.MemTable

MemTable是在内存中的数据结构,用于保存最近写入的数据(包含需要更新和删除的数据),MemTable会按照Key顺序地组织这些数据。

2.WAL

  • 因为数据暂时保存在内存中,内存并不是可靠存储,如果断电会丢失数据,因此通常会通过WAL(Write-ahead logging,预写式日志)的方式来保证数据的可靠性。

  • 数据在写入内存的同时也会写入到WAL中(默认异步写入),当数据在WAL中写入完成后,服务端即对客户端返回处理成功,客户端就可以去处理其他数据,剩下的工作以及数据安全的问题就由服务端来处理和保证。

  • WAL会在某些场景下进行老化和删除(如Immutable MemTable的flush以及WAL大小达到一定阈值后触发的flush),因为这部分数据已经安全落盘了,那这部分WAL也就失去存在的意义了。

  • 在某些场景为了极限提高写入性能以及吞吐量的场景下,尤其是对部分数据丢失不太敏感的业务场景,可以选择禁用WAL。这样做的好处是可以提高写入性能,但是缺点就是如果服务器宕机,那么在内存中的数据将会丢失。

3.Immutable MemTable

  • 当MemTable达到一定大小后,会转化成Immutable MemTable。Immutable MemTable是将转MemTable变为SSTable的一种中间状态。写操作由新的MemTable处理,在转存过程中不阻塞数据更新操作。

  • 当Immutable MemTable flush到硬盘上成为SSTable后,Immutable MemTable将被删除,而Immutable MemTable对应的WAL也一并会被删除掉。

4.SSTable(Sorted String Table)

  • 有序键值对集合,是LSM树在磁盘中的数据结构。为了加快SSTable的读取,可以通过建立key的索引以及布隆过滤器来加快key的查找。

  • SSTable在硬盘上分层存储,每层的数据都会在一定条件下被合并到下一层,这个过程被叫做compact,compact后之前的删除以及修改操作会真正执行。

  • SSTable第一层的每个文件都是文件内数据有序;而除第一层外每一层的数据都是全层有序且唯一。为什么是这样的?此处划重点:这就是flush以及compact的差别。flush仅仅是将memetable中的数据写到硬盘上成为SSTable;而compact是将所有的SSTable重新组织合并,那么全局有序就会在这个操作中完成;而刚才说了compact后之前的删除以及修改操作会真正执行,这也就保证了数据的全层唯一。

  • 另外SSTable的分层操作也带来了另外一个好处,那就是将数据按时间分类,针对不同时间的数据采用不同的压缩方式。针对大多数的查询场景,查询的数据基本上都是最新的数据,所以上面的几层数据可以不压缩;中间的几层使用压缩和解压缩性能以及占用资源比较均衡的算法来进行压缩,比如lz4;最后几层可以采用压缩比比较高的算法来进行压缩,比如zstd算法。而这些灵活设置的依据则是当前服务器的磁盘IO以及CPU负载的一个全面考量。

LSM树的缺点

上面说了一堆LSM树的优点,那么LSM树就这么完美,没有缺点吗?显然不是,LSM树同样有他的阿喀琉斯之踵。而且不止一个,有三个:

  • 读放大:顾名思义,就是1次读数据的时候真实的读取动作超过1次,从上面的讲述来看,一次读取可能会查询block cache、MemTable以及多层的SSTable,读放大可见一斑,而读放大直接影响到了读取数据的效率。

  • 写放大:顾名思义,就是1次写数据的时候真实的写数据动作超过1次,从上面来看,一条数据从写入MemTable后,然后flush到硬盘,然后数据每一层都至少compact一次,写放大十分严重。在大数据刚出现的时候,大家都是使用HDD(普通硬盘),这种情况下写放大带来的影响很有限,反而带来了许多其他的好处,如写入吞吐量的大增。但是随着一种叫做SSD(固态硬盘)的诞生以及普遍应用,事情发生了变化。由于SSD随机读的能力很强,能够提供大数据情况下的随机读取速度。因而开始慢慢的广泛地应用在了大数据场景中。但是SSD有个致命的弱点,那就是数据没办法覆写。只能按块擦除,然后重新写。但是SSD是有“寿命”的,即可擦写的次数有限,擦没了SSD就GG了。在这种场景下写放大带来的就是成本的提升以及系统稳定性的干扰了。

  • 存储放大:从上面LSM树的处理数据流程来看,由于修改以及删除数据并不是实时生效,而是通过compact的方式来生效。所以在compact之前LSM树是存在存储放大的情况。

有问题解决问题就得了呗,但是事情没那么简单。RWS(读放大、写放大以及存储放大)就和CAP之于数据库一样,在LSM树的场景下没办法同时解决。现在就看怎么取舍了。

由于LSM树模型读取没办法通过其他方式进行修改,所以读放大基本上很难避免,所以接下来就看怎么减少写放大或者存储放大了,而这件事情在LSM树中就是由compact的策略来调节的。

LSM树的compact

  • size-tiered 策略:每层设置存储为N个SSTable,超过N个的时候,就将此层的所有SSTable文件合并成一个SSTable并放到下一层,当最后一层的SSTable超过N个的时候,合并所有的SSTable文件然后放在最后一层。从上面描述可以看出,该策略的存储放大很严重,只有该层文件超过N个的时候触发compact时才能解决这个问题,而且最底层的SSTable文件也会变得很大。唯一的好处就是该策略的写放大不算太严重。然而该策略由于存储放大严重,且由于每层的数据不唯一且不是全局有序,所以会引起更严重的读放大,所以这种策略使用的比较少。

  • leveled策略:leveled会将每一层切分成多个大小相近的SSTable。这些SSTable在这一层是全局有序的(第一层除外),意味着一个key在每一层至多只有1条记录,不存在冗余记录。所以存储放大的问题得到了控制。但是这个策略的写放大的问题很严重,当上层向下层flush的时候,如果该SSTable中的key遍布于下面所有的SSTable,则这次合并则是全层的合并,而且可能不止一次,所以写放大的问题可见一斑。但是由于这个策略对读放大基本上没有加重,所以一般的LSM的compact策略都选的是这种方式,上面的LSM逻辑也是基于这个策略进行的讲解。

这里简单的说下compact的两种策略,就不展开了,大家后续如果感兴趣可以自学下。

另外LSM树在具体实现中都会有不一样的变化,于是就出现了很多LSM-like的结构。对于levelDB(RocksDB)以及HBase的实现都不一样,后续我会单独写文章来说明,大家敬请期待。

后记

我们常常对身边的变化固执的抵抗,比如技术的更迭以及某些技术的没落,毕竟那些技术的辉煌由我们一起见证过。但这种抵抗会让我们忽视新技术的魅力。物是人非的画面更容易激起大家的回忆。

但回忆可以怀念,却不能沉湎。万事向前看,拥抱变化才能拥抱未来。给自己制定一个新的目标,一个新的开始,毕竟“Nobody can return back to a new start,But everyone can start now and writing a new ending!”,与大家共勉。

最后,如果大家感觉笔者写的还不错,麻烦大家多多点赞和分享转发,也许你的朋友也喜欢。

最后挂个公众号二维码,多谢大家支持。

俗话说别在一棵树上吊死,那为什么那么多NOSQL都喜欢在LSM树上吊死呢?相关推荐

  1. Golang的GUI探讨:不要在一棵树上吊死,要在多棵树上

    原文: Golang的GUI探讨:不要在一棵树上吊死,要在多棵树上 • 腾图工作室,威远博客,威远工作室,Ease 关于Golang开发GUI界面始终是一个问题,似乎没有大家公认比较好的简单方法. 我 ...

  2. 架构设计:不要在一棵树上吊死

    "不要在一棵树上吊死" 大家不愿承认世界是混乱的.常常期望用单一的,简单的方法来解决现实问题. 怕遇到错综复杂的依赖关系,担心数据更新无法同步,额外的维护开销. 示例: 数据集市的 ...

  3. 刘强东:死掉的创业公司,几乎都违背这 4 点最基本的经济常识

    导读:从 2004 年做电商到今年已经快 12 年了,过去这么多年,有一点可以确定,就是未来不管是互联网怎么发展,出现什么样新的商业模式.新的概念,迄今为止没有任何一个人,没有任何一家公司和国家可以打 ...

  4. 那些在一个公司死磕到底的人,最后都怎么样了?在一棵树上吊死真的好吗?

    推荐阅读:前阿里P7架构师,分享多年工作心得and面试经验,祝你圆大厂梦 为面阿里P8,我肝了一份651个技术分支的脑图,要么?(限时领) 最近在知乎上看到一个话题 那些在一个公司死磕了5-10年的人 ...

  5. 失败至少不会在一棵树上吊死

        年轻人需要失败,只有失败才有更新的选择,也只有失败,才会让你有更多的思考,更多的想法,更多的尝试,     很多有成就的老板都告诉我,要坚持,坚持守着一个行业,你才能成功,     我知道老板 ...

  6. 与其不在同一棵树上吊死

    古人云:"三百六十行,行行出状元". 即使仅这点学不好, 请你不要放弃, 或许另一种发方法成就了你. 倘若你觉得什么都难, 更懒得去学, 我劝你乖乖的买两只羊, 找一个适合你的青青 ...

  7. Windows 令人“社死”的新功能,你都知道吗?

    相信每个人电脑上都或多或少都保存着一些秘密,或许是某个人的照片,或许是一段往事,也有可能是一些不可描述的学习资料. 而这些东西,想必都是不能被除了自己以外的人看到的. 此前,Windows 的搜索框功 ...

  8. kafka监听topic消费_分布式专题|最近一直死磕kafka设计原理,都肝吐了

    kafka架构图 kafka核心控制器 定义 在kafka集群中,会选举出一个broker作为控制器(controller),负责管理集群中所有的分区和副本的状态: 职责 监听broker变化,通过监 ...

  9. 研发提效还在死磕敏捷开发?他们都在用新工具了

    每一个做项目的软件企业都有一个产品梦,都希望能够通过在项目的实战中总结经验完成产品化,最终能够拿出一个完美的产品在行业内复制. 而项目交付延期.拖后腿的产品质量.业务沉淀靠人.研发人员流动性大等等这些 ...

最新文章

  1. 自动删除指定文件夹下N天前文件的批处理
  2. 网站SEO优化之如何维护网站权重?
  3. textbox matlab,matlab gui 编程文本框更新
  4. Pro*c源程序中使用宿主结构保存查询结果
  5. 微小宝公众号排行榜_榜单 广东省技工院校微信公众号排行榜(第51期)
  6. NGenerics算法库是学习的好代码
  7. java之歌_程序员之歌
  8. windows下cmd批量复制文件
  9. Exp4 恶意代码分析 20164323段钊阳
  10. SAP CO-PC物料标准价格更改方案
  11. cron 每隔3天_crontab实现每隔多少天执行一次脚本的两种方法
  12. 如何从网页获取原图片
  13. 银河麒麟服务器操作系统V10SP2搭建时间服务器
  14. 模式分解(2NF、3NF)
  15. 最优理论与技术--多目标规划问题
  16. 最诡异航空事件,幽灵航班包括驾驶人员,所有人都在高空中昏睡!而后整机坠毁!...
  17. 走进MSTP -- 1. EOS业务
  18. 拯救者r720黑苹果_拯救级手游语音,大象声科助力联想拯救者电竞手机
  19. c语言除法结果溢出怎么办,关于C ++:导致除法溢出错误(x86)
  20. 什么样的 GitHub 才适合放简历上?

热门文章

  1. 建模simulink - xpc调试手段
  2. 有符号数与无符号数的运算
  3. python二级操作题分值_计算机二级MS Office考试具体内容及分值
  4. Java Jsp+Servlet+mysql实现的火车票查询预定系统(管理员/普通用户 功能:火车票查询、订票付款退票改签、火车票信息管理、线路管理、站点管理等)
  5. kubeadm故障排除
  6. 2021美团杯CTF ez-sql
  7. Java代理服务器---Freedom_Server
  8. ShaderForge适用于unity 2018x、2019x、2020版本
  9. Ubuntu系统性能优化详细教程
  10. python 程序运行计时 动态_python中time库clock 使用Python,实现程序运行计时的数码管表示...