注: 此系列内容来自网络,未能查到原作者。感觉不错,在此分享。不排除有错误,可留言指正。
系统设计:避免"过度设计"和"过早优化"。
高性能(高并发):(缓存)多级缓存、复用对象(池化,减少GC)、批处理(打包传输/缓冲)、分片(写分流)、集群、主从、异步削峰、last-write win(高频同步逻辑,最后一个持久化)。
高可用:几个9衡量,99.99即一年53分钟不可用。集群、冗余备份,故障转移、有损服务,压测/过载保护/限流/熔断降级、服务化(隔离)、超时重试、幂等。
可伸缩:临时加机器增性能,不用时可以把机器撤掉。缓存服务器集群:一致性哈希算法(影响最小)。数据库集群:增加从库(读分流)
可扩展:模块化、组件化,高内聚低耦合,提高复用性扩展性。稳定接口:接口不变情况下,内部结构能随意变化;设计模式:面向对象原则,用设计模式设计代码;消息队列:模块化系统,用消息队列交互,模块之间的依赖解耦;分布式服务:公用模块服务化(中台),给其他系统使用,提高可重用性,扩展性;
动态CDN:永远都不命中缓存,每次都回源到机房。如几百个http请求经过动态CDN回源时做连接池,到源站就只有十几个http连接请求。传输也可以把内容压缩。边缘CDN回到核心机房,核心机房有两三个,选一个最佳链。
漏桶/令牌桶(限流算法):想自身系统不被打垮,用令牌桶;要保证他人系统不被打垮,用漏桶算法;
漏桶算法:请求先进到漏桶里,固定速度流出,流入速度过大会溢出(舍弃请求),强行限制数据的传输速率。
令牌桶算法:以恒定速度往桶放入令牌,当请求要被处理时,先从桶里拿一个令牌。当没令牌可取时,拒绝服务,允许某种程度的突发传输。
使用场景:令牌桶用来保护自身系统,对调用者进行限流,让自身不被打垮。当有流量突发时,处理速率能超过配置限制;漏桶用来保护他人系统,当三方系统没保护机制(流量限制)时,调用速度不能超过他的限制。因为不能改三方系统,只能主调方控制。因消费能力是第三方决定的,必须舍弃突发流量。
分段锁:map读写时要加锁防竞争,大map用Bucket数组hash打散,各个Bucket里的map有独立的锁,减少锁竞争开锁,减少阻塞等待时间。
消息队列:应用解耦、写流量削峰(解决数据流入速度和数据持久化落盘速度的差异)、数据分发(和应用解耦差不多意思)、分布式事务
就近部署:websocket服务可用户就近机房部署服务,可防网络链路太长、路由跳转次数多导致网络丢包、pingpong丢包断连问题。
多活架构:多活不是整个体系的多活,而是分成不同维度,不同重要性的多活。分为:GZone(全局只有一份)、CZone(RZone跨机房访问GZone延迟大要备份,CZone是GZone的备份)、RZone(三地五机房部署,折成5个分片,5个副本,各个机房有部分主数据)。kafka ISR同城双机房部署,链路短同步速度快、OSR异地机房部署,灾难时尽可能保留数据。
伴生容器/应用:又名sidecar,应用启动时再启一个用IPC做的`伴生容器/应用`,如service mesh、MHA应用。用来做监控/上报等非业务性功能。代码解耦,非侵入性。
用户ID和订单ID多维度查询:可以以用户ID做为前缀+时间+步长进行订单ID设计,分表时就不用再冗余多张订单表给后台查询,直接用用户Id来切分就行了。
消息批处理(缓冲区bufferCache):
定时定量结合来做批处理,定量是防在定时范围内收到太多消息导致本地消息积压,定时是防在范围内收到太少消息导致长时间停留。
单房间千万级ws在线推送,分布式ws里一个服务上也有过万条ws连接。当发送的消息过多几百上千条,从kafka/rpc消费一条消息时,推送要遍历过万条ws连接。遍历过万条ws,写connfd是耗时系统调用过程,会导致队列消费过慢而积压消息,就算遍历开协程异步也要阻塞到全部推完才能下条否则无法保证时序。此时要批处理,即消费一段时间(100ms内)/一定数量(20条)的消息,把消息集合做为整体,再遍历ws组推送消息块。不是推一条而是推一批消息。
写扩散优缺点:优点:控制逻辑和读取逻辑简单、粉丝数据独立,方便粉丝内容定制化推荐、大V数据丢失,对关注者数据影响不大,关注者依然可以正常读取关注者发布的数据内容。缺点:浪费存储、写扩散要专门的扩散队列、数据实时性较差(要等数据扩散完才能看到关注的内容)
读扩散优缺点:优点:数据实时性实时率高;写入逻辑简单;当读少写多时可以省下扩散成本(不需要扩散队列,也可以节省存储)缺点:数据读取会导致热点问题。
动态负载均衡:程序定时采集负载信息并计算出权值,更新至元数据存储(etcd zk),动态更新加权轮询的权值,然后按加权轮询去进行动态负载均衡。
Nginx:
keepalived抢占模式:当master宕机后,backup接管服务。后续当master恢复后,vip漂移到master上,master重新接管服务,多了一次多余的vip切换
keepalived非抢占模式:实际生产中是,当原先的master恢复后,状态变为backup,不接管服务,这是非抢占模式。
keepalived软件有两种功能,分别是监控检查(心跳检测主是否存活)、VRRP(虚拟路由器冗余协议)。Nginx实现高可用,要借助keepalived地址漂移功能。
kafka为什么快:批处理(消息合一个批次处理,生产者默认每个分区16KB缓存,消费者拉取max.poll.records条数据)、压缩(节省网络传输开销,批消息下效果更好)、副本(高可用)、顺序写、producer2broker(mmap共享空间)、分片(负载均衡)、broker2consumer(sendFile)。 主题无序,分区有序。
kafka消费者组:消费者线程数不能大于分区数(消费者数大于分区数,多余消费者会挂着什么都不干,等某个消费者线程挂掉时,多余消费者线程会顶上来),多个消费者组订阅同一个topic组成广播。
kafka零拷贝 :mmap、sendfile , kafka是用mmap直接写到pagecache 所以重启broker不会丢数据,但宕机会。
kafak消息可靠性:kafka是leader负责所有的读写,followers只是备份。
kafka的消息丢失:消息是mmap写入PageCache,不是直接写入磁盘,重启broker不会丢数据,但宕机会。有可能丢消息,ISR在做高可用。
生产者端:request.required.acks (0:只管发,1:发了只要leader确定, -1: ISR里所有followers拉取成功后才返回client端ack) 。
消费者端:auto.commit.enable(true自动ack, false手动ack提交offset偏移量),pull是一次拉取一批(max.poll.record)。未提交的offset可以重复进行消费。
若true,每过5s,消费者会自动把拉取下来的最大偏移量提交上去。提交时间间隔由auto.commit.interval.ms控制,默认是5s。
若false,可用commitAsync异步提交偏移量,防同步提交偏移量失败而一直阻塞。如发出请求用于提交偏移量20,发生通信问题,服务器收不到请求,不会作出响应。此时我们处理了另一批消息,并成功提交了偏移量30。若commitAsync重新提交偏移量20,可能在偏移量30后提交成功。这时发生再均衡,则会重复处理消息,支持幂等则使用commitAsync。commitAsync重新尝试提交偏移量20,还是失败是程序有问题,打日志告警人工介入处理。
要数据一致,用commitSync同步阻塞提交。确保执行任何操作前知道偏移是否提交成功,会花更多时间等待提交完成,会导致高延迟。若确定某些数据不一致并希望延迟较低,用commitAsync不会阻塞完成,稍后发出提交请求并处理来自Kafka的响应(成功或失败),代码会继续执行,但不会自动重试。
kafka是拉模式消费,pull能简化broker的设计,不用像push要监控client端做流控,消费速率由consumer控制,能控制消费方式-批量/逐条,但没数据时会轮循空转。防broker推送速度大于consumer消费速度而崩溃。
防kafka消息丢失设置参数:
replication.factor=N,总副本数,设置一个比较大的值,保证至少有2个或者以上的副本。有异步副本和同步副本,但insync.replicas<=factor。
min.insync.replicas=N,这个是设定ISR(同步副本)数是多少,默认是1。当required.ack=-1时这参数才生效,推送数据时leader完全同步到ISR里所有副本。ISR里的副本少于配置数时,调用会报错误。
unclean.leader.election.enable=false,选举时false是完全同步的分区副本才能成为Leader(ISR里的才能成为leader)、true可以没有完全同步的副本成为Leader,但有消息丢失的风险。
auto.offset.reset=earliest,没有偏移量可以提交或broker不存在偏移量的时候,消费者如何处理。earliest从分区的开始位置读取,可能会重复读取消息,但不会丢失,消费方肯定要保证幂等
同步机制:https://cloud.tencent.com/developer/article/1476632 https://cloud.tencent.com/developer/article/1536646 (ISR伸缩)
副本间的同步有两个概念,HighWatermark:高水位,也叫做复制点. LogEndOffset:下一条待写入消息的位移。副本间的同步依赖的就是HW和LEO的更新
LEO更新了即数据写成功了。HW:小于等于HW是被认为已备份的,消息已复制到其他副本并确认了。同步数据是follower拉leader。remoteLEO是Leader保存follower发送过来的followerLEO。HW = Min(LeaderLEO,remoteLEO)
ISR:ack=-1时,client新消息到leader后,ISR里follower拉取并落盘同步成功后,leader才返回ack给client。若client没收到ack会重发消息,broker相同消息重发会幂等处理。消费者不丢消息主要是依赖于broker中的ISR机制。动态增加Topic的副本数:https://cloud.tencent.com/developer/article/1590099
ISR与OSR转换:ISR伸缩,Leader会跟踪ISR中follower滞后状态。ISR是同步副本,相对的是跟不上同步节奏的副本OSR。0.9.0.0前用数量差值replica.lag.max.messages判断是否落后太多,是否要踢出ISR,这里指具体的LEO值。0.9.0.0版本后,去掉了replica.lag.max.messages,用replica.lag.time.max.ms代替,允许follower副本不同步消息的最大时间值,即在replica.lag.time.max.ms时间内follower有同步消息,认为其处于ISR中,避免了在某个瞬间生产者发大量消息到leader副本导致该分区ISR频繁收缩与扩张。
存储:
以-为存储文件夹名,里面的文件按日志追加形式WAL进行分段(有很多个小文件,方便旧的整块删除)。
索引文件有点像跳表的区间快速查找,时间->索引文件->数据文件
事务:一个事务中发送多条消息,要么都成功要么都失败。这里的多条消息不一定在同一个主题和分区中,可以是发往多个主题和分区的消息(像mysql对不同的表update)。
kafka之Rebalance(重平衡): https://www.jianshu.com/p/e1af3a703550 https://www.cnblogs.com/listenfwind/p/14146727.html
rebalance过程很像GC时的STW,整个消费群组会停止工作,重平衡间所有消费者都暂停消费,会造成消息积压。
重平衡条件:分区数发生变化;Topic数发生变化;消费者组成员数发生变化;pull超过最大时限、心跳保活超过最大时限(以为消费者挂了);2.3以下版本重启必会;
session.timeout.ms:最大多少时间内向coordinator发送自己心跳不会被认为超时,默认10秒。若消费者不能及时发送心跳,coordinator会认为它已经挂了,将其从Group中移除,然后Rebalance。
heartbeat.interval.ms:每个消费者会有一个独立的线程去定时发送心跳给协调者保持心跳,此参数用来控制发送心跳请求频率,值越小,发送心跳频率会越高。
max.poll.interval.ms:消费者两次调用poll方法(拉数据)的最大时间间隔,默认5分钟,消费者处理时长大于这个时间间隔会让kafka以为消费者挂了而rebalance。
重平衡优化:消费者超时/重启引起的重平衡无法避免。消费者重启后,身份标识ID会变。kafka不确定新加入的消费者是不是刚挂掉的。Kafka2.3引入了静态成员ID,挂掉/重启的消费者在session.timeout.ms前重新加入后,可以保持旧标识,就不需要Reblance。
kafka之topic:https://blog.csdn.net/weixin_42641909/article/details/89294698 https://cloud.tencent.com/developer/news/733186
topic过多顺序IO会降为随机IO(很多文件一起写变成随机io)。
有序消息:指定key,对消息的key hash然后对partition数量取模,决定去哪个分区上,相同key的消息,总是会发送到同一个分区上,也就是消息分区有序性。
消息积压:若分区数大于消费者数则增加消费者;若消费者数>=分区数则扩分区,让分区数与消费者数一对一;增大max.poll.record参数;
分区不是越多越好:producer和consumer都可以多线程地并行操作,每个线程处理一个分区的数据。分区就是调优Kafka并行度的最小单元。producer多个线程并发地向不同分区所在broker发起socket连接,发消息。对consumer,消费者组内consumer线程指定到某个分区进行消费。
生产者有个参数batch.size,为每个分区缓存消息(默认16KB),推送批消息,满了就打包发出。若分区越多,缓存所需内存占用会更多。如1w个分区,生产者的缓存要占用约157MB内存(16*10000/157),也占用更多句柄。
消费者内存开销,依次从partition读取数据,其不断向broker发起fetch请求,每次从partition拉取fetch.max.bytes大小的数据,执行max.poll.records条数据,用commitSync/commitAsync方法提交,拉取的批消息中最大offset保留在coordinator。要手动提交,将commitSync和commitAsync组合使用才能到达最好效果。
消费者线程开销,若1w个分区,消费者线程数要匹配分区的话,在消费者端要有1w个线程,要大约1w个socket去获取分区数据。线程切换开销已经不容小觑了。
分区数计算:创建1个分区的topic,测试其生产者和消费者的吞吐量。如值分别是Tp和Tc,单位是MB/s。总目标吞吐量Tt,则分区=Tt/min(Tp,Tc)。producer吞吐量=20m/s;consumer吞吐量=50m/s,期望吞吐量100m/s;分区数=100/20=5,分区一般设置为:3-10个。
kafka是ap还是cp系统:像mysql一样取决于配置(异步,半同步,全同步)
CP配置,保证强一致性读(写响应返回后立马读到最新读),满足CP。写入一条数据,要同步到所有副本后才返回ack;任意节点都能消费到这条数据,即在有节点宕机的情况下,包括主节点。replication.factor = 3;min.insync.replicas = 3;acks = -1。全同步
AP配置,保证可用性,满足AP。写入一条数据,主节点提交后就返回ack;若主节点在数据同步到从节点前就宕机,重新选举后,消费端就读不到这条数据。这种配置,保证了可用性,但是损失了一致性读。replication.factor = 3;min.insync.replicas = 3;acks=1或0。异步
介于AP和CP间的配置,损失了一定的一致性读和可用性。这种配置可以容忍一个节点(包括主节点)宕机的情况下,任然保证数据强一致性读和整体可用性;但有两个节点宕机的情况,就整体不可用了。replication.factor = 3;min.insync.replicas = 2;acks = all。半同步
rocketmq:
架构基本和kafka一致,有事务反查机制,有延时队列,setDelayTimeLevel(),设置了后会发送到SCHEDULE_TOPIC这个topic里,内部有16个queue,16个延时级别,每个queue都对应不同级别延时。设置级别会放到对应的延时时间的queue里,延时时间是固定写死的。
kafka:
rabbitmq:有延时队列(通过死信队列的方式去做)、rocketmq是DLedger多副本模式,基于raft协议
rocketmq:有延时队列(topic:queue),setDelayTimeLevel(),设置了后会把消息发送到SCHEDULE_TOPIC这个topic里,内部有16个queue,每个queue都对应不同级别的延时。设置对应的级别会放到对应的延时时间的queue里。
etcd异地多活:
镜像模式mirror-maker、raft协议 (数据同步,选主)。是CP系统,当发生网络分区时,有个区会重新选主,但少的区无法复制到大多数防脑裂,成为不可用,另一个区是大多数可正常进行同步保证一致,对外部而言是保证一致的。
分布式强一致算法:过半follower同步完成才返回, 放弃可用性,追求一致性和分区容错性,zookeeper就是追求的强一致,又比如跨行转账,一次转账请求要等待双方银行系统都完成整个事务才算完成,若后面的某个节点挂了,必然返回失败,只要转账成功,立马去读一定读到最新的值。
Raft协议:节点有3种状态 Follower Candidate Leader ,raft是强一致性的。
日志复制:客户端写leader,这时日志是未提交状态,用心跳同步日志到follower,等大多数(过半)follower确认返回后,leader里日志提交,并返回响应给客户端,然后同步apply给所有follwer。
领导选举:所有follower都启动定时器,有leader心跳包来时会重置。当leader挂了或一开始没有leader follower会成为candidate,然后定时器谁先到时就投票(自己1票)别的follower返回,收到半数以上 follower响应(N/2+1)则成为leader
网络分区:出现网络分区时,各集群里实例数量要满足 majority > N/2,集群才能正常写入,少数派集群不可用,所以etcd不会有脑裂问题。
领导人完全原则:若一个日志条目在一个给定任期内被提交,那么这个条目一定会出现在所有任期号更大的领导人中。领导人完全原则保证了领导人一定拥有所有已经被提交的日志条目,但是在它任期开始的时候,它可能不知道哪些是已经被提交的。为了知道这些信息,它需要在它的任期里提交一条日志条目。
写数据时leader故障:
请求到达前Leader宕机了:没有任何问题,选举新的Leader。挂了的Leader未向client发送成功响应,client会重新发送该些操作请求。
未同步数据前Leader宕机了:这种情况下,选举出新的Leader,前任Leader重启后会作为follower重新加入集群,并同步更新Leader中的数据以保证数据一致性。之前接收到client的数据被丢弃。由于原先的Leader没有向client发送处理成功的响应,会向新的Leader再发送请求,新Leader处理后向所有子节点广播,前任Leader中的数据会被覆盖,所以数据可以得到保障。
同步完部分后Leader宕机了:client发送写操作给Leader,接收完数据后开始向follower复制数据。部分follower复制完后Leader挂了,发起新的 Leader选举。这时候有两种情况,1、若Leader产生于已经复制完日志的follower,会继续将前面接收到的写操作请求完成,向client进行响应。2、若Leader产生于尚未复制日志的follower,原来已经复制过日志的follower则会将这个没有完成的日志放弃。由于client没有接收到响应,client会重新发送该写操作请求。
提交通知发出后Leader宕机了:client发送写操作请求给Leader,Leader接收完数据后向follower复制。Leader成功接收到过半follower复制完的响应后,Leader将日志写入到状态机。此时Leader向Follower发送apply通知。发送通知的同时,会向client发出响应。此时leader挂了,前任Leader已经向client发送成功接收响应,且apply通知已经发出,说明这个写操作已经被server成功处理。这里可以靠领导人完全原则保证数据的一致性。
分布式理论:一个分布式系统必须要满足一点,分区容错性(Partition)
微服务:
服务雪崩:超时机制,目的是为了让流量尽快消耗掉。防上游服务长时间不响应导致请求积压下游也没有响应从而整个链路像滚雪球一样雪崩。mysql请求的写超时要取全局ctx和配置的最小值。
超时传递:客户端发起RPC调用时传入了带timeout的ctx,gRPC底层将timeout值写入到HEADERS的`grpc-timeout`。服务收到RPC请求,覆盖透传到实际处理业务的gPRC Handle中,此时服务又调用其他gRPC服务,且用的是透传的ctx。这个timeout会减去在本服务的耗时,传到下个gRPC服务时会变短,这就是所谓的超时传递 。
过载保护:服务过载、流量过多时主动拒绝请求的机制,是可测量的。根据服务当前负载自动丢弃流量,不是改限流配置,对服务而言太被动。比如CPU 90%+高负载,负载变大了,就节流,过载保护是在限流前面的。利特尔法则:每250ms测量当前服务的cpu指标,看是否90%+了要主动拒绝请求。
限流:生产环境都是用redis做分布式限流,令牌桶原理,是在每次访问时计算出来。每次拿一批令牌缓存在本地,减少redis的网络开销。分配限流资源算法:最大最小公平分享 https://pandaychen.github.io/2020/09/21/A-DISTRIBUTE-GOREDIS-RATELIMITER-ANALYSIS/
重试机制:防止网络抖动时(请求抖动/响应抖动)误判服务超时,重新打到网络好的服务去请求,grpc_middleware中间件可做。注意:只能在访问失败的这层服务进行重试,最终失败的话要返回无须重试错误码,避免级联重试风暴。不建议写操作做重试(数据重复),若要做,业务方要做好幂等操作。
负载均衡:满足需求下,节点数都是N+2,1个节点会单点问题。2个节点,1个挂了另外1个就是100%压力。所以要N+2个节点。参考JSQ(最闲轮询)
幂等:返回响应时网络抖动(数据提交是成功的)导致的重试,但数据是真正更新成功了,重试时接收到同样的数据,提交两次、要去重、幂等
熔断:go-breaker hystrix sentinel 熔断降级作为保护服务自身的手段,通常是在客户端进行规则配置和熔断识别。当上游服务过载/限流了,就进入熔断,自身快速失败的同时,保护上游别再给压力了,限流也是有压力的。max(0,(request-K*accept)/(request+1))。双熔断算法:A熔断了写B,B熔断了又回写A。
降级:本质为提供有损服务,看是用什么指标来做降级(cpu 延迟 线程数 rpc错误率)。手动降级:如rpcCallFailure、全局降级:打开服务暂时不可用开关。
链路追踪:jaeger链路追踪好处,做全链路压力测试,分析服务间调用哪个服务耗时长的问题。
API网关:上层抽象出各业务系统需要的通用功能,其实就是把http服务中间件抽出来一个进程负责,如:鉴权、限流、ACL、降级等。随着微服务的流行,API网关已经成为一个微服务架构中的标配组件。动态路由、负载均衡、服务发现、限流管理、熔断、降级、流量管理(黑白名单)、反爬策略 golang常用的网关是kong
隔离:本质上是对系统或资源分割,当系统发生故障时能限定传播范围和影响范围,故障后只有出问题的服务不可用,保证其他服务仍然可用。服务隔离:动静分离、读写分离;轻重隔离:核心、快慢、冷热;物理隔离:线程、进程、集群、机房
同业务隔离:按规格分割集群做隔离,如视频上传:同样是转码业务,但按视频时长做分流,5-10小时的发到A集群、1-5小时的发到B集群、0-1小时的发到C集群,这样大量短视频就可以被C集群快速处理,而不会被长视频处理给阻塞住。
冷热隔离:垂直分表,当做多维(用户/商户)分表时,公共字段可以垂直拆分出去、表频繁更新字段也可以垂直拆分出去,这样更新不频繁字段还能用上BufferPool做加速。
微服务拆分原则:
基于业务逻辑拆分:按照业务职责范围拆分,划分好业务边界,如“商品”,“订单”,“支付”,“买家”,“卖家”,“发货”等多个服务。
基于性能拆分:将流量最大的业务及强关联的业务拆分出来,降低业务互相影响度,优化流量大的业务,提升性能降低成本。
基于可用性拆分:将经常出问题的业务拆分出来,针对性提升问题多的业务
基于稳定性拆分:将稳定的业务拆分出来,拆分后有利于不断变化的业务快速迭代,像会议主体是稳定的、预约是快速迭代的。
基于业务重要程度(可靠性)拆分:重要程度高的核心业务拆分出来,提升重要程度高的业务的可用性。重点保证核心服务的高可用避免非核心服务故障影响核心服务。如:评论是非核心、Id发号器是核心,将其拆分出来,就算评论系统出问题,也不影响核心服务。
ID发号器实现:https://segmentfault.com/a/1190000024499175 https://cloud.tencent.com/developer/article/1820225
分布式Id:雪花算法64位第1位不用,41位毫秒级时间戳,10位工作机器ID其中5位数据中心号5位机器号(可自定义同步到etcd里),12位步长。趋势递增:99.9%情况是顺序写IO,0.1%的情况是随机写IO。可以做分布式Id发号器。实现方式:
cap理论的应用:强一致性强调的是强一致性读,写响应返回后立马去读,能读到最新值不会读到过期值
ap : 放弃强一致,追求分区容错性和可用性,当发生网络分区时,为了保证系统可用性,无需强一致性读。
cp : 在网络分区的情况下强调强一致性读。所有设计是为了一致性,当发生网络分区时,为了保证一致性读,是不可用的。如:etcd ,zk
ca : 单机无网络相连,无多机器网络隔离,不会出现网络分区,保证了强一致读和可用性。
Base理论:最终一致性、基本可用、软状态(基于AP)
基本可用:指分布式系统出现不可预知故障时,允许损失部分可用性(比如高峰期限流熔断了,让少部分人不可用)。
软状态:允许系统中的数据存在中间状态 ,并认为该中间状态存在不会影响整体可用性,即系统在同步数据之间允许延时。
最终一致:经过一段时间同步后,系统最终能够达到一个一致的状态。
各大服务注册发现中心组件比较
分布式事务:整个流程中一定要在业务中做幂等处理(会推送或回调失败做重试)
基于TCC:
复杂一些要自研事务管理器(TM),大厂在用
基于本地消息的最终一致:
异步事件通知:(rocketmq kafka rabbitmq)维护一个本地消息表记录,使消息达到最终一致,重复发送、消费端做幂等
此方案的核心是通过本地事务保证数据业务操作和消息的一致性,然后通过定时任务将消息发送至消息中间件,待确认消息发送给消费方成功再将消息删除。
基于可靠消息的最终一致:
异步事件通知:事务消息(rocketmq是分布式的) 有事务消息回查机制,更可靠。rocketmq并发比rabbitmq高,代码简单了,不用定时任务检查消息记录生产成功
最大努力通知:如 支付宝的支付回调通知接口,返回ok的那个,多次尝试通知成功(回调接口要做幂等)。
(公众号作者注: 此图应该来自慕课网)
2PC:基于XA协议,对高并发不友好。执行时,要多次通信,占用时间长,且当中所有节点处于阻塞状态,每个参与者持有的资源要加锁。
XA 分布式协议制定的分段提交过程:阶段一(prepare)每个参与者准备执行事务并对需要的资源加锁,进入ready状态,并通知协调者已经就绪;阶段二(commit)当协调者确认每个参与者都ready后,通知参与者进行commit操作,若有参与者fail,发送rollback命令,各参与者回滚;
单点故障:由上面可知协调者扮演着非常重要的角色,一旦协调者发生故障,参与者就会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。
数据不一致:在阶段二中,当协调者向参与者发送commit后,出现局部网络异常或在发送commit请求过程中协调者发生了故障,导致只有一部分参与者收到了commit请求。在这部分参与者接到commit请求后会执行commit操作,但其他未接到commit请求的则无法执行事务提交,就导致了数据的不一致。
Elasticsearch:
相关性:结果是按相关性进行排序的,最相关的文档排在最前。每个文档都有相关性评分,用字段_score表示。_score越高,相关性越高。检索词在字段中出现的频率越高,相关性越高;检索词在集合所有文档里出现的频率越高,相关性越低;字段的长度越长,相关性越低;
linux指令:
awk:逐行处理文本(awk处理超大文件,大文件模糊查询正则匹配),不会占据大量内存,而且 awk 执行效率也挺高,可以用来处理超大型文件。
sed:流编辑器,文件需要批量查找替换,用sed敲一行指令,然后可以直接查找替换掉它了。
listof 显示进程已打开文件数
natstat 网络状态相关查询
HUP信号:(动态让服务加载新配置,不需重新启动服务) 想要改配置,但不停止并重新启动服务,使用该命令。在对配置文件做更改后,发出该命令以动态更新服务配置。
高级数据结构:
基数树(radix压缩前缀树): https://learnku.com/docs/eudore/example-radixtree/10178
跳表:
MD5:信息摘要演算法,不属于加密算法。
取余优化:sum%count 可以改成 sum&(count-1) ,数值操作改成位运算。前提条件 count 一定要是2的N次幂。不然不会成功
重试的几种方式:1. fastfail;2. sleep重试 解决闪断;3. 批量扫描重试,解决最终一致性, 支付常见;4. mq排队+ack重试,解决丢失,以及同一个对象需要时序问题
MapReduce: 分小文件算总数,然后读入内存用topK排、10亿的url去重:url相同的放到同一文件,就是MapReduce思想 ,10亿url hash到N个文件,然后读入内存用map去重,然后写入一个文件;文件外排序中判断做减枝处理;
下表总结了若干Linux下的工具: