互联网面试知识技术总结(javacore、jvm、redis、kafka、zookeeper、mysql、hystrix)-第三章

301 阅读37分钟

39,mybatis 一级缓存和二级缓存 1,一级缓存是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。如果中间sqlSession去执行commit操作(执行插入、更新、删除),则会清空SqlSession中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。个人认为一级缓存不是线程安全的,对于同一行数据,另外一个线程写入数据(不使用mybatis组件),会导致session数据无法正常更新。   2,二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。 3,二级缓存默认是不开启的。

40, MySQL 中的行级锁、表级锁和页级锁

InnoDB引擎默认的修改数据语句,update,delete,insert都会自动给涉及到的数据加上排他锁,select语句默认不会加任何锁类型,如果加排他锁可以使用select ...for update语句,加共享锁可以使用select ... lock in share mode语句。所以加过排他锁的数据行在其他事务种是不能修改数据的,也不能通过for update和lock in share mode锁的方式查询数据,但可以直接通过select ...from...查询数据,因为普通查询没有任何锁机制。

在数据库的锁机制中,可以按照锁的粒度把数据库锁分为行级锁(InnoDB引擎)、表级锁(MyISAM引擎)和页级锁(BDB引擎)。 行级锁:MySQL中锁定粒度最细的一种锁,表示只针对当前操作的行进行加锁。行级锁能大大减少数据库操作的冲突,其加锁粒度最小,但加锁的开销也最大。行级锁分为共享锁和排他锁。 特点:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。

表级锁:MySQL中锁定粒度最大的一种锁,表示对当前操作的整张表加锁,它实现简单,资源消耗较少,被大部分MySQL引擎支持。最常使用的MyISAM与InnoDB 都支持表级锁定。表级锁定分为表共享读锁(共享锁)与表独占写锁(排他锁)。 特点:开销小,加锁快,不会出现死锁;锁定粒度大,发出锁冲突的概率最高,并发度最低。

页级锁:MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。因此,采取了折衷的页级锁,一次锁定相邻的一组记录。BDB支持页级锁。 特点:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。

MySQL 常用存储引擎的锁机制 MyISAM和Memory采用表级锁(table-level locking) BDB采用页级锁(page-level locking)或表级锁,默认为页级锁; InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁。

在InnoDB 引擎中既支持行锁也支持表锁,那么什么时候会锁住整张表?什么时候只锁住一行呢? InnoDB 行锁是通过给索引上的索引项加锁来实现的,InnoDB行锁实现的特点意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁。 在实际应用中,要特别注意 InnoDB 行锁的这一特性,不然的话,可能导致大量的锁冲突,从而影响并发性能。 在不通过索引条件查询的时候,InnoDB 确实使用的是表锁,而不是行锁

行级锁又分共享锁和排他锁。     共享锁:       名词解释:共享锁又叫做读锁,所有的事务只能对其进行读操作不能写操作,加上共享锁后在事务结束之前其他事务只能再加共享锁,除此之外其他任何类型的锁都不能再加了。       用法:SELECT id FROM table WHERE id in(1,2) LOCK IN SHARE MODE 结果集的数据都会加共享锁     排他锁:       名词解释:若某个事物对某一行加上了排他锁,只能这个事务对其进行读写,在此事务结束之前,其他事务不能对其进行加任何锁,其他进程可以读取,不能进行写操作,需等待其释放。       用法:SELECT id FROM mk_user WHERE id=1 FOR UPDATE

行级锁与死锁 MyISAM中是不会产生死锁的,因为MyISAM总是一次性获得所需的全部锁,要么全部满足,要么全部等待。而在InnoDB 中,锁是逐步获得的,就造成了死锁的可能。 在 MySQL 中,行级锁并不是直接锁记录,而是锁索引。索引分为主键索引和非主键索引两种,如果一条 SQL 语句操作了主键索引,MySQL 就会锁定这条主键索引;如果一条 SQL 语句操作了非主键索引,MySQL 就会先锁定该非主键索引,再锁定相关的主键索引。 在进行UPDATE、DELETE操作时,MySQL 不仅锁定WHERE条件扫描过的所有索引记录,而且会锁定相邻的键值,即所谓的next-key locking 当两个事务同时执行,一个锁住了主键索引,在等待其他相关索引;另一个锁定了非主键索引,在等待主键索引。这样就会发生死锁。 发生死锁后,InnoDB一般都可以检测到,并使一个事务释放锁回退,另一个获取锁完成事务。

