00:00
好,我们来上课啊。呃,还是先回顾一下我们昨天讲的东西啊。昨天我们把ES中啊,最后的两张内容给他,这个说完了啊,这个第五章分布式读写原理,还有这个第六章就是我们讲一下这个API操作啊。好,再把这个简单过一下啊,在这个分布式读写原理里面啊,首先。前两个啊,是基于这个ID的这个读和写啊,嗯,这两个应该比较简单啊,我们先说这个基于ID的这个写啊。呃,首先要明确的是,我们写数据呢,只能是往这个主分片上面去写。对吧,所以你这个写请求过来以后呢,他只能去找这个主分片。然后呢,等这个主分片写完以后呢,才能够复制到这个相关的这个副本这个分片上面。啊,先把这个明确一下啊,然后那这个请求到底是怎么处理的呢?呃,那你的客户端比如说发送了一个请求啊,这个请求比如说我们发给了这个NOTE1了。
01:01
那这个时候我们的NOTE1呢,就是一个协调节点了。啊,它会变成一个协调节点啊,就接受这个客户端请求的那个节点啊,就是一个协调节点啊,然后接下来呢,呃,他会去算一下啊,就是你当前要写的这个。文档啊,当他要写的这个数据,它不是有ID吗?反正我们都基于ID的写,对吧,他为什么按照ID呢去做计算,怎么算呢?呃,其实就是对于你的,对于你的什么这个分片的个数啊,对这个什么Sha的个数啊,做一个什么呀,取操作,那取出来是几,我就要把数据呢,写到哪个Sha的上面。好,那假如说呢啊,我们这个取出来是这个零吧,啊,那我就让你们写到这个零号这个分区上面,那这个时候我看一下你零号分区的这个主分面呢,是在这个NOTE3上面的。OK,那这个时候我就把请求呢,就打给这个NOTE3了,他就开始写,那等他写完以后呢,他再把请求呢,给到这个NOTE2和这个NOTE1啊,因为他们上面都有一个副本。对吧,然后等他们也都写完以后呢,这个NOTE3呢,就会把写完的结果呢,再通知给什么,通知给这个协调节点,比如说我们NOTE1好,那这个时候协调节点呢,再跟这个客户端说一声啊,说你这个写完了。
02:09
对吧,就是这样的一个过程啊,这个比较简单啊,那这里面的两个关键点,一个就是写必须在主分面完成啊,这你要记住,再一个就是它怎么去确定。我的一个文档要写到哪一个分片上面啊,它是有这么一个公式的。啊,就是对你的这个。呃,ID啊,做一个什么哈希操作,然后呢,对在对什么呀,对这个。分片的个数呢,做一个取域操作啊,其实重点是这个取域啊,能明白吧,因为取域操作我就可以,呃决定啊在你的哪个分片上面去写,那么至于前面这个计算,呃,你就知道他这个核心的内容是基于这个ID来算的就完事了,你甭管他做了到。甭管他到底做了什么计算对吧,反正是基于你的这个ID来算的啊,就完事了好吧。想呃,这里面还问了一个问题,说这个如果说Sha的这个数量呢,这个变化了,那是是不是要这个重新这个re哈C啊呃,这个是不会的啊,因为我们的这个Sha是不可能变的,就你这个,所以创建好以后,那你这个沙子的个数呢,就不让你变了。
03:12
明白吧,啊,所以这个不用担心的啊好,下面是读啊,读的话呢,呃,跟这个写差不多啊。然后这里面有两个关键点,一个就是读的话呢,既可以从这个主分面读啊,也可以从这个副本去读。明白吧,啊,副本也读也可以啊,好,那么还是一样的,你的客户端呢,会发送一个读请求啊,那这个请求发给谁,谁就是那个协调节点,比如说我的请求还是发给他了啊。它就是一个协调节点啊,然后接下来呢,还是啊,因为你读的时候也带了ID啊,那我就按照你的ID呢,啊,还是对什么对这个杀的数呢,做一个取操作,对吧?假如说呢,我们取出来又是个零啊,那这个时候呃,我们就要从你的零号分片,然后去读数据,那这时候我一看啊,我的NOTE1上面有NOTE2也有,NOTE3也有啊,因为你这个主副都能读嘛。
04:02
对吧,好,这个时候他就要决定,我到底把这个请求打给他,还是打给他,还是打给他。对吧,大家想一下啊,如果说我把请求打给他啊,或者什么都打给他,或者什么都打给他,都给都打给他,这肯定是不合适的啊,那么我们尽可能希望能够他们几个呢?诶相对比较什么均衡的去复载一些这个读的请求啊,那这个他在底层是怎么做的呢?他会通过一个轮询的方式来去做啊,就他会记录一下,就是我上一次我是给谁了,那下一次我应该给谁,下一次我应该给谁,就是把他们轮流起来。啊,基本上就是每个人呢,都去接受一个请求,这样的话呢,他们相对来讲啊,接收的请求呢,就是比较均衡的。好吧,这要知道的啊,行,这是两个关键点啊。那最后读完以后呢,也是啊,把这个结果呢,返回到这个协调节点,这个协调节点呢,再把结果呢,返回给这个客户端啊,所以这两个还是比较简单的啊行。呃,第三个是一个搜索的流程啊,这个稍微会复杂一点点啊,因为它分成了两步来去操作。
05:03
啊,首先呢,第一步啊,就是广播,我们把你这个。呃,所要查询的东西呢,作为一个广播,广播到这个每一个分片。然后呢,每个分片呢,在这个本地呢,进行这个搜索啊,并构建一个匹配文档大小的这个from加size一个什么优先队列啊,就说白了,我在我的本地,我要给你构建一个队列,我要从哪个地方去读,然后呢,读多少数据对吧,把这个队列给你什么构建出来。能明白吧,好,那我的每一个分片呢,都构建一个队列啊,最后呢,这些个分片呢,把这个他们所构建好的队列呢,统一在。返回到什么这个协调节点上面。理解吧,那这个协调节点干嘛呢?他会把你们这个所有的这个什么优先队列呢,诶整体去组合成一个,然后呢,再从中挑出来,最终我们想要的数据。对吧,那这个时候呢,其实我就有ID了啊,我就知道我要读哪些数据了,我的每条数据的ID是什么了,那接下来我就按照这个ID呢,再次做一个什么读取操作,就说白了啊,我再告诉他来,你把你里面的那那那些数据给我返还回来啊,你把你里面的什么什么数据给我什么调出来。
06:06
对吧,就是第一个阶段呢,就是一个query啊,就是我查一下我想要哪些数据,第二个是什么取回阶段啊,就是把数据呢,再给他什么取回来。好吧,它是分成两个阶段的啊,这个大家是需要这个知道的啊。OK。然后下个话题是文档的修改和并发控制啊,这个其实我们主要就是提到了。呃,我们在写数据的时候怎么去。处理这个并发血啊带来的问题。啊,其实就是两种方式吧,一种是这个悲观锁,一种是这个所谓的乐观锁啊,那这个悲观锁的话,一般在我们的传统的这个关系型数据库里面用的比较多啊,像这个mysle啊,Oracle啊,他们用的是比较多的,就是我在读数据的时候呢,就是我在操作这个数据的时候啊,那我要把这个数据呢,先跟大家怎么说起来。熟悉来以后呢,别人是不可能操作的啊,等我这个操作完成以后呢,别人才可能过来操作。对吧,这样的话呢,我就可以什么把你们这个并发的这个操作呢,都什么排成队去执行,同一时刻只有一个人再去执行啊,这个问题就得到解决了。
07:08
好,那这个乐观并发控制是什么意思呢?它是不加锁啊,不加锁就是每个人呢,都可以去读,都可以去写,但是呢,我在写之前的话呢,要做一个事儿,什么事呢?就是判断一下我手里面的那个版本跟人家这个数据库里面那个版本是否是一致的,如果说是一致的,我才能够写成功,对吧,如果说是不一致的,那我就会放弃这一次的写操作,就说明在我这个写之前,已经有一个人去把这个数据做了更改了,那我就不能再去做更改了。啊,它其实就是能通过一个版本号的这个方式啊,来进行这个管理的啊,进行这个乐观并发控制的。好吧,行,那这两个整体来看的话呢,呃,各有各的好处,各有各的坏处啊。这个悲观悲观并发控制的话,它是很安全啊,但是它这个性能呢,会稍微低一些,因为你这个所有的这个请求呢,都是排成队去执行的,就哪怕说我后面都是读请求,就你前面一个写请求,那也得什么,等着我把这个数据都写完以后,你才能够去读。
08:05
但其实我们发现读是不会影响你的写的。对吧,啊,这是它的一个不好的地方啊,那好的地方就是它绝绝对安全啊,足够安全。能听懂吧,而且的话呢,如果说我们真有什么多个写操作的话呢,他们都是能够成功的啊,就是只不过是第一个写完以后呢,来第二个再去写,第三个再去写,第四个再去写,第五个再去写,只不过他们需要什么等着去执行啊,那到了这个乐观并发控制里面的话呢,它的好处就是我在写的时候呢,别人读是没有任何问题的。对吧,就读的请求呢,不需要什么被这个写的请求所阻塞。啊,但是不好的地方就是,假如说我有同时有什么多个写请求,那么其实他们如果同时并发的话呢,只有一个是能够成功的。对吧,假如说他们都拿到了这个相同的版本号,那么其中一个把这个版本的从零改成一了,他们手里面都是零,那么他们再去改的时候,发现我的零跟人家的一不一样,对吧?我的零呢跟人家一也不一样,我的零呢跟他的一也不一样,我的零呢跟他的一也不一样,这个时候他们都会放弃这一次修改操作。
09:03
明白了吧,啊,这是一个,呃,也不叫一个问题吧,它就是这样的一种机制啊,那你放弃了这个修改操作以后呢,如果你在这个代码中来实现的话呢,首先我们肯定会给你的代码中的反馈一个结果,比如说你这个修改失败了,或者说什么抛出异常了啊,那你要什么尝试着重新再次什么进行一次一个修改,要不你这个操作他就什么不做了。明白吧,啊,这就看你怎么去设计了啊,其实也不叫问题吧,就是人家的这个机制就是就是这样子的。啊。行啊,这是文档的这个修改和并发控制啊。下面是这个删除方式啊,删除方式的话,你就记住它不是直接做这个物理删除啊,而是什么先做逻辑删除就先打标记。那么等到我们最终这个引发断合并的时候,才会什么做这个物理删除啊,释放这个存储的空间。OK吧,啊是这样子的啊,这这里面就提到一个概念,叫这个段合并啊,那我们就需要说一下什么断是怎么来的啊,那你要说断的话呢,你就得先去说一下这个煞的。
10:03
对吧,因为这个段是属于这个沙的里面的啊好,这个沙的话呢,我们,呃,主要研究是这么几个话题啊,就是你这个沙的个数问题。啊,我将来怎么去规划这个啥的,我得每个索引,我要怎么规划几个这个啥。啊,那首先我们能够知道的是这个煞的呀,它并不是越多越好啊,当然也不是越少越好,它一定是要什么控制到一个这个合理的范围内的。明白吧,啊行,那如果他太太多的话呢,会带来什么问题啊,太多的话带来的问题就是你维护的数据量可能没有那么多,对吧,但是呢,你这个分呢很多,那么就会什么呀。消耗更多的什么CPU和这个内存。是吧,这是一个问题啊。然后呢,呃,太多的话,还有另外一个问题,就是你太多了的话呢,你的很多个Sha呢,就会什么分到同一个这个节点上,那么同一个节点上这个多个Sha呢,如果说这个同时这个接受请求的话,那就会什么造成这个资源的一个竞争。
11:01
对吧,就是什么内耗了。好吧,啊内耗了啊,这个要指导一下啊,那同样道理,如果说我这个杀的太少的话呢,也不好啊,为什么呢?因为我们还是希望能够做到,就是尽可能什么通过这个负载啊,通过这个分布式做到这个负载均衡。对吧,如果说你这个杀的太少了,比如说我就一个,那我就什么只能放到我的一个节点上面,那我所有的这个读写请求呢,都得打给他。对吧,那你这个负担也上不去啊,说这个不能多也不能少啊,OK,那你说不能多不能少,那我们准备多少个合适呢?诶下面就是告诉你怎么去规划这个沙的数量啊呃,两方面,第一方面是根据这个每日的数据量来进行规划的,第二个方面呢,是根据这个内存来进行规划的。啊,那我们这个,呃,业内这个比较就是嗯,算是这个比较常用的一种方案啊,就是如果说我们单日数据量这个评估一下,是这个低于这个十个G的时候呢,那你肯定是什么一个分片就搞定了,如果说你这个比十个G多啊,但是呢,不超过这个30个G的情况下,也可以什么通过一个分片来搞定。
12:03
如果说再多的话呢,那你就要什么拆分了啊,就要什么就多个分片了。明白吧,啊,就是你要记大概记一下我的一个煞的,我大概这个能够维护多少数据啊,是比较合适的,它也是一个范围,他也不是一个什么精确的值啊好,这是一方面。然后再一方面就是从这个内存角度啊,因为我们知道这个每一个shade呢,就假如说这个一个十个G左右的这个shade。啊,一个十个G左右的这个Sha的,然后它大概会占用这个30~80兆的这个堆内存空间。啊,那如果说我知道了你占用多少内存空间,那我就会什么,根据我目前给你这个ES所规划的什么内存空间,内存大小,然后计算一下我当前这个节点能够支撑到多少个Sha的。对吧,啊,这些是可以去算的啊好。就从两方面啊,然后呢,去规划你这个沙子的数量啊OK,嗯。好。
13:00
然后呃,再往后吧,再往后是这个Sha的这个优化啊,就是我们知道的这个煞的太多呢,呃不好,太少也不好,那我们在这个合理规划以后呢,呃就能够正常使用,但是你这个长时间用下来以后呢,其实你这个沙的也会越来越多,越来越多,越来越多。对吧,越来越多啊,就对我这个整个ES的这个造成了这个非常大的一个压力。啊,因为这个数据量多了呀。对吧,所以这个时候我们就什么想着去优化啊,呃,优化的话两方面,第一方面什么从这个冷数据啊角度去优化,就比如说我们把这个时间比较长的的数据啊给它拿走,你就不要在我这个ES里面放着,因为我对对你这个查询的频率肯定很低了,对吧,比如说我们就什么把这个长期的一个明细呢,给它什么做一个聚合,对吧,聚合成一个结果啊,比如说我就算一个什么结果出来。啊,算一个汇总值出来,然后呢,存一下就完事了,然后呢,对于这个明细数据我就拿走了啊,你就看一下,如果说这个明细数据你在你这个离线中也有,那这个数据我觉得你可以删掉了。但如果说你这个离线里面没有你就这一份的话,那你最好是把这个数据呢,给它放到一个存储代价比较低的一个容器里面,比如说这个HDRS啊什么的,对吧,这样的话呢,你从ES拿走以后呢,诶它的这个。
14:11
这个压力就会比较小一点啊好,这是从这个冷数据啊,然后呢,再一个角度是就从你这个煞的内部去做这个优化啊呃。刚刚说到一个刹的这个就是十个G左右的这个Sha的啊,可能占用的是这个30到这个80的一个什么内存空间,那为什么跨度会这么大呢?其实就是因为它这个内部的段导致的。啊,如果说我的一个刹中段越多,那我占用的这个内存空间就越多,段越少,我占用的内存空间就越少。啊,那这个段到底是怎么来的呢?呃,这个段雅琪就是因为你在写数据的时候这个造成的啊。比如说。呃,我们中间自己画了个图啊,还是看那个图吧。好看一下啊,很简单,呃,你在这个写数据的时候呢,首先啊,你的数据呢,肯定是往内存中先写的,因为它是一个异步写入嘛,对吧,异步写入啊好,那先写到你的一个不可读的这个八分钟,这里面就放的就是你的一条一条的数据,然后呢,呃,经过这个一秒钟以后啊,然后它就会把这里面的数据呢,给你什么包装成一个段,然后呢,再写到一个可读的一个八分钟。
15:18
那段就是这么来产生的,好,那最终的话呢,经过这个,比如说这个30分钟了啊,或者说你这个translog达到这个512兆了,这个时候呢,我们会什么把数据呢,往你这个磁盘里面去写。那你落到磁盘里面以后呢,其实也是一个一个的断,明白吧,那这个时候你会发现啊,其实我的一个Sha里面,它会怎么包含很多个段。啊,但是你这个段的个数呢,我们昨天也强调过啊,跟你这个数据量大小呢,没有一个正比的关系啊,你不能说我的段越多,难道我的数据量就越大吗?不一定啊,那还得看你这个每个段中能有多少数据。对吧,但是这里面我们能够知道是你的段越多,那么你要消耗的内存是越多的。啊,所以说呢,我们就希望说能够把这个段呢,但是做一个正常的合并,对吧。
16:04
合并了,合并成什么一个段,那这样的话,我们占用的这个内存空间就会小一点啊,那怎么去合并这个段呢。你合并段的话呢,呃,这个ES呢,它也提供了就是。方案啊。对吧,就他这个官方的也会什么做这个段的这个合并啊,它是有这个方案的。明白它会在什么,在这个后台呢,做这个周期性的这个合并啊,但是呢,它这个门槛呢,设置的比较高哈,所以我们就没有去研究过它啊,那因此我们一般情况下,这个对于段子合并的话呢,我们通常都是。手动去优化的。啊,手动去优化的,比如说我们以天这个为单位做这个索引分割,那我今天这个索引用完以后啊,这个索引我今天用完了,用完以后,那我怎么抽一个合适的时间。对吧,抽一个这个合适的时间,然后就可以什么做一个短的合并啊,那你合并的话就什么执行一下这个操作就可以了,OK吧,执下这个操作啊就行了啊行,这个我们昨天也能够看到这个效果啊,就你合并完成以后呢,确实啊,他这个各方面这个所占的这个资源呢,就会降低一点。
17:10
啊,那肯定是有好处的啊,好吧,这也不多说了啊。呃,最后有一个这个关于这个master啊,这个其实没啥啊,就是告诉你这个master他主要干什么事儿啊,嗯,其实就是一个集群的一个管理者啊,他会负责这个集群的一个信息的这个维护啊,啊这个自己看一看吧啊。OK啊,再往后就是我们这个Java程序中的这个应用了啊,就是这个API应用啊,那我们呃,目前讲的是这个high level rest client。对吧,用的是它啊行,这里面我们主要讲的就是基本的这个正常改查。对吧。呃,基本的正常改吧,然后查的话呢,我们讲了,呃,有这个基本的普通的查询,还有什么条件的查询,还有什么这个聚合的查询。是吧啊,重点就是你这个写入数据啊和这个查询数据啊,这两块是一定要掌握的,像这个修改和删除的话呢,呃,你知道一下就行了啊,他也能做这个事儿啊,但其实一般情况下,呃,修改可能还会有一点点吧,但删除基本上是没有的啊。
18:15
你的数据进来以后,一般我们是不会做删的。理解吧啊,一般是不会做删的啊OK,行,那这个具体怎么写我就不再跟你说了啊,这就是你这个代码,你就下去以后多写吧。啊,多写多练,然后呢,把它熟悉起来啊。讲,呃,这是我们昨天讲的这个ES的内容啊,呃,这个完事以后,我们就直接又回到我们这个项目中了啊,我们回到项目中呢,我们去研究了一下,嗯,就是我们之前没有做完的东西啊。我们的日活宽表和这个。订单环表啊,我们最后只差这个写S是吧?啊,那我们这个分析了一下,这个怎么去呃写S啊。对不对?好,那我们总共做了哪几件事情呢?这个看代码来说吧,哈啊,日后宽表就在这。
19:01
其实就是规划一下,你要这个写入到这个ES中,你怎么去写啊,那我们肯定会用上这个索引的分割。对吧,那如果说你要用这个索引分割的话呢,呃,我们就要什么有这个模板来去控制我们这个每次建索引的时候,你的map,你的sing,你的别名等等一些。对吧,所以说我们肯定要什么去写一个模板啊,专门去什么针对于我这个日活呢,宽表,然后去写一个模板,然后让你这个每次见索引的时候呢,都能够用得上啊这样的话呢,你的字段呀,你的字段类型啊,我们都是可以什么自己控制的。好吧。行,呃,然后再一个就是你既然往往这个ES中写,那你不管是写这个日活也好啊,写这个什么,呃,这个订单也好。对吧,虽然说你写的数据不一样,你写入的这个index不一样,但是你的写入动作都是一样的,所以说我们就可以考虑把这个写入的这个过程呢,给它写成一个工具类。对吧,将来我们用的时候什么直接调这个工具类就可以了。是不是说昨天我们就什么写了一个工具类啊,然后呢,完成这个写入的写入的动作啊,行,就现在我们这个日活宽表的话,就已经把它写进去了啊,这个这就是写入ES的一个过程。
20:08
其实很简单啊,就把你的数据呢,呃,按照我们这个工具类中所封装好的哈,我们是要求你。哎,这个我昨天。这50天没提吗。这颜色怎么还还这样子的呀。这我昨天没提吗,这个同学们。啊忘了来,呃,就是按照我们这个工具类啊,工具类中的这个封装好的方法啊,比如说我要求你呢,给我传一个说你的名字传一个就是你要写入的数据啊,那因为我们做的是这个密登写啊说你的这个每条数据的,呃,这个doc ID你得给我传过来。理解吧,Doc ID要传过来啊,行,所以说我们就把我们数据呢,作了一个结构的转换啊,我们写的这个deal info啊,我们呃把它的这个m midd提出来,作为我们这个do ID去写啊,因为我们之前就是拿它做了驱重操作啊,那如果说呃,你到了这部这个环节,其实它已经没有什么重复数据了,因为我们这个red那一层呢,已经帮我们做了驱重了。
21:09
对吧,当然啊,如果说有遗漏的啊,有漏网之鱼,你到了这一层了,还有这个重复数据的情况下,好,那为什么直接通过midd作为什么do ID去写,那写到ES中呢,你就。只能是保留一条数据。对吧,啊,所以这个要知道的啊,行结构与转换,那就是正常往里面去写呗。是吧,啊,只能往里面去写就可以了啊OK,行,这是我们昨天做的啊,然后这个订单的话呢,让你们下去以后自己写一写,我觉得这个应该问题都不大吧。啊,这个稍后我们把这个写一写啊,这个很简单好了,呃,然后呢,还让大家这个去分析了一个问题啊,这个问题我不知道你有没有想过啊,就是。在这个日活宽表中啊,如果说我们这个mid中记录了这个red了,因为我们做了驱动操作嘛,对不对啊,驱动操作啊,就比如说啊,现在我们有一个设备,今天的这个第一次访问啊,然后假如说那个叫1001吧,对吧,他一看诶ready中没有,没有的话,代表我是一次访问,那我就把1001的写到这个red中了,是不是啊,那接下来呢,我的这一条数据呢,我要对应的往这个ES中去写了,但是呢,因为某些原因写失败了。
22:16
好,那这个写失败以后呢,注意啊,因为你写到red,再往这个ES中写,这两个事情呢,其实正常情况下,他们应该是一个原子性的,对吧,你写成功我也得写成功,如果说你写失败了,那就不用说了,如果说我写失败了,你写成功了,那你就得回滚一下,这样我才能够保持一致。但是目前我们并没有,并没有把它做成一个原子操作,就说白了,他可能写成功,但是他可能写失败了。那就导致了相当于什么呀,相当于我们认为诶1001这个人呢,已经有一次访问了,但实际上呢,在我ES中是没有这个人的,那么相当于你再去统计结果的时候,肯定就会出现问题。对吧,首先问你啊,这个会造成什么问题,然后应该如何解决。对吧,这个让大家这个昨天呢,自己去思考一下啊,这个稍后的话,我们也是把这个问题呢,再来去分析一下,然后一起把它解决一下,好吧,啊来这是我们呃,昨天啊给大家讲的内容啊,行,那就先说这么多啊。
我来说两句