一:网络编程
HTPT1 与HTTP2的主要区别
http1.0:默认长连接,支持发送header信息,http1.1支持Host域
http2.0:
TCPIP模型与协议
BIO NIO AIO
RPC与HTTP服务的区别
Tcp三次握手,四次挥手
HTTPS和HTTP的区别主要如下:
加密方式
两者区别
一、Nginx负载均衡算法
二:数据库原理
数据库遇到性能问题如何处理,
(1) 硬件提升:容量不足,磁盘不足,使用share memory
(2) 软件提升:性能不够,可以使用分库,分表,读写分离
(3)分布式部署:一个事务,两个数据库联动。使用分布式事务。
MYISAM与innodb搜索引擎原理
索引类型
索引的好处
索引的弊端
判断是否应该建索引的条件
主键选择
分表分库
事务的四大特性:
隔离级别
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
读未提交(read-uncommitted) | 是 | 是 | 是 |
不可重复读(read-committed) | 否 | 是 | 是 |
可重复读(repeatable-read) | 否 | 否 | 是 |
串行化(serializable) | 否 | 否 | 否 |
MVCC多版本并发控制技术
MVCC(multi-Version Concurrency Control):即多版本并发控制技术,它使得大部分支持行锁的事务引擎,不再单纯的使用行锁来进行数据库的并发控制,取而代之的是把数据库的行锁与行的多个版本结合起来,只需要很小的开销,就可以实现非锁定读,从而大大提高数据库系统的并发性能,InnoDB的MVCC,通过在每行记录后面保存两个隐藏的列来实现:一个保存了行的创建时间,一个保存行的过期时间(删除时间),当然,这里的时间并不是时间戳,而是系统版本号,每开始一个新的事务,系统版本号就会递增。在RR隔离级别下,MVCC的操作如下:
事务的二段提交机制(2PC)
请求阶段(commit-request phase,或称表决阶段,voting phase)在请求阶段,协调者将通知事务参与者准备提交或取消事务,然后进入表决过程。在表决过程中,参与者将告知协调者自己的决策:同意(事务参与者本地作业执行成功)或取消(本地作业执行故障)
提交阶段(commit phase)在该阶段,协调者将基于第一个阶段的投票结果进行决策:提交或取消。当且仅当所有的参与者同意提交事务协调者才通知所有的参与者提交事务,否则协调者将通知所有的参与者取消事务。参与者在接收到协调者发来的消息后将执行响应的操作。
两阶段提交的缺点
两阶段提交无法解决的问题
当协调者出错,同时参与者也出错时,两阶段无法保证事务执行的完整性。
考虑协调者再发出commit消息之后宕机,而唯一接收到这条消息的参与者同时也宕机了。
那么即使协调者通过选举协议产生了新的协调者,这条事务的状态也是不确定的,没人知道事务是否被已经提交。
事务的三段提交机制(3PC)
三阶段提交协议和两阶段提交协议的不同
对于协调者(Coordinator)和参与者(Cohort)都设置了超时机制(在2PC中,只有协调者拥有超时机制,即如果在一定时间内没有收到cohort的消息则默认失败)。
在2PC的准备阶段和提交阶段之间,插入预提交阶段,使3PC拥有CanCommit、PreCommit、DoCommit三个阶段。
PreCommit是一个缓冲,保证了在最后提交阶段之前各参与节点的状态是一致的。
三阶段提交协议的缺点
如果进入PreCommit后,Coordinator发出的是abort请求,假设只有一个Cohort收到并进行了abort操作,
而其他对于系统状态未知的Cohort会根据3PC选择继续Commit,此时系统状态发生不一致性。
BASE是指基本可用(Basically Available)、软状态( Soft State)、最终一致性( Eventual Consistency)。
基本可用(Basically Available)
基本可用是指分布式系统在出现故障的时候,允许损失部分可用性,即保证核心可用。
电商大促时,为了应对访问量激增,部分用户可能会被引导到降级页面,服务层也可能只提供降级服务。这就是损失部分可用性的体现。
软状态( Soft State)
软状态是指允许系统存在中间状态,而该中间状态不会影响系统整体可用性。分布式存储中一般一份数据至少会有三个副本,允许不同节点间副本同步的延时就是软状态的体现。mysql replication的异步复制也是一种体现。
最终一致性( Eventual Consistency)
最终一致性是指系统中的所有数据副本经过一定时间后,最终能够达到一致的状态。弱一致性和强一致性相反,最终一致性是弱一致性的一种特殊情况。
BASE模型是传统ACID模型的反面,不同与ACID,BASE强调牺牲高一致性,从而获得可用性,数据允许在一段时间内的不一致,只要保证最终一致就可以了。
ACID是传统数据库常用的设计理念,追求强一致性模型。
关系数据库的ACID模型拥有 高一致性 + 可用性 很难进行分区:
Atomicity原子性:一个事务中所有操作都必须全部完成,要么全部不完成。
Consistency一致性. 在事务开始或结束时,数据库应该在一致状态。
Isolation隔离层. 事务将假定只有它自己在操作数据库,彼此不知晓。
Durability. 一旦事务完成,就不能返回。
ACID模型要求一个事物必须满足上面的四点,这是对关系型传统数据库的指导性依据。而非关系型数据库NoSql则不再依赖这一模型。
2000年7月,加州大学伯克利分校的Eric Brewer教授在ACM PODC会议上提出CAP猜想。2年后,麻省理工学院的Seth Gilbert和Nancy Lynch从理论上证明了CAP。之后,CAP理论正式成为分布式计算领域的公认定理。
CAP理论为:
一个分布式系统最多只能同时满足
join原理
面试题
系统允许一段时间,数据量已经很大,这时候系统升级,有张表A需要增加个字段,并发量百天晚上都很大,请问在, 修改表结构。
考点:修改表结构会导致表锁,数据量大修改数据时间很长会导致用户读锁,无法访问!
答:
1、MyIASM是非事务安全的,不支持事务,而InnoDB是事务安全的,支持事务这是最重要的区别(百度面试)
2、MyIASM锁的粒度是表级的,而InnoDB支持行级锁
3、MyIASM支持全文类型索引,而InnoDB不支持全文索引
4、MyIASM相对简单,效率上要优于InnoDB,小型应用可以考虑使用MyIASM
5、MyIASM表保存成文件形式,跨平台使用更加方便
三:多线程
线程池潜在宕机风险
使用Executors来创建要注意潜在宕机风险.其返回的线程池对象的弊端如下: FixedThreadPool和SingleThreadPoolPool : 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM. CachedThreadPool和ScheduledThreadPool : 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM.
综上所述, 在可能有大量请求的线程池场景中, 更推荐自定义ThreadPoolExecutor来创建线程池, 具体构造函数配置见下文.
线程池大小配置
一般根据任务类型进行区分, 假设CPU为N核 CPU密集型任务需要减少线程数量, 降低线程之间切换造成的开销, 可配置线程池大小为N + 1. IO密集型任务则可以加大线程数量, 可配置线程池大小为 N * 2. 混合型任务则可以拆分为CPU密集型与IO密集型, 独立配置.
线程安全问题
synchronized
Lock
synchronized缺点:
通过Lock就可以办到
AQS
Lock的两个常见的锁都是基于它来实现的
拥有两种线程模式
locks包下常用的类:
锁的相关概念介绍
CAS
CAS,在Java并发应用中通常指CompareAndSwap或CompareAndSet,即比较并交换。
死锁
死锁是指当一个线程永远地持有一个锁,并且其他线程都尝试获得这个锁时,那么它们将永远被阻塞。比如,线程1已经持有了A锁并想要获得B锁的同时,线程2持有B锁并尝试获取A锁,那么这两个线程将永远地等待下去
如何排查和检查死锁
可以使用JDK自带的 一些工具jps ,jstack,jconsole,jvisualvm
如何避免死锁
ConcurrentHashMap
线程池
阻塞队列
ThreadLocal
ThreadLocal,很多地方叫做线程本地变量,
原理:
首先在每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本(即T类型的变量)。
初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。
并发编程辅助类:
四:JVM
可以不可以自己写个String类
答案:不可以,因为 根据类加载的双亲委派机制,会去加载父类,父类发现冲突了String就不再加载了;
能否在加载类的时候,对类的字节码进行修改
答案:可以,使用Java探针技术,动态代理就是基于java 探针技术
类加载过程:
答案:类加载分为三个步骤:加载,连接,初始化;
jvm内存模型:
JVM会把内存划分为:方法区,堆,栈,本地方法栈,程序计数器
方法区:被线程共享的区域,在方法区中,存储了每个类的源数据,以及编译后的代码,对应的常量池也会被创建出来。
堆:被线程共享的,主要存储对象本身,
栈:java栈中存放的是 一个个栈帧,每个栈帧对应一个被调用的方法,在栈帧中包括局部变量表,操作数栈,还有一当前类的常量的引用,和方法返回地址,当方法执行完毕后,就会将栈帧出栈,
本地方法栈:与java栈类型,只不过java栈是执行java方法,而本地方法栈则是执行本地方法
程序计数器:是线程私用的,记录着每个线程的执行位置,并且互相不干扰
JVM内存溢出的情况
程序计数器是唯一一个在Java虚拟机规范中没有规定任何OOM(内存溢出:OutOfMemoryError)情况的区域
栈:
堆:
方法区:
垃圾回收算法
一:标记-清楚算法 (最基础的垃圾回收算法)
标记-清除算法分为两个阶段:标记阶段和清除阶段。
标记阶段的任务是标记出所有需要被回收的对象,清除阶段就是回收被标记的对象所占用的空间。
缺点:标记-清除算法容易实现,但问题是,容易产生内存碎片,碎片太多可能会导致后续过程中需要为大对象分配空间时无法找到足够的空间而提前触发新的一次垃圾收集动作。
二:复制算法:
将可用内存按容量划分为大小相等的两块,每次只使用其中一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题。
优点:复制(Copying)算法实现简单,运行高效且不容易产生内存碎片,
缺点:使能够使用的内存缩减到原来的一半。复制算法的效率跟存活对象数目多少有很大的关系,如果存活对象很多,那么Copying算法的效率将大大降低。
三:标记-整理算法
该算法标记阶段和标记-清除(Mark-Sweep)一样,但是在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存。
四:分代回收算法
jvm其实是将内存中的对象放在了不同的内存区域新生代和老年代,根据不同区域的特点,使用不同的垃圾啊回收策略
一:新生代的算法:采用了GC的复制算法,速度快,因为新生代一般是新对象,都是瞬态的用了可能很快被释放的对象。
二:老年代的算法 标记/整理算法,GC后会执行压缩,整理到一个连续的空间,这样就维护着下一次分配对象的指针,下一次对象分配就可以采用碰撞指针技术,将新对象分配在第一个空闲的区域。
jvm垃圾回收器:
JDK默认的垃圾回收器:并行垃圾回收器ParallelGC
垃圾回收器分类:
1串行垃圾回收器
Serial/Serial Old
它是一个单线程收集器,并且在它进行垃圾收集时,必须暂停所有用户线程。
Serial收集器是针对新生代的收集器,采用的是复制算法,Serial Old收集器是针对老年代的收集器,采用的是标记-整理算法。
优点是实现简单高效,缺点是会给用户带来停顿。
2并行垃圾回收器(吞吐量)
并行收集器:多条垃圾收集线程并行工作,而用户线程仍处于等待状态
ParNew:
ParNew收集器是Serial收集器的多线程版本,使用多个线程进行垃圾收集
Parallel Scavenge:
该收集器是一个新生代的多线程收集器(并行收集器),它在回收期间不需要暂停其他用户线程,其采用的是Copying复制算法,
Parallel Old:
该收集器是Parallel Scavenge收集器的老年代版本(并行收集器),使用多线程和Mark-Compact(标记-整理)算法。
3并发垃圾回收器
并发收集器:垃圾收集线程与用户线程一段时间内同时工作(不是并行,而是交替执行)
CMS(Concurrent Mark Sweep):
CMS收集器是一种以获取最短回收停顿为目标的收集器,它是一种并发收集器,采用的是Mark-Sweep(标记-清除)算法。
G1:
G1收集器是一款面向服务端应用的收集器,它能充分利用多CPU、多核环境。G1收集器是一款并行与并发收集器,并且它能建立可预测的停顿时间模型。
G1收集器是在JDK1.7开始可以设置使用,在JDK1.9时设置为默认垃圾收集器。G1收集器和其他收集器相比有以下特点
并行与并发:G1能充分利用多CPU、多核的硬件优势,来缩短Stop-The-World停顿时间
分代收集:和其他收集器相同,分代概念依然保留。G1收集器不需要其他收集器的配合就可以管理整个堆,可以根据不同的方式去处理新创建的对象、存活了一段时间的对象和熬过多次GC的对象。
空间整合:不同于CMS的标记-清理,G1采用的是标记-整理的方式来处理碎片化的空间。这种方式意味着G1收集器运作期间不会产生内存空间碎片,收集后提供规整的可用空间
可预测的停顿:G1相对于CMS另外一个更大的优势,就是可以设置可预测的停顿模型,能够使开发者明确指定在长度为M毫秒的时间片段内,消耗在垃圾收集器上的时间不能超过M毫秒。
G1提供了三种模式的垃圾回收
1、Minor GC 2、Mixed GC 3、Full GC
在G1当中把堆内存分成了一个个Region,新生代,老年代不再是物理上面的分割,而是逻辑上的分割
Minor GC
当eden区满的时候,会触发一次Minor GC,这种触发机制和之前的其他垃圾收集器差不多,都是将Eden区和其中一个Survivor区的存活对象拷贝到另外一个Survivor区或者晋升到Old 区域,然后清空Eden区和Survivor区。
Mixed GC
当越来越多的年轻代中的对象晋升到老年代,会触发一次混合的垃圾收集器。也就是除了回收老年代,也会回收年轻代。这里是一部分老年代,可以选择哪些老年代的对象需要收集。
Full GC
如果对象内存分配的过快,mixed gc来不及回收,老年代被填满,就会触发Full GC.G1的Full GC就是Serial Old gc, 会暂停工作线程。
G1收集器的大概的工作过程
1、初始标记,整个过程STW,标记了从GC Roots 的可达对象
2、并发标记 ,真个过程用户线程的垃圾回收线程共同执行,标记出GC Roots可达对象的关联对象,收集整个Region的存活对象。
3、最终标记,整个过程STW,标记出并发标记遗漏的,以及引用关系发生变化的存活对象。
4、筛选回收,垃圾清理过程,如果整个Region没有存活对象,将Region加入到存活列表当中。
新生代和老年代的区别(阿里面试官的题目):
**所谓的新生代和老年代是针对于分代收集算法来定义的,新生代又分为Eden和Survivor两个区。加上老年代就这三个区。数据会首先分配到Eden区 当中(当然也有特殊情况,如果是大对象那么会直接放入到老年代(大对象是指需要大量连续内存空间的java对象)。),当Eden没有足够空间的时候就会 触发jvm发起一次Minor GC。如果对象经过一次Minor GC还存活,并且又能被Survivor空间接受,那么将被移动到Survivor空 间当中。并将其年龄设为1,对象在Survivor每熬过一次Minor GC,年龄就加1,当年龄达到一定的程度(默认为15)时,就会被晋升到老年代 中了,当然晋升老年代的年龄是可以设置的。如果老年代满了就执行:Full GC 因为不经常执行,因此采用了 Mark-Compact算法清理
其实新生代和老年代就是针对于对象做分区存储,更便于回收等等**
如果遇到了Full GC如何处理
(a)看一下有没有大对象
(b)大的静态对象
(c) Dump 内存快照,进行分析
redis
(1) 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)
(2) 支持丰富数据类型,支持string,list,set,sorted set,hash
(3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行
(4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除
(1) memcached所有的值均是简单的字符串,Redis作为其替代者,支持更为丰富的数据类型
(2) redis的速度比memcached快很多
(3) redis可以持久化其数据
1)、存储方式
Memecache把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。
Redis有部份存在硬盘上,这样能保证数据的持久性。
2)、数据支持类型
Memcache对数据类型支持相对简单。
Redis有复杂的数据类型。
3)、使用底层模型不同
它们之间底层实现方式 以及与客户端之间通信的应用协议不一样。
Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。
4)value大小
redis最大可以达到1GB,而memcache只有1MB
(1) Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件
(a)Master写内存快照,save命令调度rdbSave函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以Master最好不要写内存快照。
(b)Master AOF持久化,如果不重写AOF文件,这个持久化方式对性能的影响是最小的,但是AOF文件会不断增大,AOF文件过大会影响Master重启的恢复速度。Master最好不要做任何持久化工作,包括内存快照和AOF日志文件,特别是不要启用内存快照做持久化,如果数据比较关键,某个Slave开启AOF备份数据,策略为每秒同步一次。
(c)Master调用BGREWRITEAOF重写AOF文件,AOF在重写的时候会占大量的CPU和内存资源,导致服务load过高,出现短暂服务暂停现象。
(2) 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次
(3) 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内
(4) 尽量避免在压力很大的主库上增加从库
(5) 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3…
这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。
相关知识:redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。
redis 提供 6种数据淘汰策略:
kafka
zk
分布式锁:
基于zookeeper瞬时有序节点实现的分布式锁,其主要逻辑如下:
* 客户端对某个功能加锁时,在zookeeper上与该功能对应的指定节点的目录下,生成一个唯一的瞬时有序节点。
* 判断是否获取锁的方式,只需要判断有序节点中序号最小的一个,如果最小的节点与当客户端记录节点号相同获得锁
* 当释放锁的时候,只需将这个瞬时节点删除即可。
RPC
原理:
RPC调用是基于TCP进行通信,首先,服务端需要交对外提供的服务进行注册,服务注册可以基于ZK来做,之后,调用端,将需要调用的服务对象进行代理,其实就是创建一个代理对象,当执行代理对象的方法时会调用代理的InvocationHandler的inovke方法,将服务类的全路径+类名,防范,和参数通过socket进行外出调用,服务端接到请求后,会到注册中将服务类取出,并执行指定的方法,最后将结果方法
spring
我们是在使用Spring框架的过程中,其实就是为了使用IOC,依赖注入,和AOP,面向切面编程,这两个是Spring的灵魂。
主要用到的设计模式有工厂模式和代理模式。
IOC是工厂模式参考:设计模式-工厂模式-场景以及优缺点-目的就是应对变化 (国江面试回答的)
AOP代理模式参考:设计模式-代理模式(Proxy)
参考:深入理解Java反射+动态代理
IOC就是典型的工厂模式,通过sessionfactory去注入实例。
AOP就是典型的代理模式的体现。
可以参考:Spring 学习 3- AOP
代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
spring的IoC容器是spring的核心,spring AOP是spring框架的重要组成部分。
在传统的程序设计中,当调用者需要被调用者的协助时,通常由调用者来创建被调用者的实例。但在spring里创建被调用者的工作不再由调用者来完成,因此控制反转(IoC);创建被调用者实例的工作通常由spring容器来完成,然后注入调用者,因此也被称为依赖注入(DI), 依赖注入和控制反转是同一个概念 。
面向方面编程(AOP)是以另一个角度来考虑程序结构 ,通过分析程序结构的关注点来完善面向对象编程(OOP)。OOP将应用程序分解成各个层次的对象,而AOP将程序分解成多个切面。spring AOP 只实现了方法级别的连接点,在J2EE应用中,AOP拦截到方法级别的操作就已经足够。在spring中,未来使IoC方便地使用健壮、灵活的企业服务,需要利用spring AOP实现为IoC和企业服务之间建立联系。
IOC:控制反转也叫依赖注入。
利用了工厂模式将对象交给容器管理,你只需要在spring配置文件总配置相应的bean,以及设置相关的属性,让spring容器来生成类的实例对象以及管理对象。在spring容器启动的时候,spring会把你在配置文件中配置的bean都初始化好,然后在你需要调用的时候,就把它已经初始化好的那些bean分配给你需要调用这些bean的类(假设这个类名是A),分配的方法就是调用A的setter方法来注入,而不需要你在A里面new这些bean了。
注意:面试的时候,如果有条件,画图,这样更加显得你懂了.
AOP:面向切面编程。(Aspect-Oriented Programming)
AOP可以说是对OOP的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
将程序中的交叉业务逻辑(比如安全,日志,事务等),封装成一个切面,然后注入到目标对象(具体业务逻辑)中去。
实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态植入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码CGLib.
简单点解释,比方说你想在你的biz层所有类中都加上一个打印‘你好’的功能,这时就可以用aop思想来做.你先写个类写个类方法,方法经实现打印‘你好’,然后Ioc这个类 ref=“biz.*”让每个类都注入即可实现。