避免死锁的方法 <1>如果不同程序会并发存取多个表,尽量约定以相同的顺序访问表,可以大大降低发生死锁的可能性; <2>在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率; <3>对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁定来减少死锁产生的概率。

行锁优化 1 尽可能让所有数据检索都通过索引来完成,避免无索引行或索引失效导致行锁升级为表锁。 2 尽可能避免间隙锁带来的性能下降,减少或使用合理的检索范围。 3 尽可能减少事务的粒度,比如控制事务大小,而从减少锁定资源量和时间长度,从而减少锁的竞争等,提供性能。 4 尽可能低级别事务隔离,隔离级别越高,并发的处理能力越低。

参考:blog.csdn.net/qq_35246620… 41,乐观锁和悲观锁 悲观锁 总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。

乐观锁 总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。

两种锁的使用场景 从上面对两种锁的介绍,我们知道两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下(多读场景),即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行retry,这样反倒是降低了性能,所以一般多写的场景下用悲观锁就比较合适。

42,索引失效   1.如果条件中有or,即使其中有条件带索引也不会使用(这也是为什么尽量少用or的原因)   要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引   2.对于多列索引,不是使用的第一部分,则不会使用索引(使用组合索引没有符合最左原则,导致实际没能真实使用索引)   3.like查询以%开头   4.如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引   5.如果mysql估计使用全表扫描要比使用索引快,则不使用索引 6.索引无法存储null值,因此索引中有null,查询时,采用is null条件时,不能利用到索引,只能全表扫描。 7.not in ,not exist 参考:www.cnblogs.com/shynshyn/p/…

