面试就是这么简单,offer拿到手软(一)—— 常见非技术问题回答思路
面试就是这么简单,offer拿到手软(二)—— 常见65道非技术面试问题
面试就是这么简单,offer拿到手软(三)—— 常见中间件框架面试题,es,redis,dubbo,zookeeper kafka 等
面试就是这么简单,offer拿到手软(四)—— 常见java152道基础面试题
文章目录
- 一、消息队列
- 1.1 常见消息队列优缺点
- 1.2如何保证消息队列高可用?
- 1.2.1 使用kafka使用集群模式
- 1.2.2 确保不重复消费
- 1.2.3 确保消息可靠性传输
- 1.2.4 如何保证保证消息顺序性
- 1.2.5 如何设计消息中间件
- 二、分布式搜索引擎
- 2.1. es分布式架构原理
- 2.2. es读写流程原理
- 2.3. es优化
- 三、分布式缓存redis
- 3.1 为什么要用分布式缓存?
- 3.2 常见问题
- 3.3 redis介绍以及与memcached的区别
- 3.3.1. redis是单线程工作模型
- 3.3.2. redis和memcached 区别
- 3.3.3. redis高可用原因
- 3.3.4. redis数据:
- 3.3.5 .redis过期策略
- 3.3.6. redis高并发高可用的保证
- 3.3.7 .redis持久化
- 3.3.8. redis横向扩容
- 3.3.9 一致性hash算法(有虚拟节点,为解决热点数据)
- 3.3.10 redis cluster,hash slot算法
- 3.3.11 缓存雪崩、穿透
- 3.3.12 数据库双写不一致
- 3.3.13 redis并发竞争
- 四、dubbo:
- 4.1 dubbo工作原理
- 4.2. 支持协议
- 4.3.dubbo负载均衡策略
- 4.4.集群容错
- 4.5.动态代理策略
- 4.6 自设计rpc框架
- 五、zookeeper
- 5.1. 适用场景
- 5.2.分布式锁
- 六、分布式session
- 七、分布式事务
- 八、设计一个高并发的系统架构
- 九、分库分表
- 十、读写分离、主从复制、同步延时问题
- 10.1. 读写分离
- 10.2.主从复制
- 10.3.主从同步机制
- 10.4.同步延时问题
一、消息队列
1.1 常见消息队列优缺点
常见消息队列:activemq、rabbitmq、rocketmq、kafka
消息队列的优点: 解耦、异步、削峰
消息队列的优点: 系统可用性降低、系统复杂性提高、一致性问题
1.2如何保证消息队列高可用?
1.2.1 使用kafka使用集群模式
1.2.2 确保不重复消费
- 使用offset序号(zk实现)
- 保证幂等性(使用数据库表主键)
1.2.3 确保消息可靠性传输
- 如何解决消费端弄丢问题?
关闭自动提交offset,改为手动提交offset - 如何解决kafka本身弄丢的问题?
leader宕掉
topic设置replication.factor值大于1,要求partition必须至少2个副本
kafka服务端设置min.insync.replicas值大于1,要求leader至少感知到有至少一个follower跟自己保持联系
producer(生产者)端设置acks=all,每条数据必须是写入所有replica后,才认为写入成功
producer端设置retries=MAX,一旦写入失败,无限重试,卡在这里
1.2.4 如何保证保证消息顺序性
- kafka保证写入一个partition中的数据是一定有顺序的,生产者指定的一个key的数据一定会写入到一个partition中
- 消费者从partition中取出数据也是一定有顺序的
- 多线程处理时可能会顺序出错,设定内存队列,hash分发时,同一key分到同一队列
1.2.5 如何设计消息中间件
- 支持扩容
- 数据落磁盘
- 可用性
- 数据可靠性
二、分布式搜索引擎
elasticsearch 即 es
2.1. es分布式架构原理
es存储数据的基本单位是索引index
index -> type -> mapping -> document -> field
1个index能被分成多个shard,分布在不同的机器上,shard类比kafka,有主从性(备份)
写只能主,读可以主从
2.2. es读写流程原理
写入内存buffer和translog
- buffer快满了或一定时间后,将buffer中数据refresh到一个新的segment file中(先进入到os cache,一般1s执行一次)
- refresh持续执行后,当translog达到一定体量时,触发commit操作(buffer中现有数据全部refresh到os cache中,清空buffer,将一个commit point写入磁盘文件,标识对应的segment file,将os cache中数据fsync到磁盘)
- 可以调用api手动执行flush操作(整个commit过程即flush)
- translog也是先进入到os cache中,默认5s持久化操作一次
- 删除操作,标识del标记,逻辑删除,非物理删除
- 更新操作,即先标记原有数据del,重新写入一条数据
- 定期执行merge操作,当segment file多到一定程度的时候,es就会自动触发merge操作,将多个segment file给merge成一个segment file
2.3. es优化
- 加大分配给es的内存(数据量的体量最好小于或等于分配给es的内存)
- 数据预热,对于大量搜索的数据,定时的查询一次,将数据存入到es内存中
- 优化存入filesystem cache的数据,只存入用于搜索的数据
- 冷热分离,尽可能的将热数据放到一个索引,冷数据放到另一个索引中去,防止热数据被冷数据从cache中冲掉
三、分布式缓存redis
3.1 为什么要用分布式缓存?
为了高性能和高并发使用缓存(使用场景:数据字典)
3.2 常见问题
1)缓存与数据库双写不一致
2)缓存雪崩
3)缓存穿透
4)缓存并发竞争
3.3 redis介绍以及与memcached的区别
3.3.1. redis是单线程工作模型
3.3.2. redis和memcached 区别
1)Redis支持服务器端的数据操作:
Redis相比Memcached拥有更多的数据结构和并支持更丰富的数据操作,通常在Memcached里,你需要将数据拿到客户端来进行类似的修改再set回去。
这大大增加了网络IO的次数和数据体积。在Redis中,这些复杂的操作通常和一般的GET/SET一样高效。
2)集群模式:memcached没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;但是redis目前是原生支持cluster模式的
3.3.3. redis高可用原因
- 非阻塞IO多路复用模型
- 纯内存操作
- 避免了多线程的频繁上下文切换问题
3.3.4. redis数据:
String、Hash、list、set、zset
3.3.5 .redis过期策略
- 定期删除+惰性删除
- 内存淘汰
1)noeviction:当内存不足以容纳新写入数据时,新写入操作会报错
2)allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个是最常用的)
3)allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key
4)volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key(这个一般不太合适)
5)volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key
6)volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除
3.3.6. redis高并发高可用的保证
主从架构、读写分离、水平扩容
哨兵sentinel机制
redis集群数据丢失问题:
1)异步复制
2)集群脑裂
min-slaves-to-write 1
min-slaves-max-log 10
选举:
slaves priority优先级 -> replica offset -> run id
3.3.7 .redis持久化
用于故障恢复
持久化方案:
AOF:每条数据写入一个AOF文件内,适合做热备
当AOF文件膨胀到一定体量时,会触发rewrite操作,基于现有redis数据生成一份新的AOF文件,并将原有AOF文件清除
一般AOF会每隔1秒,通过一个后台线程执行一次fsync操作,最多丢失1秒钟的数据
AOF日志文件以append-only模式写入,所以没有任何磁盘寻址的开销,写入性能非常高,而且文件不容易破损,即使文件尾部破损,也很容易修复
但:
AOF日志文件通常比RDB数据快照文件更大
AOF开启后,支持的写QPS会比RDB支持的写QPS低
做数据恢复的时候,会比较慢
RDB:每隔一定时间,生成一个快照,适合做冷备
RDB对redis对外提供的读写服务,影响非常小,可以让redis保持高性能,因为redis主进程只需要fork一个子进程,让子进程执行磁盘IO操作来进行RDB持久化即可
但:
时间间隔问题,数据不全
3.3.8. redis横向扩容
redis cluster支撑N个redis master node,每个master都可挂载多个slave node
3.3.9 一致性hash算法(有虚拟节点,为解决热点数据)
3.3.10 redis cluster,hash slot算法
cluster有固定的16384个hash slot,对每个key计算CRC16值,然后对16384取模,可以获取key对应的hash slot
redis cluster中每个master都会持有部分slot,增加一个master,就将其他master的hash slot移动部分过去,减少一个master,就将它的hash slot移动到其他master上去
移动hash slot的成本是非常低的,客户端的api,可以对指定的数据,让他们走同一个hash slot,通过hash tag来实现
3.3.11 缓存雪崩、穿透
3.3.12 数据库双写不一致
3.3.13 redis并发竞争
分布式锁+时间戳
四、dubbo:
4.1 dubbo工作原理
第一层:service层,接口层,给服务提供者和消费者来实现的
第二层:config层,配置层,主要是对dubbo进行各种配置的
第三层:proxy层,服务代理层,透明生成客户端的stub和服务单的skeleton
第四层:registry层,服务注册层,负责服务的注册与发现
第五层:cluster层,集群层,封装多个服务提供者的路由以及负载均衡,将多个实例组合成一个服务
第六层:monitor层,监控层,对rpc接口的调用次数和调用时间进行监控
第七层:protocol层,远程调用层,封装rpc调用
第八层:exchange层,信息交换层,封装请求响应模式,同步转异步
第九层:transport层,网络传输层,抽象mina和netty为统一接口
第十层:serialize层,数据序列化层
工作流程:
1)第一步,provider向注册中心去注册
2)第二步,consumer从注册中心订阅服务,注册中心会通知consumer注册好的服务
3)第三步,consumer调用provider
4)第四步,consumer和provider都异步的通知监控中心
4.2. 支持协议
1)dubbo协议
单一长连接,NIO异步通信,基于hessian作为序列化协议;适用的场景就是:传输数据量很小(每次请求在100kb以内),但是并发量很高
2)rmi协议
走java二进制序列化,多个短连接,适合消费者和提供者数量差不多,适用于文件的传输
3)hessian协议
走hessian序列化协议,多个短连接,适用于提供者数量比消费者数量还多,适用于文件的传输
4)http协议
走json序列化
5)webservice
走SOAP文本序列化
4.3.dubbo负载均衡策略
1)random loadbalance 权重
2)roundrobin loadbalance 轮询
3)leastactive loadbalance 自动感知
4)consistanthash loadbalance 一致性hash算法
4.4.集群容错
1)failover cluster模式
失败自动切换,自动重试其他机器,默认就是这个,常见于读操作
2)failfast cluster模式
一次调用失败就立即失败,常见于写操作
3)failsafe cluster模式
出现异常时忽略掉,常用于不重要的接口调用,比如记录日志
4)failbackc cluster模式
失败了后台自动记录请求,然后定时重发,比较适合于写消息队列这种
5)forking cluster
并行调用多个provider,只要一个成功就立即返回
6)broadcacst cluster
逐个调用所有的provider
4.5.动态代理策略
默认使用javassist动态字节码生成,创建代理类,但是可以通过spi扩展机制配置自己的动态代理策略
4.6 自设计rpc框架
注册中心 -> 动态代理 -> 负载均衡 -> 网络通信
五、zookeeper
5.1. 适用场景
1)分布式协调
2)分布式锁
3)元数据/配置信息管理
4)HA高可用性
5.2.分布式锁
redis实现 -> 叫做RedLock算法,是redis官方支持的分布式锁算法
互斥(只能有一个客户端获取锁),不能死锁,容错(大部分redis节点存活这个锁就可以加可以释放)
1)第一个最普通的实现方式,如果就是在redis里创建一个key算加锁
创建锁 SET my:lock 随机值 NX PX 30000
释放锁 一般可以用lua脚本删除,判断value一样才删除
2)RedLock算法
使用redis cluster集群,为避免上一方法redis宕机问题
zookeeper实现
zookeeper保证只有一个人获取到锁(创建临时节点),某一线程获取到一个锁后执行一定的操作后释放锁,其他线程如果没有获取到这个
锁就对这个锁注册一个监听器,感知到锁被释放后再次重新尝试取锁
六、分布式session
1.tomcat + redis
在tomcat配置文件配RedisSessionManager属性
2.spring session + redis
spring-session-data-redis.jar
jedis.jar
七、分布式事务
1.两阶段提交方案(XA方案)
有一个事务管理器,先询问后执行
2.tcc方案(try、confirm、cancel)
1)Try阶段:对各个服务的资源做检测以及对资源进行锁定或者预留
2)Confirm阶段:在各个服务中执行实际的操作
3)Cancel阶段:业务方法执行出错,那么这里就需要进行补偿,执行已经执行成功的业务逻辑的回滚操作
3.本地消息表
通过zookeeper、mq和数据库来做,数据库中有个业务表和一个消息表
4.可靠消息最终一致性
基于mq实现,阿里的rocketMQ
1)A系统先发送一个prepared消息到mq,如果这个prepared消息发送失败那么就直接取消操作不执行
2)如果这个消息发送成功,那么接着执行本地事务,如果成功,向mq发送确认消息,如果失败就告诉mq回滚消息
3)如果发送了确认消息,那么此时B系统会接收到确认消息,然后执行本地的事务
4)mq会自动定时轮询所有prepared消息回调你的接口
5)如果系统B的事务失败了,自动不断重试直到成功
5.最大努力通知
1)系统A本地事务执行完之后,发送个消息到MQ
2)这里会有个专门消费MQ的最大努力通知服务,这个服务会消费MQ然后写入数据库中记录下来,或者是放入个内存队列也可以,接着调用系统B的接口
3)要是系统B执行成功就ok了;要是系统B执行失败了,那么最大努力通知服务就定时尝试重新调用系统B,反复N次,最后还是不行就放弃
八、设计一个高并发的系统架构
1.系统拆分
2.使用缓存
3.使用mq
4.分库分表
5.读写分离
6.es
九、分库分表
分库分表中间件: cobar、TDDL、atlas、sharding-jdbc、mycat
range分法(按时间分) 扩容快,但是大部分的请求,都是访问最新的数据
哈希分法(以某一字段取模分) 可以平均分配给库的数据量和请求压力,但扩容麻烦
垂直拆分:把一个有很多字段的表给拆分成多个表,或者是多个库上去
水平拆分:一个表的数据给弄到多个库的多个表里去,但是每个库的表结构都一样,只不过每个库表放的数据是不同的,所有库表的数据加起来就是全部数据
不停机迁移分库分表:
双写迁移方案
十、读写分离、主从复制、同步延时问题
10.1. 读写分离
基于主从复制架构,简单来说,就搞一个主库,挂多个从库,然后我们就单单只是写主库,然后主库会自动把数据给同步到从库上去。
10.2.主从复制
主库将变更写binlog日志,然后从库连接到主库之后,从库有一个IO线程,将主库的binlog日志拷贝到自己本地,写入一个中继日志中。
接着从库中有一个SQL线程会从中继日志读取binlog,然后执行binlog日志中的内容,也就是在自己本地再次执行一遍SQL。
10.3.主从同步机制
mysql实际上在这一块有两个机制,一个是半同步复制,用来解决主库数据丢失问题;一个是并行复制,用来解决主从同步延时问题。
10.4.同步延时问题
1)分库,将一个主库拆分为4个主库,每个主库的写并发就500/s,此时主从延迟可以忽略不计
2)打开mysql支持的并行复制,多个库并行复制,如果说某个库的写入并发就是特别高,单库写并发达到了2000/s,并行复制还是没意义
3)重写代码,插入数据之后,直接就更新,不要查询
4)如果确实是存在必须先插入,立马要求就查询到,然后立马就要反过来执行一些操作,对这个查询设置直连主库。不推荐这种方法,你这么搞导致读写分离的意义就丧失了