1、raft算法的基本流程?
答:raft协议中,一个节点任一时刻处于以下三个状态之一:leader、follower、candidate。所有节点启动时都是follower状态;在一段时间内如果没有收到来自leader的心跳,从follower切换到candidate,发起选举;如果收到majority的造成票(含自己的一票)则切换到leader状态;如果发现其他节点比自己更新,则主动切换到follower。 总之,系统中最多只有一个leader,如果在一段时间里发现没有leader,则大家通过选举-投票选出leader。leader会不停的给follower发心跳消息,表明自己的存活状态。如果leader故障,那么follower会转换成candidate,重新选出leader。每个leader工作一段时间,然后选出新的leader继续负责。这根民主社会的选举很像,每一届新的履职期称之为一届任期,在raft协议中,也是这样的,对应的术语叫term。
上面已经说过,如果follower在election timeout内没有收到来自leader的心跳,(也许此时还没有选出leader,大家都在等;也许leader挂了;也许只是leader与该follower之间网络故障),则会主动发起选举。步骤如下:
1)增加节点本地的 current term ,切换到candidate状态
2)投自己一票
3)并行给其他节点发送 RequestVote RPCs
4)等待其他节点的回复。在这个过程中,根据来自其他节点的消息,可能出现三种结果
a、收到majority的投票(含自己的一票),则赢得选举,成为leader。
b、被告知别人已当选,那么自行切换到follower。
c、一段时间内没有收到majority投票,则保持candidate状态,重新发出选举。
第一种情况,赢得了选举之后,新的leader会立刻给所有节点发消息,广而告之,避免其余节点触发新的选举。在这里,先回到投票者的视角,投票者如何决定是否给一个选举请求投票呢,有以下约束:
在任一任期内,单个节点最多只能投一票
候选人知道的信息不能比自己的少(这一部分,后面介绍log replication和safety的时候会详细介绍)
first-come-first-served 先来先得
第二种情况,比如有三个节点A B C。A B同时发起选举,而A的选举消息先到达C,C给A投了一票,当B的消息到达C时,已经不能满足上面提到的第一个约束,即C不会给B投票,而A和B显然都不会给对方投票。A胜出之后,会给B,C发心跳消息,节点B发现节点A的term不低于自己的term,知道有已经有Leader了,于是转换成follower。
第三种情况,没有任何节点获得majority投票,比如下图这种情况:
总共有四个节点,Node C、Node D同时成为了candidate,进入了term 4,但Node A投了NodeD一票,NodeB投了Node C一票,这就出现了平票 split vote的情况。这个时候大家都在等啊等,直到超时后重新发起选举。如果出现平票的情况,那么就延长了系统不可用的时间(没有leader是不能处理客户端写请求的),因此raft引入了randomized election timeouts来尽量避免平票情况。同时,leader-based 共识算法中,节点的数目都是奇数个,尽量保证majority的出现。
2、raft算法出现脑裂怎么办?
选举安全性,即任一任期内最多一个leader被选出。这一点非常重要,在一个复制集中任何时刻只能有一个leader。系统中同时有多余一个leader,被称之为脑裂(brain split),这是非常严重的问题,会导致数据的覆盖丢失。在raft中,两点保证了这个属性:
一个节点某一任期内最多只能投一票;
只有获得majority投票的节点才会成为leader。
因此,某一任期内一定只有一个leader。
3、raft算法和zookeeper的zab算法的区别是?
检测 leader down 机:Raft 协议 leader 宕机仅仅由 folower 进行检测,当 folower 收不到 leader 心跳时,则认为 leader 宕机,变为 candidate。Zk 的 leader down 机分别由 leader 和 folower 检测,leader 维护了一个 Quorum 集合,当该 Quorum 集合不再超过半数,leader 自动变为 LOOKING 状态。folower 与 leader 之间维护了一个超链接,连接断开则 folower 变为 LOOKING 状态。
过期 leader 的屏蔽:Raft 通过 term 识别过期的 leader。Zk 通过 Epoch识别过期的 leader。这点两者是相似的。
leader 选举的投票过程:Raft 每个选举周期每个节点只能投一次票,选举失败进入下次周期才能重新投票。Zk 每次选举节点需要不断的变换选票以便选出数据最新的节点为 leader。
保证 commited 的数据出现在未来 leader 中:Raft选取 leader 时拒绝数据比自己旧的节点的投票。Zk 通过在选取 leader 时不断更新选票使得拥有最新数据的节点当选 leader。
参考https://www.dazhuanlan.com/2019/11/28/5ddf86f7e72c2/。
4、有没有了解协程?说下协程和线程的区别?
协程就是充分利用cpu给该线程的时间,在一个线程中放多个任务,某个任务遇到阻塞,执行下一个任务。特点是记住这些任务执行到哪里了。如果一个线程一个任务,比较容易进入阻塞队列,如果这条线程永远在工作,永远不会进入阻塞队列。
cpu虽然可以分时操作,但是能开启的进程是有限的,尽管线程比较轻量,一个cpu同一时刻只能处理一个线程。如果我要处理的任务是无限,如50000个,假如开了200个线程,这200个线程都阻塞了,那下面的4万多个都动不了。当然,如果一个ie线程中没有IO阻塞,只有计算,cpu就会得到充分利用。但是实际情况中往往IO阻塞非常多,如果阻塞程序就停止,就不能做其他事情了,虽然操作系统会调度其他进程或线程工作,但是当前的进程还是会有分配给他的时间片,而他实际是阻塞时还占用着cpu,这是对cpu的浪费。
而进程,线程都会占用系统资源,在他们之间切换也会浪费一些时间,所以在高并发越来越重要的今天,使用线程或进程就不能满足我们了。
协程,有几个特点:协同,因为是由程序员自己写的调度策略,其通过协作而不是抢占来进行切换,在用户态完成创建,切换和销毁。从编程角度上看,协程的思想本质上就是控制流的主动让出(yield)和恢复(resume)机制,generator经常用来实现协程。
Lua从5.0版本开始使用协程,通过扩展库coroutine来实现。python可以通过yield/send的方式实现协程。在python3.5以后,async/await成了更好的替代方案。Go语言对协程的实现非常强大而简洁,可以轻松创建成百上千个协程并发执行。Java语言并没有对协程的原生支持,但是某些开源框架模拟出了协程的功能。
5、http和https有啥区别?说下https解决了什么问题,怎么解决的?说下https的握手过程?
1)https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
2)http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
3)http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4)http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
HTTPS的通信步骤:
(1)客户使用https的URL访问Web服务器,要求与Web服务器建立SSL连接。
(2)Web服务器收到客户端请求后,会将网站的证书信息(证书中包含公钥)传送一份给客户端。
(3)客户端的浏览器与Web服务器开始协商SSL连接的安全等级,也就是信息加密的等级。
(4)客户端的浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,并传送给网站。
(5)Web服务器利用自己的私钥解密出会话密钥。
(6)Web服务器利用会话密钥加密与客户端之间的通信。
6、是否了解tcp/udp,说下两者的定义,tcp为什么要三次握手和四次挥手?tcp怎么保证有序传输的,讲下tcp的快速重试和拥塞机制,知不知道time_wait状态,这个状态出现在什么地方,有什么用(参考quic)?
7、知道udp是不可靠的传输,如果你来设计一个基于udp差不多可靠的算法,怎么设计?
8、如果请求出现问题没有响应,如何定位问题,谈下思路?
答:一、应用服务器CPU利用率高
1)使用top命令查看占用CPU最高的进程号
2)查看进程下所有线程信息,可以使用top,也可以使用ps命令。
top -Hp
也可以用其他命令行将 cpu 占用率高的线程找出来:ps H -eo user,pid,ppid,tid,time,%cpu,cmd --sort=%cpu
3)找到了占用cpu最高的两个线程,要查看线程的信息, 把上面的进程信息dump下来,然后在文件查找线程信息,不是直接dump线程信息。线程对应的是栈,在栈中可以看到线程操作了哪些数据。jmap -dump:format=b,file=文件名
注意,有时候需要把线程号转成16进制,因为dunmp的线程号可能是以16进程显示的,比如:nid=0xc46e,0x表示是16进制的数据,c46e是线程号,转成十进制是142156,则对应的线程号是142156。
二、响应时间慢
在数据库中搜索慢的sql,一般sql的慢是没加索引或者索引失效引起的,也有可能是查询方式过于复杂,表的关联关系不对,小表驱动大表,或者在sql语句中进行了大量的计算等。如果不是慢sql引起的,则需要查找程序的问题,可以通过压测工具定位到具体方法,也可以dump进程,查看是否有锁争用、死锁或者资源等待的情况。在本次压测中出现了大量dubbo服务等待数据库响应的线程,数据库的CPU利用率达到90%,导致应用的大部分进程是sleeping状态,通过查看dump下来的线程发现大部分处于Runable状态,而他们都在等待锁住同一资源(数据库)。增加数据库CPU之后,响应时间慢的问题得到解决。
三、内存泄露
怀疑是内存泄露,于是查看jvm内存回收情况:jstat -gcutil 10366 2000。
每2秒查看一次10366进程的内存回收情况,几乎是一秒2次,并且伊甸园区和年老代的内存已经使用完毕,即使1秒回收2次也不能释放,肯定有不能回收的大对象存在。下面就使用jmap工具,dump 将内存使用的详细情况输出到文件
jmap -dump:live,format=b,file=a.log pid
dump下来的文件大概2个G,使用工具(IBM HeapAnalyzer 工具 )进行分析。
9、tcp粘包问题怎么处理?
应用程序写入的数据大于套接字缓冲区大小,这将会发生拆包。
应用程序写入数据小于套接字缓冲区大小,网卡将应用多次写入的数据发送到网络上,这将会发生粘包。
进行MSS(最大报文长度)大小的TCP分段,当TCP报文长度-TCP头部长度>MSS的时候将发生拆包。
接收方法不及时读取套接字缓冲区数据,这将发生粘包。
通常会有以下一些常用的方法:
使用带消息头的协议、消息头存储消息开始标识及消息长度信息,服务端获取消息头的时候解析出消息长度,然后向后读取该长度的内容。
设置定长消息,服务端每次读取既定长度的内容作为一条完整消息,当消息不够长时,空位补上固定字符。
设置消息边界,服务端从网络流中按消息编辑分离出消息内容,一般使用‘\n’。
更为复杂的协议,例如楼主最近接触比较多的车联网协议808,809协议。
10,服务器CPU 100%怎么定位?
1)先找到Java应用的pid
ps -ef | grep java 或者 jps -l 查看
2)查看堆内存使用量
jmap -heap
3)查看Java进程中的每一个线程的情况(linux),可以清晰的看到每一个线程的cpu及内存使用情况
top -Hp
4)打印线程快照信息,保存到文件xxx.txt中方便查看
jstack
> xxx.txt
参考这一篇文章: https://www.jb51.net/article/195797.htm
5)通过top -Hp
printf "%x"
假如输出为 1111
6)在xxx.txt文件中查找tid为1111的栈信息,可以看到这个线程在干什么,定位到问题代码。
7)程序宕机会自动产生dump文件,若没有宕机就手动导出dump文件
jmap -dump:format=b,file=文件名
11,HTTP的常见错误码,分别表示什么?
200:正确的请求返回正确的结果,如果不想细分正确的请求结果都可以直接返回200。
201:表示资源被正确的创建。比如说,我们 POST 用户名、密码正确创建了一个用户就可以返回 201。
202:请求是正确的,但是结果正在处理中,这时候客户端可以通过轮询等机制继续请求。
203:请求的代理服务器修改了源服务器返回的 200 中的内容,我们通过代理服务器向服务器 A 请求用户信息,服务器 A 正常响应,但代理服务器命中了缓存并返回了自己的缓存内容,这时候它返回 203 告诉我们这部分信息不一定是最新的,我们可以自行判断并处理。
300:请求成功,但结果有多种选择。
301:请求成功,但是资源被永久转移。比如说,我们下载的东西不在这个地址需要去到新的地址。
303:使用 GET 来访问新的地址来获取资源。
304:请求的资源并没有被修改过。
308:使用原有的地址请求方式来通过新地址获取资源。
400:请求出现错误,比如请求头不对等。
401:没有提供认证信息。请求的时候没有带上 Token 等。
402:为以后需要所保留的状态码。
403:请求的资源不允许访问。就是说没有权限。
404:请求的内容不存在。
406:请求的资源并不符合要求。
408:客户端请求超时。
413:请求体过大。
415:类型不正确。
416:请求的区间无效。
500:服务器错误。
501:请求还没有被实现。
502:网关错误。
503:服务暂时不可用。服务器正好在更新代码重启。
505:请求的 HTTP 版本不支持。
12、MYSQL数据库引擎?应用场景?
一 Innodb。支持事务,是事务安全的,提供行级锁与外键约束,有缓冲池,用于缓冲数据和索引。
适用场景:用于事务处理,具有ACID事物支持,应用于执行大量的insert和update操作的表。
二 MyISAM。不支持事务,不支持外键约束,不支持行级锁,操作时需要锁定整张表,不过会保存表的行数,所以当执行select count(*) from tablename时执行特别快。
适用场景:用于管理非事务表,提供高速检索及全文检索能力,适用于有大量的select操作的表,如 日志表。
三 MEMORY。使用存在于内存中的内容创建表,每一个memory只实际对应一个磁盘文件。因为是存在内存中的,所以memory访问速度非常快,而且该引擎使用hash索引,可以一次定位,不需要像B树一样从根节点查找到支节点,所以精确查询时访问速度特别快,但是非精确查找时,比如like,这种范围查找,hash就起不到作用了。另外一旦服务关闭,表中的数据就会丢失,因为没有存到磁盘中。
适用场景:主要用于内容变化不频繁的表,或者作为中间的查找表。对表的更新要谨慎因为数据没有被写入到磁盘中,服务关闭前要考虑好数据的存储
四 MERGE。MERGE存储引擎把一组MyISAM数据表当做一个逻辑单元来对待,让我们可以同时对他们进行查询。构成一个MERGE数据表结构的各成员MyISAM数据表必须具有完全一样的结构。每一个成员数据表的数据列必须按照同样的顺序定义同样的名字和类型,索引也必须按照同样的顺序和同样的方式定义。假设日志数据表的当前集合包括 log_2004、log_2005、log_2006、log_2007 ,而你可以创建一个如下所示的MERGE数据表把他们归拢为一个逻辑单元:
CREATE TABLE log_merge
(
dt DATETIME NOT NULL,
info VARCHAR(100) NOT NULL,
INDEX(dt)
) ENGINE = MERGE UNION = (log_2004, log_2005, log_2006, log_2007);
13,MYSQL查询优化的方式
1)避免查询不需要的记录,只返回需要的列,避免重复查询相同的列应用缓存缓存起来。
2)避免在 where 子句中使用!=或<>操作符;避免在 对字段进行 null 值判断;避免用 or ;避免前模糊匹配%like;避免对字段进行表达式操作;避免在“=”左边进行函数、算术运算或其他表达式运算。
3)一个表的索引数最好不要超过6个;避免更新 clustered 索引数据列;尽量使用数字型字段;尽可能的使用 varchar/nvarchar 代替 char/nchar ;尽量避免使用游标;尽量避免大事务操作;
4)两表关联查询次数大于例如5次的时,考虑使用内连接inner join而不是左连接。如果要求是查询次数小于例如5次的,就不能使用内连接inner join,减少关联之后的数据量。
14,排序算法了解哪些?时间复杂度分别是?快速排序是稳定点吗?快排排对象的时候有什么问题?
1)冒泡排序:时间复杂度O(N^2)。首先从第一个元素开始到数组最后一个元素为止,对相邻的两个元素比较,如果左端元素大于右端元素,则交换这两个元素位置,此时数组最右端元素即为该数组中所有元素的最大值。接着对该数组剩下的n-1个元素进行冒泡排序,直到整个数组有序排列。
2)选择排序:时间复杂度O(N^2)。先从n个数字中找到最小值min1,如果最小值min1的位置不在数组的最左端(也就是min1不等于arr[0]),则将最小值min1和arr[0]交换,接着在剩下的n-1个数字中找到最小值min2,如果最小值min2不等于arr[1],则交换这两个数字,依次类推,直到数组arr有序排列。
3)快速排序:算法的时间复杂度是O(nlogn),最坏的时间复杂度O(n^2),空间复杂度O(nlogn)。通过一趟排序将待排的记录分割成独立的两部分,其中一部分记录的关键字均比另一个部分的关键字小,然后再分别对这两个部分记录继续进行排序,以达到整个序列有效。
具体做法为:设置两个指针low和high分别指向待排序列的开始和结尾,记录下基准值baseval(待排序列的第一个记录),然后先从high所指的位置向前搜索直到找到一个小于baseval的记录并互相交换,接着从low所指向的位置向后搜索直到找到一个大于baseval的记录并互相交换,重复这两个步骤直到low=high为止。
4)归并排序:时间复杂度O(n^2)。假设初始序列含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并。
5)插入排序:时间复杂度为O(n^2)。每次将一个待排的记录插入到前面的已经排好的队列中的适当位置。
6)希尔排序:时间复杂度O(n^1.25),空间复杂度O(1)。不稳定的排序方法。先将待排记录序列分割成为若干子序列分别进行插入排序,待整个序列中的记录"基本有序"时,再对全体记录进行一次直接插入排序。
7)堆排序:时间复杂度O(nlogn)。
8)基数排序:时间复杂度O(d(n+rd))。
15、HashMap中Hash是怎么解决冲突的?其他hash冲突解决的方式有哪些?
链地址法(拉链法):将所有冲突元素按照链表存储,冲突后时间复杂度变为O(1+n)n为冲突元素个数)。HashMap里面没有出现hash冲突时,没有形成单链表时,hashmap查找元素很快,get()方法能够直接定位到元素,但是出现单链表后,单个bucket 里存储的不是一个 Entry,而是一个 Entry 链,系统只能必须按顺序遍历每个 Entry,直到找到想搜索的 Entry 为止——如果恰好要搜索的 Entry 位于该 Entry 链的最末端(该 Entry 是最早放入该 bucket 中),那系统必须循环到最后才能找到该元素。
在JAVA8中,当链表元素达到8个后,会自动转化红黑树存储。
其他长途解决方式:
开放地址法:寻找下一个为空的数组下标,而后将冲突元素存储。
再散列法(二次哈希法):再次使用一个不同的哈希算法再计算一次 (第一次%16换另一个数进行%运算)。
16、红黑树的插入?
因为红黑树本身是二叉搜索树,所以,只需要从根节点开始,逐步比较大小,即可把待插入的节点放置在合适的位置上。红黑树的左右子树具有相等的黑高。如果新插入的节点默认设置为黑色,则势必会破坏左右子树的黑高相等的特点。此时,再调整红黑树,就会比较繁琐。而如果默认将新插入的节点设置为红色,它不会破坏相等黑高的已有状态,我们只需要解决红节点的左右孩子必须是黑节点的问题即可。所以,红黑树新插入的节点,默认设置为红色。
17、java中怎么分区,怎么判断对象是否需要回收?
一、JVM分区
1)本地方法栈:native方法调用时的方法调用栈,存储本地栈帧
2)虚拟机方法栈:Java的栈,每个线程有一个线程调用栈,栈的元素是栈帧。栈帧包括:局部变量表、操作数栈、指向堆中对象的引用、返回地址、附加信息。每个方法调用时,回向当前指向的线程栈顶部压入一个栈帧,栈帧的大小是固定的,虚拟机通过解析.class文件可以得知。
3)堆:堆是所有Java线程共享的一个内存区域,用于分配对象。
4)方法区:存储类的信息(类名、方法信息、字段信息)、静态变量、常量池。
5)程序计数器:用于记录下一条要执行的指令,每个线程都有自己的程序计数器,配合线程栈用于在线程调度时的线程上下文切换。执行本地方法时程序计数器中没有值或为undefined。
二、怎么判断对象是否需要回收?
可达性分析算法。Java中定义了一些起始点,称为GC Root,当有对象引用它的时候,就把对象挂载在它下面,形成一个树状结构,当一个对象处于一个这样的树里时,就认为此对象是可达的,反之是不可达。
那么有哪些可以作为GC Root呢?静态属性(被static修饰的属性)、常量(被static final修饰的属性)、虚拟机栈(本地变量表)中引用的对象、本地方法栈中引用的对象
参考 JVM内存分区与GC知识点
18、kafka如何实现消息幂等?怎么保证消息的可靠性?
Producer
默认不是幂等性的,向分区发送数据时,可能会出现同一条消息被发送多次导致消息重复的情况。但只需增加一些参数,即可开启幂等性。开启enable.idempotence
后,kafka
就会自动帮你做好消息去重的一系列工作。底层具体实现原理很简单,就是用空间换时间的优化思路,即在broker
端多存一些字段来标识数据的唯一性。当Producer
发送了具有相同字段值的消息后,broker
会进行匹配去重,丢弃重复的数据。实际的代码没这么简单,但大致是这么个处理逻辑。
官方的这个幂等实现看似简单高效,但也存在他的局限性。他只能保证单分区上的幂等性,即一个幂等性Producer
只能够保证某个topic
的一个分区上不出现重复消息,无法实现多分区的幂等。此外,如果Producer
重启,也会导致幂等重置。
当Producer发送消息(x2,y2)给Broker时,Broker接收到消息并将其追加到消息流中。此时,Broker返回Ack信号给Producer时,发生异常导致Producer接收Ack信号失败。对于Producer来说,会触发重试机制,将消息(x2,y2)再次发送,但是,由于引入了幂等性,在每条消息中附带了PID(ProducerID)和SequenceNumber。相同的PID和SequenceNumber发送给Broker,而之前Broker缓存过之前发送的相同的消息,那么在消息流中的消息就只有一条(x2,y2),不会出现重复发送的情况。
比如说消费端已经消费了 offset=2,offset=3,offset=4 的三条数据,正准备把这个 offset 的值传给 kafka,这时候消费端机器宕机了,这个数据没传过去;重启之后,消费端同步 kafka,kafka 那边消费的记录 offset 还是 1,那么 kafka 会认为之前的 2、3、4 都没有消费过,会把这几个数据在传给消费端;这样消费端这边就重复对这几条数据进行消费了。在数据库里面可能就多了很多重复的数据。像其他的 MQ,也是一样,消费端再返回给 MQ 的时候,当机了或者重启了,那么都会出现重复消费的问题。
可靠性:当producer向leader发送数据时,可以通过request.required.acks参数来设置数据可靠性的级别:
1(默认):这意味着producer在ISR中的leader已成功收到的数据并得到确认后发送下一条message。如果leader宕机了,则会丢失数据。
0:这意味着producer无需等待来自broker的确认而继续发送下一批消息。这种情况下数据传输效率最高,但是数据可靠性确是最低的。
-1:producer需要等待ISR中的所有follower都确认接收到数据后才算一次发送完成,可靠性最高。但是这样也不能保证数据不丢失,比如当ISR中只有leader时,这样就变成了acks=1的情况。
19、kafka的架构介绍一下
(1)Topics(主题)
属于特定类别的消息流称为主题。 数据存储在主题中。Topic相当于Queue。
主题被拆分成分区。 每个这样的分区包含不可变有序序列的消息。 分区被实现为具有相等大小的一组分段文件。
(2)Partition(分区)
(3)Partition offset(分区偏移)
每个分区消息具有称为 offset 的唯一序列标识。
(4)Replicas of partition(分区备份)
副本只是一个分区的备份。 副本从不读取或写入数据。 它们用于防止数据丢失。
(5)Brokers(经纪人)
(6)Kafka Cluster(Kafka集群)
Kafka有多个代理被称为Kafka集群。 可以扩展Kafka集群,无需停机。 这些集群用于管理消息数据的持久性和复制。
(7)Producers(生产者)
生产者是发送给一个或多个Kafka主题的消息的发布者。 生产者向Kafka经纪人发送数据。 每当生产者将消息发布给代理时,代理只需将消息附加到最后一个段文件。实际上,该消息将被附加到分区。 生产者还可以向他们选择的分区发送消息。
(8)Consumers(消费者)
Consumers从经纪人处读取数据。 消费者订阅一个或多个主题,并通过从代理中提取数据来使用已发布的消息。
20、concurrentHashMap是怎么实现原理?
21、分布式事务的实现?
22、http为什么会出现大量的time-wait?2msl的作用是什么?
23、一致性hash算法?
24、java的类加载机制?
25、redis的主从复制原理?
26、Springboot如何去掉不用的配置?springboot不是开箱即用么?怎么把不必要的配置去掉呢?
27、假设有一万个优惠券,每个优惠券只能由一个人抢到,每个人只能抢到一个优惠券,如何设计?
28、如何理解BTree机制?
29、谈一谈悲观锁和乐观锁以及SQL怎么实现?
30、MYSQL的锁并发?
31、高并发场景下如何防止死锁,保证数据的一致性?
32、Redis的setnx命令是如何实现分布式锁的?使用redis怎么进行异步队列?会有什么缺点?
33、treemap和hashmap的区别?
34、多线程的五大状态?以及什么场景会进入该状态
35、java的nio和io的区别?
36、AOP的实现原理?
37、java的锁有哪些?可重入锁和不可重入锁的区别?
38、MYSQL实现事务的原理?
39、MQ底层原理是怎么实现的?
40、聚集索引和非聚集索引
数据行的物理顺序与列值(一般是主键的那一列)的逻辑顺序相同,一个表中只能拥有一个聚集索引。MySQL的Innodb是以主键索引来组织数据结构的,主键索引与行记录是存储在一起的,故叫做聚集索引(Clustered Index)。
41、DNS解析的过程?
解答一:
1、当应用过程需要将一个主机域名映射为IP地址时,就调用域名解析函数,解析函数将待转换的域名放在DNS请求中,以UDP报文方式发给本地域名服务器;
2、本地的域名服务器查到域名后,将对应的IP地址放在应答报文中返回;
3、同时域名服务器还必须具有连向其他服务器的信息以支持不能解析时的转发;
4、若域名服务器不能回答该请求,则此域名服务器就暂成为DNS中的另一个客户,向根域名服务器发出请求解析,根域名服务器一定能找到下面的所有二级域名的域名服务器,这样以此类推,一直向下解析,直到查询到所请求的域名。
解答二:
第一步:客户机提出域名解析请求,并将该请求发送给本地 域名服务器。
第二步:当本地 域名服务器收到请求后,就先查询本地 缓存,如果 该纪录项,则本地 域名服务器就直接把查询 结果返回。
第三步:如果本地 缓存中没 该纪录,则本地域名服务器就直接把请求发给根域名服务器,然后根域名服务器再返回给本地域名服务器一个所查询域(根 子域) 主域名服务器 地址。
第四步:本地服务器再向 一步返回 域名服务器发送请求,然后接受请求 服务器查询自己 缓存,如果没 该纪录,则返回相关 下级 域名服务器 地址。
第五步:重复第四步,直到找到正确 纪录。
第六步:本地域名服务器把返回 结果保存到缓存,以备下一次使用,同时还将结果返回给客户机。
42、频繁老年代回收怎么分析和解决?
43、java并发工具和类有哪些?线程安全的方式有哪些?
44、负载均衡算法?
答:1)轮询(RoundRobin)将请求顺序循环地发到每个服务器。当其中某个服务器发生故障,AX就把其从顺序循环队列中拿出,不参加下一次的轮询,直到其恢复正常。
2)比率(Ratio):给每个服务器分配一个加权值为比例,根椐这个比例,把用户的请求分配到每个服务器。当其中某个服务器发生故障,AX就把其从服务器队列中拿出,不参加下一次的用户请求的分配,直到其恢复正常。
3)优先权(Priority):给所有服务器分组,给每个组定义优先权,将用户的请求分配给优先级最高的服务器组(在同一组内,采用预先设定的轮询或比率算法,分配用户的请求);当最高优先级中所有服务器或者指定数量的服务器出现故障,AX将把请求送给次优先级的服务器组。这种方式,实际为用户提供一种热备份的方式。
4)最少连接数(LeastConnection):AX会记录当前每台服务器或者服务端口上的连接数,新的连接将传递给连接数最少的服务器。当其中某个服务器发生故障,AX就把其从服务器队列中拿出,不参加下一次的用户请求的分配,直到其恢复正常。
5)最快响应时间(Fast Reponse time):新的连接传递给那些响应最快的服务器。当其中某个服务器发生故障,AX就把其从服务器队列中拿出,不参加下一次的用户请求的分配,直到其恢复正常。
以上为通用的负载均衡算法,还有一些算法根据不同的需求也可能会用到,例如:
6)哈希算法( hash): 将客户端的源地址,端口进行哈希运算,根据运算的结果转发给一台服务器进行处理,当其中某个服务器发生故障,就把其从服务器队列中拿出,不参加下一次的用户请求的分配,直到其恢复正常。
7)基于策略的负载均衡:针对不同的数据流设置导向规则,用户可自行编辑流量分配策略,利用这些策略对通过的数据流实施导向控制。
8)基于数据包的内容分发:例如判断HTTP的URL,如果URL中带有.jpg的扩展名,就把数据包转发到指定的服务器。
45、聊一聊JVM的分区和GC
46、请说出常用的异常类型?
参考java中常见的异常种类
47、泛型通配符,在什么情况下使用?
48、MYSQL是怎么解决幻读的?
答:幻读的例子非常的清楚,为了高并发数据库系统中,保证事务与事务之间隔离性和数据一致性,mysql innodb引擎默认是RR的隔离级别,在mysql中通过MVCC快照读和next-key(当前读)两种模式解决幻读问题。
多版本并发控制(MVCC)(快照读/一致性读);
next-key(当前读):将当前数据行与上一条数据和下一条数据之间的间隙锁定,保证此范围内读取数据是一致的。next-key锁包括记录锁和间隙锁,记录锁是加在索引上的锁,间隙锁是加在索引之间的锁。
49、TIME_WAIT状态了解吗?
答:通信双方建立TCP连接后,主动关闭连接的一方就会进入TIME_WAIT状态。客户端主动关闭连接时,会发送最后一个ack后,然后会进入TIME_WAIT状态,再停留2个MSL时间(后有MSL的解释),进入CLOSED状态。MSL就是最大分节生命期,这是一个IP数据包能在互联网上生存的最长时间,超过这个时间IP数据包将在网络中消失 。MSL在RFC 1122上建议是2分钟。
(客户端主动关闭连接)客户端则是使用一个本地的空闲端口(大于1024),与服务端的Apache的80端口建立连接。当通信时使用短连接,并由客户端主动关闭连接时,主动关闭连接的客户端会产生TIME_WAIT状态的连接,一个TIME_WAIT状态的连接就占用了一个本地端口。这样在TIME_WAIT状态结束之前,本地最多就能承受6万个TIME_WAIT状态的连接,就无端口可用了。
大多数服务器端一般执行被动关闭,服务器不会进入TIME_WAIT状态。服务端为了解决这个TIME_WAIT问题,可选择的方式有三种:保证由客户端主动发起关闭(即做为B端); 关闭的时候使用RST的方式;对处于TIME_WAIT状态的TCP允许重用。
50、feign和http调用的区别?
答:feign只是一个接口声明式调用的框架,只实现了一个抽象层的逻辑,没有真正实现底层http请求,它提供了一个Client接口用于实现底层http操作,默认提供的实现是基于httpurlconnection,也有基于apache httpclient的实现。feign插件有分布式负载均衡的功能。
51、重做日志(redo log)和回滚日志(undo log)?
答:一、redo log重做日志:确保事务的持久性。redo日志记录事务执行后的状态,用来恢复未写入data file的已成功事务更新的数据。防止在发生故障的时间点,尚有脏页未写入磁盘,在重启mysql服务的时候,根据redo log进行重做,从而达到事务的持久性这一特性。
事务开始之后就产生redo log,在事务的执行过程中,便开始写入redo log文件中。当对应事务的脏页写入到磁盘之后,redo log的使命也就完成了,重做日志占用的空间就可以重用(被覆盖)。重做日志有一个缓存区Innodb_log_buffer,Innodb_log_buffer的默认大小为8M(这里设置的16M),Innodb存储引擎先将重做日志写入innodb_log_buffer中。
二、undo log 回滚日志:保证数据的原子性,保存了事务发生之前的数据的一个版本,可以用于回滚,同时可以提供多版本并发控制下的读(MVCC),也即非锁定读。事务开始之前,将当前是的版本生成undo log,undo 也会产生 redo 来保证undo log的可靠性。
当事务提交之后,undo log并不能立马被删除,而是放入待清理的链表,由purge线程判断是否由其他事务在使用undo段中表的上一个事务之前的版本信息,决定是否可以清理undo log的日志空间。
52、redis 底层 hash 表扩容机制说一下?
答:字典中包含两个hashtable,首先Redis在正常读写时会用到一个hashtable,另一个hashtable的作用实际上是作为哈希表进行rehash时的一个临时载体。
扩容时,redis会根据数据量和桶的个数初始化那个备用的hashtable,来使这个hashtable从容量上满足后续的使用,并开始把之前的hashtable的数据迁移到这个新的hashtable上(重新计算哈希值),等到数据全部迁移完毕,再进行一次HashTable的地址更名,把这个备用的HashTable更名为正式的HashTable,同时清空另一个HashTable,以供下一次rehash的使用。然后将rehashidx赋值为0,打开渐进式rehash标志。同时该值也标志渐进式rehash当前已经进行到哪个hash值。
扩容条件:当前哈希表中保存的key的数量超过了哈希表的大小size,并且redis服务当前允许执行rehash(指的是当前没有子进程在执行AOF文件重写或者生成RDB文件《持久化操作》);或者保存的节点数与哈希表的大小的比例超过了安全阈值(默认为5)。
缩容条件:元素个数低于数组长度的10%,缩容不会考虑Redis是否正在做bgsave。
53、什么是非对称加密?
答:
对称加密是使用的同一把密匙进行加密和解密。那么,非对称加密自然是使用不同的密钥进行加密和解密啦。
非对称加密有两个钥匙,及公钥(Public Key)和私钥(Private Key)。公钥和私钥是成对的存在,如果对原文使用公钥加密,则只能使用对应的私钥才能解密;因为加密和解密使用的不是同一把密钥,所以这种算法称之为非对称加密算法。
非对称加密算法的密匙是通过一系列算法获取到的一长串随机数,通常随机数的长度越长,加密信息越安全。通过私钥经过一系列算法是可以推导出公钥的,也就是说,公钥是基于私钥而存在的。但是无法通过公钥反向推倒出私钥,这个过程的单向的。就算接收方公钥泄漏,也不会导致消息泄漏,因为密文只能通过接收方的私钥才能打开。所以,信息安全过程中,接收方只需要保管好自己的私钥不泄露即可。
同样,当接收方向发送方发送消息时,接收方将密文通过原发送方的公钥进行加密,原发送方通过自己的私钥才可解密。
54、什么是数字证书?
答:
数字证书有点类似于我们的居民身份证,只是数字证书是基于互联网通信的,用于标记通信双方身份的一种方式。数字证书是由权威机构Certificate Authority发行的,又称之为证书授权,简称为:CA。人们在网上可以根据它来识别对方身份信息。
数字证书绑定了公钥及其持有者的真实身份,它类似于现实生活中的居民身份证,所不同的是数字证书不再是纸质的证照,而是一段含有证书持有者身份信息并经过认证中心审核签发的电子数据,广泛用在电子商务和移动互联网中。
55、什么是数字签名?
答:数字签名是指将摘要信息使用接收者的公钥进行加密,与密文一起发送给接收者。接收者使用自己的私钥对摘要信息进行解密,然后使用Hash函数对收到的密文产生一个摘要信息,然后将摘要信息与发送着传输过来解密后的摘要信息对比是否一致。如果一致,则表明数据信息没有被篡改。
也就是说,数字签名能够验证收到的信息的完整性,避免中途信息被劫持篡改或丢失。对方可以根据数字签名来判断获取到的数据信息时候是最原始的数据。
如果不好理解,这里再给举个例子。
例如,发送一段文字”abcd”,通过Hash算法(方便测试,拟比HashCode)得到hashCode值2987074,然后将2987074再次使用对方的公钥进行加密,然后将文字”abcd”使用对方的公钥进行加密得到密文,一起发送给接收者。接收者再获取到信息后,先通过自己的私钥将摘要信息解密,然后将密文解密,并且通过相同的hash算法计算出密文的hashcode值,对比两个hashcode值是否一致。
56、TCP连接怎么保证可靠传输?
答:一、校验和:发送的数据包的二进制相加然后取反,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP将丢弃这个报文段和不确认收到此报文段。
二、确认应答+序列号(累计确认+seq):接收方收到报文就会确认(累积确认:对所有按序接收的数据的确认),TCP给发送的每一个包进行编号,接收方对数据包进行排序,把有序数据传送给应用层。
三、超时重传:当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。
四、流量控制:TCP连接的每一方都有固定大小的缓冲空间,TCP的接收端只允许发送端发送接收端缓冲区能接纳的数据。当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失。TCP使用的流量控制协议是可变大小的滑动窗口协议。接收方有即时窗口(滑动窗口),随ACK报文发送。
五、当网络拥塞时,减少数据的发送。发送方有拥塞窗口,发送数据前比对接收方发过来的即使窗口,应用数据被分割成TCP认为最适合发送的数据块。TCP的接收端会丢弃重复的数据。
参考:TCP协议如何保证可靠传输
57、redis zset底层实现原理?
答:基于ziplist
或者skiplist。默认
元素数量小于128个和所有member的长度都小于64字节使用ziplist,其他核心使用skiplist。
参考:redis zset底层实现原理
未完待续。。。