41.内存映射技术 内存映射文件都知道,它比传统的IO读写数据快很多,那么,它为什么会这么快,从代码层面上来看,从硬盘上将文件读入内存,都是要经过数据拷贝,并且数据拷贝操作是由文件系统和硬件驱动实现的,理论上来说,拷贝数据的效率是一 样的。其实,原因是read()是系统调用,其中进行了数据 拷贝,它首先将文件内容从硬盘拷贝到内核空间的一个缓冲区,如图2中过程1,然后再将这些数据拷贝到用户空间,如图2中过程2,在这个过程中,实际上完成 了两次数据拷贝 ;而mmap()也是系统调用,如前所述,mmap()中没有进行数据拷贝,真正的数据拷贝是在缺页中断处理时进行的,由于mmap()将文件直接映射到用户空间,所以中断处理函数根据这个映射关系,直接将文件从硬盘拷贝到用户空间,只进行了 一次数据拷贝 。因此,内存映射的效率要比read/write效率高(经过测试,内存映射技术比常规的io读取,速度快上6倍左右) (blog.csdn.net/whoamiyang/…

JAVA NIO之浅谈内存映射文件原理与DirectMemory 在传统的文件IO操作中,我们都是调用操作系统提供的底层标准IO系统调用函数 read()、write() ,此时调用此函数的进程(在JAVA中即java进程)由当前的用户态切换到内核态,然后OS的内核代码负责将相应的文件数据读取到内核的IO缓冲区,然后再把数据从内核IO缓冲区拷贝到进程的私有地址空间中去,这样便完成了一次IO操作。至于为什么要多此一举搞一个内核IO缓冲区把原本只需一次拷贝数据的事情搞成需要2次数据拷贝呢? 我想学过操作系统或者计算机系统结构的人都知道,这么做是为了减少磁盘的IO操作,为了提高性能而考虑的,因为我们的程序访问一般都带有局部性,也就是所谓的局部性原理,在这里主要是指的空间局部性,即我们访问了文件的某一段数据,那么接下去很可能还会访问接下去的一段数据,由于磁盘IO操作的速度比直接访问内存慢了好几个数量级,所以OS根据局部性原理会在一次 read()系统调用过程中预读更多的文件数据缓存在内核IO缓冲区中,当继续访问的文件数据在缓冲区中时便直接拷贝数据到进程私有空间,避免了再次的低效率磁盘IO操作(实际上是预读机制)

www.cnblogs.com/lyftest/p/6…

42,kafka

典型的kafka集群中包含若干producer(可以是web前端产生的page view,或者是服务器日志,系统CPU、memory等),若干broker(Kafka支持水平扩展,一般broker数量越多,集群吞吐率越高),若干consumer group,以及一个Zookeeper集群。Kafka通过Zookeeper管理集群配置,选举leader,以及在consumer group发生变化时进行rebalance。producer使用push模式将消息发布到broker,consumer使用pull模式从broker订阅并消费消息

1,为了做到水平扩展,一个topic实际是由多个partition组成的(避免文件尺寸达到单机磁盘的上限,有效提升并发消费的能力),遇到瓶颈时,可以通过增加partition的数量来进行横向扩容。单个parition内是保证消息有序,同一个topic在不同partition之间是无法保证有序的(每条消息的偏移量id保证消费有序),这需要业务方根据自己的业务来实现。 2,一个broker有多个topic,每个topic的分区分散在多个broker之间,并且每个分区在各个broker有备份(即每个partition的非leader备份),但是每个分区只有一个leader 3,异步复制,只要leader写完就算发送完成了,同步复制,得要所有follower写完才算发送完成 4,每个topic将被分成多个partition,每个partition在存储层面是append log文件。任何发布到此partition的消息都会被直接追加到log文件的尾部,每条消息在文件中的位置称为offset(偏移量),offset为一个long型数字,它是唯一标记一条消息,因此在一个partition内可以保证消息是顺序消费的。kafka并没有提供其他额外的索引机制来存储offset,因为在kafka中几乎不允许对消息进行“随机读写”。对于consumer而言,它需要保存消费消息的offset(一个partition可能对应多个多个consumer,分布在多个consume_group里面,为每个consumer分配一个offset,就可以保证线性消费了,实际上这个offset是存在zk里面,每个consumer一个offset),对于offset的保存和使用,有consumer来控制;当consumer正常消费消息时,offset将会"线性"的向前驱动,即消息将依次顺序被消费。 4,在kafka中,一个partition中的消息只会被group中的一个consumer消费 5,一个partition最多只能对应一个consumer(一个consumer可能对应多个partition)

broker: 每个正在运行的kafka节点 producer:消息生产者 consumer:消息的消费者 consumer group:消费者组,同一个消费者组只能有一个consumer能消费消息 kafka server :也叫作broker, 已部署kafka的服务器, 以broker.id来区分不同的服务器 topic:主题, 主题中的每条消息包括key-value和timestamp。可以定义多个topic,每个topic又可以划分为多个分区 partition:topic下的消息分区,通过key取哈希后把消息映射分发到一个指定的分区,每个分区都映射到broker上的一个目录。一般每个分区存储在一个broker上 replica:副本, 每个分区按照生产者的消息达到顺序存放。每个分区副本都有一个leader leader replica:leader角色的分区副本,leader角色的分区处理消息的读写请求. Leader和follower位于不同的broker. follower replica:follower角色的分区副本,负责从Leader拉取数据到本地,实现分区副本的创建 zookeeper:严格来说这不是kafka的组件。但是在Kafka集群中, 很有必要通过Zookeeper管理kafka集群的配置、选举leader(每个topic对应的partition的leader),以及在Consumer Group发生变化时进行rebalance。下面说一下kafka的哪些组件需要注册到zookeeper 为什么要注册到zk集群? 1,Kafka集群通过Zookeeper来管理kafka的配置,选举leader; 2,在Consumer Group发生变化时进行rebalance 3,所有的topic与broker的对应关系都由zk维护

kafka的哪些组件需要注册到zookeeper? (1)Broker注册到zk(因此Broker和zk之间保持心跳,如果无心跳,broker意味着挂掉) 每个broker启动时,都会注册到zk中,把自身的broker.id通知给zk。待zk创建此节点后,kafka会把这个broker的主机名和端口号记录到此节点

(2)Topic注册到zk 当broker启动时,会到对应topic节点下注册自己的broker.id到对应分区的isr列表中;当broker退出时,zk会自动更新其对应的topic分区的ISR列表,并决定是否需要做消费者的rebalance

(3)Consumer注册到zk 一旦有新的消费者组注册到zk,zk会创建专用的节点来保存相关信息。如果zk发现消费者增加或减少,会自动触发消费者的负载均衡。 (注意,producer不注册到zk)

消息如何被消费的? Producer使用push模式将消息发布到broker,Consumer使用pull模式从broker订阅并消费消息;producer通过联系zk获取leader角色的消息分区码,把消息写到leader Producer使用push模式将消息发布到broker +————+ | broker | +————+ | | / PULL | | / Consumer使用pull模式从broker订阅并消费消息

参考:www.cnblogs.com/liyanbin/p/… www.cnblogs.com/likehua/p/3… www.jianshu.com/p/d3e963ff8… blog.csdn.net/dshf_1/arti…

leader选举(容灾) controller会在Zookeeper的/brokers/ids节点上注册Watch,一旦有broker宕机,它就能知道。当broker宕机后,controller就会给受到影响的partition选出新leader。controller从zk的/brokers/topics/[topic]/partitions/[partition]/state中,读取对应partition的ISR(in-sync replica已同步的副本)列表,选一个出来做leader。

kafka的数据存储 实际上是以文件的形式存储在文件系统的。topic下有partition,partition下有segment,segment是实际的一个个文件,topic和partition都是抽象概念。 在目录/{topicName}-{partitionid}/下,存储着实际的log文件(即segment),还有对应的索引文件。 每个segment文件大小相等,文件名以这个segment中最小的offset命名,文件扩展名是.log;segment对应的索引的文件名字一样,扩展名是.index。有两个index文件,一个是offset index用于按offset去查message,一个是time index用于按照时间去查

kafka中的zookeeper作用(consumer连接到哪个broker,是由zk决定的,因此kafka的负载均衡是由zk完成的) 管理broker、consumer 创建Broker后,向zookeeper注册新的broker信息,实现在服务器正常运行下的水平拓展。具体的,通过注册watcher,获取partition的信息。 Topic的注册,zookeeper会维护topic与broker的关系,通过/brokers/topics/topic.name节点来记录。 Producer向zookeeper中注册watcher,了解topic的partition的消息,以动态了解运行情况,实现负载均衡。Zookeepr是没有管理producer,只是能够提供当前broker的相关信息。 Consumer可以使用group形式消费kafka中的数据。所有的group将以轮询的方式消费broker中的数据,具体的按照启动的顺序。Zookeeper会给每个consumer group一个ID,即同一份数据可以被不同的用户ID多次消费。因此这就是单播与多播的实现。以单个消费者还是以组别的方式去消费数据,由用户自己去定义。Zookeeper管理consumer的offset跟踪当前消费的offset.

kafka的leader选举:www.cnblogs.com/aspirant/p/…

新版本的kafka对leader的选举是这样的:在Kafka集群中会有一个或者多个broker,其中有一个broker会被选举为控制器(Kafka Controller),它负责管理整个集群中所有分区和副本的状态。当某个分区的leader副本出现故障时,由控制器负责为该分区选举新的leader副本。当检测到某个分区的ISR集合发生变化时,由控制器负责通知所有broker更新其元数据信息。当使用kafka-topics.sh脚本为某个topic增加分区数量时,同样还是由控制器负责分区的重新分配

在Kafka的早期版本中,并没有采用Kafka Controller这样一个概念来对分区和副本的状态进行管理,而是依赖于Zookeeper,每个broker都会在Zookeeper上为分区和副本注册大量的监听器(Watcher)。当分区或者副本状态变化时,会唤醒很多不必要的监听器,这种严重依赖于Zookeeper的设计会有脑裂、羊群效应以及造成Zookeeper过载的隐患。在目前的新版本的设计中,只有Kafka Controller在Zookeeper上注册相应的监听器,其他的broker极少需要再监听Zookeeper中的数据变化,这样省去了很多不必要的麻烦。不过每个broker还是会对/controller节点添加监听器的,以此来监听此节点的数据变化(参考ZkClient中的IZkDataListener),简单理解就是旧版本的leader和follower partition都在zk注册,导致zk变得沉重

Zookeeper羊群效应:如果有1000个客户端watch 一个znode的exists调用,当这个节点被创建的时候,将会有1000个通知被发送。这种由于一个被watch的znode变化,导致大量的通知需要被发送,将会导致在这个通知期间的其他操作提交的延迟,因此,只要可能,我们都强烈建议不要这么使用watch。仅仅有很少的客户端同时去watch一个znode比较好,理想的情况是只有1个。 解决思路:使用zookeeper时,尽量避免大量节点监控一个节点的行为

策略就是每个client去创建一个顺序的znode /lock/lock-.ZooKeeper 会自动添加顺序号/lock/lock-xxx.我们可以通过/lock getChildren 去拿到最小的顺序号。如果client不是最小的序列号,就再比自己小一点的znode上添加watch,由于序列号最小(最先创建的node)才是获得锁的,因此去通知下一个次小的即可,不用全部通知。 参考:www.cnblogs.com/bnbqian/p/4…

Kafka的消息读取为什么不从follower读取? 1,mysql不用于高qps的读取,并且允许有延迟(可以在从库读取,信息存在不准确性)。但是kafka是为了低延迟和高吞吐量,只同步到部分的follower就完成消息的投递。 2,一个consumer对应一个partition的leader,如果对应多个,就出现了一条数据重复消息多次。(除非消费一次leader,同时修改所有的follower的offset)

43,零拷贝技术(消除多余的拷贝次数,并非一次都没拷贝) 由于数据实际上仍然由磁盘复制到内存,再由内存复制到发送设备,有人可能会声称这并不是真正的"零拷贝"。然而,从操作系统的角度来看,这就是"零拷贝",因为内核空间内不存在冗余数据。应用"零拷贝"特性,出了避免复制之外,还能获得其他性能优势,例如更少的上下文切换,更少的CPU cache污染以及没有CPU必要计算校验和。

步骤一:mmap系统调用导致文件的内容通过DMA模块被复制到内核缓冲区中,该缓冲区之后与用户进程共享,这样就内核缓冲区与用户缓冲区之间的复制就不会发生。 步骤二:write系统调用导致内核将数据从内核缓冲区复制到与socket相关联的内核缓冲区中。 步骤三:DMA模块将数据由socket的缓冲区传递给协议引擎时,第3次复制发生。 参考:www.cnblogs.com/pengdonglin…

44,kafka如果做到百万级高吞吐量的(参考:blog.csdn.net/u010039929/…

生产端 1,可以将消息buffer起来,当消息的条数达到一定阀值时,批量发送给broker,即批量写入磁盘(批量写入,避免零碎写入多次io的打开和关闭)。 2,消息写入到磁盘是顺序写入,充分利用磁盘的顺序读写性能。 3,多个partition同时写入,提高数据的并发写入效率。

broker端 基于内存映射技术实现的零拷贝,sendfile系统调用可以潜在的提升网络IO的性能:将文件的数据映射到系统内存中,socket直接读取相应的内存区域即可,而无需进程再次copy和交换. 其实对于producer/consumer/broker三者而言,CPU的开支应该都不大,因此启用消息压缩机制是一个良好的策略;压缩需要消耗少量的CPU资源,不过对于kafka而言,网络IO更应该需要考虑.可以将任何在网络上传输的消息都经过压缩。 kafka支持gzip/snappy等多种压缩

消费端 批量fetch多条消息,避免多次pull操作过程中的io操作带来的性能损耗。

另外一种解读: 一、接收数据时写得快 (1)消息顺序写入磁盘 (2)消息集合批量发送 (3)采用由Producer,broker和consumer共享的标准化二进制消息格式,这样数据块就可以在它们之间自由传输,无需转换,降低了字节复制的成本开销。 (4)采用了MMAP(Memory Mapped Files,内存映射文件)技术。 (5)利用操作系统的页缓存来实现文件到物理内存的直接映射。完成映射之后对物理内存的操作在适当时候会被同步到硬盘上。 二、推送数据时发得快 (1)在生产端和消费端分别采取的push和pull的方式,也就是你生产端可以认为KAFKA是个无底洞,有多少数据可以使劲往里面推送,消费端则是根据自己的消费能力,需要多少数据,你自己过来KAFKA这里拉取,KAFKA能保证只要这里有数据,消费端需要多少,都尽可以自己过来拿。 (2)采用页缓存和sendfile组合,意味着KAFKA集群的消费者大多数都完全从缓存消费消息,而磁盘没有任何读取活动。 (3)批量压缩,支持Gzip和Snappy压缩协议。 (4)采用多分区设计,并发读写,加快读写速度。

kafka面试题:blog.csdn.net/linke118398…

45,单点登录原理 单点登录全称Single Sign On(以下简称SSO),是指在多系统应用群中登录一个系统,便可在其他所有系统中得到授权而无需再次登录,包括单点登录与单点注销两部分,sso需要一个独立的认证中心,只有认证中心能接受用户的用户名密码等安全信息,其他系统不提供登录入口,只接受认证中心的间接授权。间接授权通过令牌实现,sso认证中心验证用户的用户名密码没问题,创建授权令牌,在接下来的跳转过程中,授权令牌作为参数发送给各个子系统,子系统拿到令牌,即得到了授权,可以借此创建局部会话,局部会话登录方式与单系统的登录方式相同

46,如何扩展spring 1,自定义注解或者自定义解析器。 2,BeanFactoryPostProcessor(调用时机是所有bean的定义信息都已经初始化好)和BeanPostProcessor(针对bean初始化提供扩展) 3,spring aop

47,zookeeper在工作中常见的作用:它是分布式系统中的协调系统,可提供的服务主要有:配置中心(像lion)、分布式同步(分布式锁)、rpc服务注册管理(rpc注册中心) Apache ZooKeeper是由集群(节点组)使用的一种服务,用于在自身之间协调,并通过稳健的同步技术维护共享数据。ZooKeeper本身是一个分布式应用程序,为写入分布式应用程序提供服务。

Zookeeper的角色:leader(负责进行投票的发起和决议,数据变更),follower(接受client读取请求,参与选举),observer(ZooKeeper集群的读取负载很高,可以设置一些observer服务器,以提高读取的吞吐量,不参与选举和投票),follower和Observer都是Learner。

ZooKeeper提供的常见服务如下 : 命名服务 - 按名称标识集群中的节点。它类似于DNS,但仅对于节点。 配置管理 - 加入节点的最近的和最新的系统配置信息。 集群管理 - 实时地在集群和节点状态中加入/离开节点。 选举算法 - 选举一个节点作为协调目的的leader。 锁定和同步服务 - 在修改数据的同时锁定数据。此机制可帮助你在连接其他分布式应用程序(如Apache HBase)时进行自动故障恢复。 高度可靠的数据注册表 - 即使在一个或几个节点关闭时也可以获得数据。

zookeeper入门:www.cnblogs.com/felixzh/p/5… zookeeper实际上以文件形式对节点进行管理,因此同一级目录下不存在重复节点

zookeeper选主流程(basic paxos) 当leader崩溃或者leader失去大多数的follower,这时候zk进入恢复模式,恢复模式需要重新选举出一个新的leader,让所有的Server都恢复到一个正确的状态。Zk的选举算法有两种:一种是基于basic paxos实现的,另外一种是基于fast paxos算法实现的。系统默认的选举算法为fast paxos。

ZooKeeper如何解决"脑裂" 3种可行的思路 (1) Quorums(法定人数,一般只有过半投票才能承认选主成功)法: 通过设置法定人数, 进而确定集群的容忍度, 当集群中存活的节点少于法定人数, 集群将不可用.(或者限制全局至少一半以上节点投票才行) 比如: 3个节点的集群中, Quorums = 2 —— 集群可以容忍 (3 - 2 = 1) 个节点失败, 这时候还能选举出leader, 集群仍然可用; 4个节点的集群中, Quorums = 3 —— 集群同样可以容忍 1 个节点失败, 如果2个节点失败, 那整个集群就不可用了. (2) Redundant communications(冗余通信): 集群中采用多种通信方式, 防止一种通信方式失效导致集群中的节点无法通信. (3) Fencing(共享资源): 通过共享资源的方式, 将所有共享资源添加到集群中, 能对共享资源进行写操作(即加锁)的节点就是leader节点. 原文:blog.csdn.net/ma_shou_fen…

48,zookeeper面试题(segmentfault.com/a/119000001… 1.ZooKeeper是什么? ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务 2,ZooKeeper提供了什么? 文件系统和通知机制 3,Zookeeper通知机制 client端会对某个znode建立一个watcher事件,当该znode发生变化时,这些client会收到zk的通知,然后client可以根据znode变化来做出业务上的改变等。 4,zookeeper是如何保证事务的顺序一致性的? zookeeper采用了递增的事务Id来标识,所有的proposal(提议)都在被提出的时候加上了zxid,zxid实际上是一个64位的数字,高32位是epoch(时期; 纪元; 世; 新时代)用来标识leader是否发生改变,如果有新的leader产生出来,epoch会自增,低32位用来递增计数。当新产生proposal的时候,会依据数据库的两阶段过程,首先会向其他的server发出事务执行请求,如果超过半数的机器都能执行并且能够成功,那么就会开始执行 5,机器中为什么会有leader? 在分布式环境中,有些业务逻辑只需要集群中的某一台机器进行执行,其他的机器可以共享这个结果,这样可以大大减少重复计算,提高性能,于是就需要进行leader选举 6,部署方式?集群中的机器角色都有哪些?集群最少要几台机器 单机,集群,伪集群。Leader、Follower、Observer。集群最低3(2N+1)台,保证奇数,主要是为了选举算法。 7,集群如果有3台机器,挂掉一台集群还能工作吗?挂掉两台呢? 过半存活即可用。

8,zookeeper如何保证数据的一致性,并且如何进行leader和follower的选举? ZooKeeper使用的是ZAB协议作为数据一致性的算法, ZAB(ZooKeeper Atomic Broadcast ) 全称为:原子消息广播协议。zab有两种工作模式:恢复模式和广播模式。zab如何保证数据的一致性:所有事务请求必须由一个全局唯一的服务器(Leader)来协调处理,其他的服务器则成为Follower。Leader会将一个客户端的事务请求转换为一个Proposal(提议),然后分发为集群中所有的Follower。当有过半数的Follower进行了正确的反馈之后,Leader会向所有的Follower发出Commit消息,然后返回客户端成功。 Zab保证数据的一致性是典型的两阶段提交策略。

选举流程:找出一批zxid最大follower,开始投票(paxos算法),投票过半的节点就会成为leader。

Zab协议特点: 1)Zab 协议需要确保那些已经在 Leader 服务器上提交(Commit)的事务最终被所有的服务器提交。 2)Zab 协议需要确保丢弃那些只在 Leader 上被提出而没有被提交的事务。

参考:www.jianshu.com/p/2bceacd60…

9,ZK目录树中每个节点对应一个Znode。每个Znode维护这一个属性,当前版本、数据版本(zxid)、建立时间和修改时间等。znode的数据操作是原子性的(实际还是乐观锁机制,node有版本号)。 zxid在leader和follower选举时很有意义(找出所有zxid为最大的follower,就是所有follower中数据最新的,此时它可以作为leader),zookeeper每次的节点新增、删除,数据修改都会影响zxid递增。

10,zk的节点类型, 持久节点:该数据节点被创建后,就会一直存在于zookeeper服务器上,直到有删除操作来主动删除这个节点,使用场景:常见的配置中心(如点评的lion) 临时节点:临时节点的生命周期和客户端会话绑定在一起,客户端会话失效,则这个节点就会被自动清除。使用场景:①分布式锁(客户端挂掉无法保证心跳,超时导致会话结束,临时节点会被自动删除,锁可以被其它客户端使用),②服务注册中心:服务提供者在ZooKeeper的/dubbo/com.XXX.UserService/providers节点下创建子节点(机器),服务消费者在/dubbo/com.XXX.UserService/consumers创建临时节点 顺序节点:在创建临时或者永久节点时,可以给节点加上递增编号,可以很好解决分布式锁的羊群效应,当锁释放时,只需要通知同一级别的下一个最小节点获取锁,不需要全部通知。 参考:segmentfault.com/a/119000001…

48,分布式锁的实现方式(blog.csdn.net/u010963948/…) 背景:分布式环境下,保证某一段业务逻辑只能被某一台机器执行 实现方式: 1,基于数据库乐观锁的版本号机制实现分布式锁(读取一行记录的版本号,根据版本号去修改,如果修改成功,就代表抢锁成功)。原理:基于行级锁实现,优点:操作简单,容易理解。缺点:不适合高并发场景。 2,基于redis的setnx实现锁创建和释放。原理:基于redis写入操作的原子性(因为redis是单线程),优点:是所有分布式锁中性能最好的(基于内存操作),缺点:由于redis的master-slave同步不是绝对可靠,可能出现锁写入主节点,还没同步到slave(在cluster集群下,客户端加锁可以强制去slave读取一遍,校验锁是否同步成功,不成功就自旋等待) 3,基于zookeeper的临时节点创建实现锁创建和释放,缺点:有羊群效应,需要调用方自己去优化逻辑。

使用redis作为分布式锁注意的问题,cloud.tencent.com/developer/a… 举例:记得给锁加上超时时间,避免执行逻辑过程中发生异常,导致锁无法被显式释放,锁就会被永久占用,导致其它线程无法再次使用。获取锁时,最好加上自己的线程id,以便在删除锁时,再次判断是不是当前线程的锁(如果不是,需要回滚当前事务)。 备注:setnx有潜在风险,先调用setnx设置锁,再调用expire设置超时,这一连串的操作非原子操作,可能超时未设置成功就发生异常,导致引发锁无法释放,应该调用set方法,锁和超时一块执行。

49,分布式事务的解决方案 分布式事务的应用场景(soa服务化的系统里对数据的操作在不同的数据库,即保证业务数据在关联的系统中的流转正确性) 1、支付 最经典的场景就是支付了,一笔支付,是对买家账户进行扣款,同时对卖家账户进行加钱,这些操作必须在一个事务里执行,要么全部成功,要么全部失败。而对于买家账户属于买家中心,对应的是买家数据库,而卖家账户属于卖家中心,对应的是卖家数据库,对不同数据库的操作必然需要引入分布式事务。 2、在线下单 买家在电商平台下单,往往会涉及到两个动作,一个是扣库存,第二个是更新订单状态,库存和订单一般属于不同的数据库,需要使用分布式事务保证数据一致性。

解决方案 1,两阶段提交,需要中间协调器的参与,在prepare阶段,协调器向参与方(如支付宝和余额宝的相互转账)向发起请求,并且开始执行任务,所有参与方执行成功给协调器回复yes,如果所有参与方都回复yes,协调器再通知业务方commit操作(完成事务提交),否则通知所有参与方回滚。

缺点: <1>同步阻塞问题。执行过程中,所有参与节点都是事务阻塞型的。 当参与者占有公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态。 <2>单点故障。由于协调者的重要性,一旦协调者发生故障。 参与者会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。(如果是协调者挂掉,可以重新选举一个协调者,但是无法解决因为协调者宕机导致的参与者处于阻塞状态的问题) <3>数据不一致。在二阶段提交的阶段二中,当协调者向参与者发送commit请求之后,发生了局部网络异常或者在发送commit请求过程中协调者发生了故障,这回导致只有一部分参与者接受到了commit请求。 而在这部分参与者接到commit请求之后就会执行commit操作。但是其他部分未接到commit请求的机器则无法执行事务提交。于是整个分布式系统便出现了数据部一致性的现象。

2,3阶段提交需要中间协调器进行协调,第一阶段协调器向所有的参与方发出执行任务的请求(cancommit),如果回应可以,协调器通知参与方进入prepare,并执行任务。执行成功回应协调器,协调器通知所有参与方进入commit,否着回滚,中间还加入了超时机制

3.TCC模式,也是两阶段提交的一个变种。TCC提供了一个编程框架,将整个业务逻辑分为三块:Try、Confirm和Cancel三个操作。以在线下单为例,Try阶段会去扣库存,Confirm阶段则是去更新订单状态,如果更新订单失败,则进入Cancel阶段,会去恢复库存。总之,TCC就是通过代码人为实现了两阶段提交,不同的业务场景所写的代码都不一样,复杂度也不一样,因此,这种模式并不能很好地被复用。 (下单tryLock库存,confirm(下单成功),cancel(下单失败还原库存))

4,消息驱动的方式,消息方案从本质上讲是将分布式事务转换为两个本地事务,然后依靠下游业务的重试机制达到最终一致性。基于消息的最终一致性方案对应用侵入性也很高,应用需要进行大量业务改造,成本较高。(比如下单成功(预定中),消息投递给库存扣减系统,如果库存扣减失败,回调给上游的订单系统)

参考:www.cnblogs.com/jiangyu666/…

49,dubbo rpc原理,参考:www.cnblogs.com/panxuejun/p… <1>服务注册、服务发现 ①服务提供者 服务提供者会在初始化启动时,首先在ZooKeeper的/dubbo/com.jeiker.UserService/providers节点下创建一个子节点(机器ip),写入自己的URL地址,这就代表了“com.jeiker.UserService”这个服务的一个提供者。 ②服务消费者 服务消费者会在启动时,读取并订阅ZooKeeper上/dubbo/com.jeiker.UserService/providers节点下的所有子节点(机器ip,这些机器ip会缓存在服务消费者端,防止zk挂掉,消费者仍能持续访问提供者),解析出所有提供者的URL地址来作为该服务地址列表,然后发起正常调用,同时,服务消费都还会在 ZooKeeper 的 /dubbo/com.jeiker.UserService/consumers节点下创建一个临时节点,并写入自己的URL地址,这就代表了com.jeiker.UserService这个服务的一个消费者。

<2>zookeeper在rpc框架中的模型 dubbo /
/
com.XX.service1 com.XX.service2 /
providers consumers /\ /
10.0.11.11 10.0.11.12 10.0.11.11 10.0.11.12

参考:blog.csdn.net/jeikerxiao/…

欢迎赞赏