面试就是这么简单,offer拿到手软(一)—— 常见非技术问题回答思路
面试就是这么简单,offer拿到手软(二)—— 常见65道非技术面试问题
面试就是这么简单,offer拿到手软(三)—— 常见中间件框架面试题,es,redis,dubbo,zookeeper kafka 等
面试就是这么简单,offer拿到手软(四)—— 常见java152道基础面试题
常见消息队列:activemq、rabbitmq、rocketmq、kafka
消息队列的优点: 解耦、异步、削峰
消息队列的优点: 系统可用性降低、系统复杂性提高、一致性问题
elasticsearch 即 es
es存储数据的基本单位是索引index
index -> type -> mapping -> document -> field
1个index能被分成多个shard,分布在不同的机器上,shard类比kafka,有主从性(备份)
写只能主,读可以主从
写入内存buffer和translog
为了高性能和高并发使用缓存(使用场景:数据字典)
1)缓存与数据库双写不一致
2)缓存雪崩
3)缓存穿透
4)缓存并发竞争
1)Redis支持服务器端的数据操作:
Redis相比Memcached拥有更多的数据结构和并支持更丰富的数据操作,通常在Memcached里,你需要将数据拿到客户端来进行类似的修改再set回去。
这大大增加了网络IO的次数和数据体积。在Redis中,这些复杂的操作通常和一般的GET/SET一样高效。
2)集群模式:memcached没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;但是redis目前是原生支持cluster模式的
String、Hash、list、set、zset
主从架构、读写分离、水平扩容
哨兵sentinel机制
redis集群数据丢失问题:
1)异步复制
2)集群脑裂
min-slaves-to-write 1
min-slaves-max-log 10
选举:
slaves priority优先级 -> replica offset -> run id
用于故障恢复
持久化方案:
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持久化即可
但:
时间间隔问题,数据不全
redis cluster支撑N个redis master node,每个master都可挂载多个slave node
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来实现
分布式锁+时间戳
第一层: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都异步的通知监控中心
1)dubbo协议
单一长连接,NIO异步通信,基于hessian作为序列化协议;适用的场景就是:传输数据量很小(每次请求在100kb以内),但是并发量很高
2)rmi协议
走java二进制序列化,多个短连接,适合消费者和提供者数量差不多,适用于文件的传输
3)hessian协议
走hessian序列化协议,多个短连接,适用于提供者数量比消费者数量还多,适用于文件的传输
4)http协议
走json序列化
5)webservice
走SOAP文本序列化
1)random loadbalance 权重
2)roundrobin loadbalance 轮询
3)leastactive loadbalance 自动感知
4)consistanthash loadbalance 一致性hash算法
1)failover cluster模式
失败自动切换,自动重试其他机器,默认就是这个,常见于读操作
2)failfast cluster模式
一次调用失败就立即失败,常见于写操作
3)failsafe cluster模式
出现异常时忽略掉,常用于不重要的接口调用,比如记录日志
4)failbackc cluster模式
失败了后台自动记录请求,然后定时重发,比较适合于写消息队列这种
5)forking cluster
并行调用多个provider,只要一个成功就立即返回
6)broadcacst cluster
逐个调用所有的provider
默认使用javassist动态字节码生成,创建代理类,但是可以通过spi扩展机制配置自己的动态代理策略
注册中心 -> 动态代理 -> 负载均衡 -> 网络通信
1)分布式协调
2)分布式锁
3)元数据/配置信息管理
4)HA高可用性
redis实现 -> 叫做RedLock算法,是redis官方支持的分布式锁算法
互斥(只能有一个客户端获取锁),不能死锁,容错(大部分redis节点存活这个锁就可以加可以释放)
1)第一个最普通的实现方式,如果就是在redis里创建一个key算加锁
创建锁 SET my:lock 随机值 NX PX 30000
释放锁 一般可以用lua脚本删除,判断value一样才删除
2)RedLock算法
使用redis cluster集群,为避免上一方法redis宕机问题
zookeeper实现
zookeeper保证只有一个人获取到锁(创建临时节点),某一线程获取到一个锁后执行一定的操作后释放锁,其他线程如果没有获取到这个
锁就对这个锁注册一个监听器,感知到锁被释放后再次重新尝试取锁
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分法(按时间分) 扩容快,但是大部分的请求,都是访问最新的数据
哈希分法(以某一字段取模分) 可以平均分配给库的数据量和请求压力,但扩容麻烦
垂直拆分:把一个有很多字段的表给拆分成多个表,或者是多个库上去
水平拆分:一个表的数据给弄到多个库的多个表里去,但是每个库的表结构都一样,只不过每个库表放的数据是不同的,所有库表的数据加起来就是全部数据
不停机迁移分库分表:
双写迁移方案
基于主从复制架构,简单来说,就搞一个主库,挂多个从库,然后我们就单单只是写主库,然后主库会自动把数据给同步到从库上去。
主库将变更写binlog日志,然后从库连接到主库之后,从库有一个IO线程,将主库的binlog日志拷贝到自己本地,写入一个中继日志中。
接着从库中有一个SQL线程会从中继日志读取binlog,然后执行binlog日志中的内容,也就是在自己本地再次执行一遍SQL。
mysql实际上在这一块有两个机制,一个是半同步复制,用来解决主库数据丢失问题;一个是并行复制,用来解决主从同步延时问题。
1)分库,将一个主库拆分为4个主库,每个主库的写并发就500/s,此时主从延迟可以忽略不计
2)打开mysql支持的并行复制,多个库并行复制,如果说某个库的写入并发就是特别高,单库写并发达到了2000/s,并行复制还是没意义
3)重写代码,插入数据之后,直接就更新,不要查询
4)如果确实是存在必须先插入,立马要求就查询到,然后立马就要反过来执行一些操作,对这个查询设置直连主库。不推荐这种方法,你这么搞导致读写分离的意义就丧失了