网上弄来得面试题,学习一下,查缺补漏
a. 更多的数据结构;
b. 可持久化;
c. 计数器;
d. 发布-订阅功能;
e. 事务功能;
f. 过期回调功能;
g. 队列功能;
h. 排序、聚合查询功能。
a. RDB:快照形式是直接把内存中的数据保存到一个 dump 文件中,定时保存,保存策略。(会丢数据) b. AOF:把所有的对Redis的服务器进行修改的命令都存到一个文件里,命令的集合。(影响性能)
a. explain select语句;
b. 当只要1条数据时使用limit 1;
c. 为搜索字段建索引;
d. 避免select *;
e. 字段尽量使用 not null;
f. 垂直分割;
g. 拆分大的delete和insert语句:delete和insert会锁表;
h. 分表分库分区。
1、虚拟机:我们传统的虚拟机需要模拟整台机器包括硬件,每台虚拟机都需要有自己的操作系统,虚拟机一旦被开启,预分配给他的资源将全部被占用。,每一个虚拟机包括应用,必要的二进制和库,以及一个完整的用户操作系统。
2、Docker:容器技术是和我们的宿主机共享硬件资源及操作系统可以实现资源的动态分配。 容器包含应用和其所有的依赖包,但是与其他容器共享内核。容器在宿主机操作系统中,在用户空间以分离的进程运行。
3、对比:
1、这里同主机不同容器之间通信主要使用Docker桥接(Bridge)模式。 2、不同主机的容器之间的通信可以借助于 pipework 这个工具。
a. 消息处理过程:
b. 四种交换机: i. 直连交换机,Direct exchange:带路由功能的交换机,根据routing_key(消息发送的时候需要指定)直接绑定到队列,一个交换机也可以通过过个routing_key绑定多个队列。 ii. 扇形交换机,Fanout exchange:广播消息。 iii. 主题交换机,Topic exchange:发送到主题交换机上的消息需要携带指定规则的routing_key,主题交换机会根据这个规则将数据发送到对应的(多个)队列上。 iv. 首部交换机,Headers exchange:首部交换机是忽略routing_key的一种路由方式。路由器和交换机路由的规则是通过Headers信息来交换的,这个有点像HTTP的Headers。将一个交换机声明成首部交换机,绑定一个队列的时候,定义一个Hash的数据结构,消息发送的时候,会携带一组hash数据结构的信息,当Hash的内容匹配上的时候,消息就会被写入队列。
a. 一个队列可以绑定多个消费者;
b. 消息分发:若该队列至少有一个消费者订阅,消息将以循环(round-robin)的方式发送给消费者。每条消息只会分发给一个订阅的消费者(前提是消费者能够正常处理消息并进行确认)。
a. fanout交换器:它会把所有发送到该交换器的消息路由到所有与该交换器绑定的队列中; b. direct交换器:direct类型的交换器路由规则很简单,它会把消息路由到哪些BindingKey和RoutingKey完全匹配的 队列中; c. topic交换器:匹配规则比direct更灵活。 d. headers交换器:根据发送消息内容的headers属性进行匹配(由于性能很差,不实用)。
a. 场景:
i. 大数据部门流数据处理;
ii. elk;
b. 特性:
i. 它被设计为一个分布式系统,易于向外扩展;
ii. 它同时为发布和订阅提供高吞吐量;
iii. 它支持多订阅者,当失败时能自动平衡消费者;
iv. 它将消息持久化到磁盘,因此可用于批量消费,例如ETL,以及实时应用程序。
a. 中大型公司首选RabbitMQ:管理界面简单,高并发。 b. 大型公司可以选择RocketMQ:更⾼并发,可对rocketmq进⾏定制化开发。 c. 日志采集功能,首选kafka,专为大数据准备。
springcloud由以下几个核心组件构成: a. Eureka:各个服务启动时,Eureka Client都会将服务注册到Eureka Server,并且Eureka Client还可以反过来从Eureka Server拉取注册表,从而知道其他服务在哪里 b. Ribbon:服务间发起请求的时候,基于Ribbon做负载均衡,从一个服务的多台机器中选择一台 c. Feign:基于Feign的动态代理机制,根据注解和选择的机器,拼接请求URL地址,发起请求 d. Hystrix:发起请求是通过Hystrix的线程池来走的,不同的服务走不同的线程池,实现了不同服务调用的隔离,避免了服务雪崩的问题 e. Zuul:如果前端、移动端要调用后端系统,统一从Zuul网关进入,由Zuul网关转发请求给对应的服务
a. 当一个服务实例启动,会将它的ip地址等信息注册到eureka;
b. 当a服务调用b服务,a服务会通过Ribbon检查本地是否有b服务实例信息的缓存;
c. Ribbon会定期从eureka刷新本地缓存。
a. 某个服务不可用时,各个Eureka Client不能及时的知道,需要1~3个心跳周期才能感知,但是,由于基于Netflix的服务调用端都会使用Hystrix来容错和降级,当服务调用不可用时Hystrix也能及时感知到,通过熔断机制来降级服务调 用,因此弥补了基于客户端服务发现的时效性的缺点。
a. 第一层缓存:readOnlyCacheMap,本质上是ConcurrentHashMap:这是⼀个JVM的CurrentHashMap只读缓存,这个主要是为了供客户端获取注册信息时使用,其缓存更新,依赖于定时器的更新,通过和readWriteCacheMap 的值做对比,如果数据不一致,则以readWriteCacheMap 的数据为准。readOnlyCacheMap 缓存更新的定时器时间间隔,默认为30秒
b. 第二层缓存:readWriteCacheMap,本质上是Guava缓存:此处存放的是最终的缓存, 当服务下线,过期,注册,状态变更,都会来清除这个缓存里面的数据。 然后通过CacheLoader进行缓存加载,在进行 readWriteCacheMap.get(key)的时候,首先看这个缓存里面有没有该数据,如果没有则通过CacheLoader的load方法去加载,加载成功之后将数据放入缓存,同时返回数据。 readWriteCacheMap 缓存过期时间,默认为 180 秒 。
c. 缓存机制:设置了一个每30秒执行一次的定时任务,定时去服务端获取注册信息。获取之后,存入本地内存。
a. 区别:
-- RPC,可以基于TCP协议,也可以基于HTTP协议 -- HTTP,基于HTTP协议
-- RPC,使用自定义的TCP协议,可以让请求报文体积更小,或者使用HTTP2协议,也可以很好的减少报文的体积,提高传输效率 -- HTTP,如果是基于HTTP1.1的协议,请求中会包含很多无用的内容,如果是基于HTTP2.0,那么简单的封装以下是可以作为一个RPC来使用的,这时标准RPC框架更多的是服务治理
-- RPC,可以基于thrift实现高效的二进制传输 -- HTTP,大部分是通过json来实现的,字节大小和序列化耗时都比thrift要更消耗性能
-- RPC,基本都自带了负载均衡策略 -- HTTP,需要配置Nginx,HAProxy来实现
-- RPC,能做到自动通知,不影响上游 -- HTTP,需要事先通知,修改Nginx/HAProxy配置 b. 总结:RPC主要用于公司内部的服务调用,性能消耗低,传输效率高,服务治理方便。HTTP主要用于对外的异构环境,浏览器接口调用,APP接口调用,第三方接口调用等。
1. 二阶段提交: a. 概念:参与者将操作成败通知协调者,再由协调者根据所有参与者的反馈情报决定各参与者是否要提交操作还是中止操作。 b. 作用:主要保证了分布式事务的原子性;第一阶段为准备阶段,第二阶段为提交阶段; c. 缺点:不仅要锁住参与者的所有资源,而且要锁住协调者资源,开销大。一句话总结就是:2PC效率很低,对高并发很不友好。
2. 三阶段提交:
a. 概念:三阶段提交协议在协调者和参与者中都引用超时机制,并且把两阶段提交协议的第一个阶段拆分成了两步:询问,然后再锁资源,最后真正提交。这样三阶段提交就有CanCommit、PreCommit、DoCommit三个阶段。
b. 缺点:如果进行PreCommit后,Coordinator发出的是abort请求,假设只有一个Cohort收到并进行了abort操作,而其他对于系统状态未知的Cohort会根据3PC选择继续Commit,此时系统状态发生不一致性。
1. 柔性事务: a. 概念:所谓柔性事务是相对强制锁表的刚性事务而言。流程入如下:服务器A的事务如果执行顺利,那么事务A就先行提交,如果事务B也执行顺利,则事务B也提交,整个事务就算完成。但是如果事务B执行失败,事务B本身回滚,这时事务A已经被提交,所以需要执行一个补偿操作,将已经提交的事务A执⾏的操作作反操作,恢复到未执行前事务A的状态。 b. 缺点:业务侵入性太强,还要补偿操作,缺乏普遍性,没法大规模推广。
2. 消息最终一致性解决方案之RabbitMQ实现: a. 实现:发送方确认+消息持久化+消费者确认。
a. 优点: i. 模块解耦:把模块拆分,使用接口通信,降低模块之间的耦合度. ii. 项目拆分,不同团队负责不同的子项目:把项目拆分成若干个子项目,不同的团队负责不同的子项目. iii. 提高项目扩展性:增加功能时只需要再增加一个子项目,调用其他系统的接口就可以。 iv. 分布式部署:可以灵活的进行分布式部署. v. 提高代码的复用性:比如service层,如果不采用分布式rest服务方式架构就会在手机wap商城,微信商城,pc,android,ios每个端都要写一个service层逻辑,开发量大,难以维护一起升级,这时候就可以采用分布式rest服务方式,公用一个service层。 b. 缺点: i. 系统之间的交互要使用远程通信,接口开发增大工作量; ii. 网络请求有延时; iii. 事务处理比较麻烦,需要使用分布式事务。
a. 线程私有;
b. 栈由一系列帧组成(因此java栈也叫做帧栈);
c. 帧保存一个方法的局部变量(局部变量表)、操作数栈、常量池指针;
d. 每一次方法调用创建一个帧,并压栈。
1.BootStrap ClassLoader 启动ClassLoader
2.Extension ClassLoader 扩展ClassLoader
3.App ClassLoader 应用ClassLoader/系统ClassLoader
4.Custom ClassLoader 自定义ClassLoader 除了BootStrap ClassLoader,每个ClassLoader都有一个Parent作为父亲。
(1).自底向上检查类是否已经加载;
(2).自顶向下尝试加载类。
5、双亲委派机制:当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,每一个层次类加载器都是如此,因此所有的加载请求都应该传送到启动类加载其中,只有当父类加载器反馈自己无法完成这个请求的时候(在它的加载路径下没有找到所需加载的Class),子类加载器才会尝试自己去加载。
a. 加载过程:通过一个类的全限定名来获取定义此类的二进制字节流,将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。在内存中(方法区)生成这个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口;
b. 验证过程:为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,文件格式验证、元数据验证、字节码验证、符号引用验证;
c. 准备过程:正式为类属性分配内存并设置类属性初始值的阶段,这些内存都将在方法区中进行分配; 准备阶段,static对象会被设置默认值,static final对象会被赋上给予的值。
d. 解析阶段:虚拟机将常量池内的符号引用替换为直接引用的过程。
i. 符号引用:字符串,引用对象不一定被加载;
ii. 直接引用:指针或者地址偏移量,引用对象一定在内存中。
e. 初始化阶段:类初始化阶段是类加载过程的最后一步。初始化阶段就是执行类构造器()方法的过程。
f. 使用阶段:
g. 卸载阶段:
-- 年轻代:-XX:+UseParNewGC。 -- 老年代:-XX:+UseConcMarkSweepGC。 -- CMS可以将STW时间降到最低,但是不对内存进行压缩,有可能出现“并行模式失败”。比如老年代空间还有300MB空间,但是一些10MB的对象方法被顺序的存储。这时候会触发压缩处理,但是CMS GC模式下的压缩处理时间要比Parallel GC长很多。 -- G1采用”标记-整理“算法,解决了内存碎片问题,建立了可预测的停顿时间类型,能让使用者指定在一个长度为M毫秒的时间段内,消耗在垃圾收集上的时间不得超过N毫秒。
指的是 java.util.concurrent 路径下的类 需要学习一下。
这篇文章说得挺好的,很透彻。
Copyright: 采用 知识共享署名4.0 国际许可协议进行许可