面试总结
1.String\StringBuffer\StringBuilder类区别
String 字符串常量
StringBuffer 字符串变量(线程安全syc)
StringBuilder 字符串变量(非线程安全)
2.三大修饰符
static(重点) 类的所有实例共享
final 修饰类 属性 方法
final修饰的类无法被继承
final修饰的方法无法覆盖
final修饰局部变量只能赋值一次 final修饰的引用变量,对象不可变,对象内容可以变 final修饰的实例变量,必须手动赋值
final finally finalize三个关键词?
3.abstract 抽象类 抽象方法 和Interface
interface与abstract的相同点:
1、都是没有具体实现,需要继承类进行实现;
2、无法直接调用;
3、interface算是抽象类的特例;
interface和abstract的区别:
1、interface无构造方法,abstract类可以有构造方法;
2、abstract类中可以有普通成员变量,interface 只有抽象方法;
3、abstract类的访问类型可以是public或是protected,但interface默认的访问类型就是public abstract
4、一个类可以实现多个interface,但只能继承一个abstract类;
5、interface主要是应用在模块通信上,abstract类主要是用在代码的重写;
6、接口里所有的变量都有默认修饰符(public static final)
4.集合
集合(重中之重)
分类:
Collection
|- List 有序有下标可重复
|- ArrayList 操作速度快 线程不安全 使用数组结构存储元素 特点:查询快,增删慢
|- Vecotr操作速度慢 线程安全 数组结构存储元素 查询块,增删慢
|- LinkedList 操作速度快 线程不安全 链表结构存储 查询慢,增删快
|- Set
|- HashSet 查询效率高,增删效率高,由数组+链表实现
|- TreeSet 可以自动为元素排序
|- LinkedHashSet 有序
Map kv
|- HashMap
|- HashTable
|- TreeMap
|- Properties
|- …
ArrayList 内部使用的动态数组来存储元素,LinkedList 内部使用的双向链表来存储元素,这也是 ArrayList 和 LinkedList 最本质的区别。
ArrayList和LinkList区别
- HashMap 的底层是个 Node 数组(Node<K,V>[] table),在数组的具体索引位置,如果存在多个节点,则可能是以链表或红黑树的形式存在。
- 增加、删除、查找键值对时,定位到哈希桶数组的位置是很关键的一步,源码中是通过下面3个操作来完成这一步:1)拿到 key 的 hashCode 值;2)将 hashCode 的高位参与运算,重新计算 hash 值;3)将计算出来的 hash 值与 “table.length - 1” 进行 & 运算。
- HashMap 的默认初始容量(capacity)是 16,capacity 必须为 2 的幂次方;默认负载因子(load factor)是 0.75;实际能存放的节点个数(threshold,即触发扩容的阈值)= capacity * load factor。
- HashMap 在触发扩容后,阈值会变为原来的 2 倍,并且会对所有节点进行重 hash 分布,重 hash 分布后节点的新分布位置只可能有两个:“原索引位置” 或 “原索引+oldCap位置”。例如 capacity 为16,索引位置 5 的节点扩容后,只可能分布在新表 “索引位置5” 和 “索引位置21(5+16)”。
- 导致 HashMap 扩容后,同一个索引位置的节点重 hash 最多分布在两个位置的根本原因是:1)table的长度始终为 2 的 n 次方;2)索引位置的计算方法为 “(table.length - 1) & hash”。HashMap 扩容是一个比较耗时的操作,定义 HashMap 时尽量给个接近的初始容量值。
- HashMap 有 threshold 属性和 loadFactor 属性,但是没有 capacity 属性。初始化时,如果传了初始化容量值,该值是存在 threshold 变量,并且 Node 数组是在第一次 put 时才会进行初始化,初始化时会将此时的 threshold 值作为新表的 capacity 值,然后用 capacity 和 loadFactor 计算新表的真正 threshold 值。
- 当同一个索引位置的节点在增加后达到 9 个时,并且此时数组的长度大于等于 64,则会触发链表节点(Node)转红黑树节点(TreeNode),转成红黑树节点后,其实链表的结构还存在,通过 next 属性维持。链表节点转红黑树节点的具体方法为源码中的 treeifyBin 方法。而如果数组长度小于64,则不会触发链表转红黑树,而是会进行扩容。
- 当同一个索引位置的节点在移除后达到 6 个时,并且该索引位置的节点为红黑树节点,会触发红黑树节点转链表节点。红黑树节点转链表节点的具体方法为源码中的 untreeify 方法。
- HashMap 在 JDK 1.8 之后不再有死循环的问题,JDK 1.8 之前存在死循环的根本原因是在扩容后同一索引位置的节点顺序会反掉。
10 HashMap 是非线程安全的,在并发场景下使用 ConcurrentHashMap 来代替。
11.list循环遍历remove的问题
5.线程
创建方式:
extends Thread
implemtns Runnbable
线程池
newFixedThreadPool 创建固定大小的线程池
newCachedThreadPool 创建具备缓存的线程池(60秒超时时间)
线程状态:
- 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
- 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。
线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。 - 阻塞(BLOCKED):表示线程阻塞于锁。
- 等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
- 超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。
- 终止(TERMINATED):表示该线程已经执行完毕
sleep、wait?
线程进入阻塞状态 ,sleep 不会释放锁,wait 释放锁(notify、notifyAll)
锁升级机制
sysch与lock区别
voli原理
6.IO
BIO NIO
7.TCP/IP三次握手 四次挥手详细过程,七层协议(扯淡的题目)
8.设计模式
单例模式(手写)、代理、工厂、适配器、策略等
9.mysql
InnoDB和MyISAM是使用MySQL时最常用的两种引擎类型,我们重点来看下两者区别。
事务和外键
InnoDB支持事务和外键,具有安全性和完整性,适合大量insert或update操作
MyISAM不支持事务和外键,它提供高速存储和检索,适合大量的select查询操作
锁机制
InnoDB支持行级锁,锁定指定记录。基于索引来加锁实现。
MyISAM支持表级锁,锁定整张表。
索引结构
InnoDB使用聚集索引(聚簇索引),索引和记录在一起存储,既缓存索引,也缓存记录。
MyISAM使用非聚集索引(非聚簇索引),索引和记录分开。
并发处理能力
MyISAM使用表锁,会导致写操作并发率低,读之间并不阻塞,读写阻塞。
InnoDB读写阻塞可以与隔离级别有关,可以采用多版本并发控制(MVCC)来支持高并发
存储文件
InnoDB表对应两个文件,一个.frm表结构文件,一个.ibd数据文件。InnoDB表最大支持64TB;
MyISAM表对应三个文件,一个.frm表结构文件,一个MYD表数据文件,一个.MYI索引文件。从MySQL5.0开始默认限制是256TB。
数据库的三范式是什么
第⼀范式:强调的是列的原⼦性,即数据库表的每⼀列都是不可分割的原⼦数据项。
第⼆范式:要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字⼀部分的属性。
第三范式:任何⾮主属性不依赖于其它⾮主属性
说⼀下事务特性 ACID是什么
Atomicity(原⼦性):⼀个事务(transaction)中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。事务在执⾏过程中发⽣错误,会被恢复(Rollback)到事务开始前的状态,就像这个事务从来没有执⾏过⼀样。即,事务不可分割、不可约简。
Consistency(⼀致性):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表⽰写⼊的资料必须完全符合所有的预设约束、触发器、级联回滚等。
Isolation(隔离性):数据库允许多个并发事务同时对其数据进⾏读写和修改的能⼒,隔离性可以防⽌多个事务并发执⾏时由于交叉执⾏导致数据的不⼀致。事务隔离分为不同级别,包括读未提交(Read?uncommitted)、读提交(read?committed)、可重复读(repeatableread)和串⾏化(Serializable)。
Durability(持久性):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
(1)通过数据库锁的机制,保障事务的隔离性;
(2)通过 Redo Log(重做日志)来,保障事务的持久性;
(3)通过 Undo Log (撤销日志)来,保障事务的原子性;
(4)通过 Undo Log (撤销日志)来,保障事务的一致性;
MySQL目前主要有的索引类型为:普通索引、唯一索引、主键索引、组合索引、全文索引
索引失效
查询条件中有or or的左右必须都有索引
like查询是以%开头
索引列上参与计算会导致索引失效
违背最左匹配原则
一、适合用索引的场景
1、主键
主键一般为id等具有唯一性标识的字段,需要频繁查找、连接。InnoDB中会自动为主键建立聚集索引,即使没有定义主键,也会自动生成一个隐藏主键建立索引;MyISAM中不会自动生成主键。建议给每张表指定主键。
2、频繁作为查询条件的字段
索引是以空间换时间的,某字段如果频繁作为查询条件,建议建立索引
3、查询中作为与其他表关联的字段(外键)
4、查询中常作为查询排序条件的字段
这里要注意,order by的字段出现在where条件中才能使用索引,否则索引失效。
5、查询中的统计、分组字段
group by和union也属于需要排序的操作,这里也要注意字段出现在where条件中才能使用索引,否则索引失效。
P.S.在高并发条件下倾向使用组合索引
二、不适合用索引的场景
1、频繁更新的字段、表
如果字段添加了索引,在更新时不仅要更新数据本身,还要维护其索引,如果频繁更新会带来很多额外开销。再者,如果一个表频繁进行增删改操作,也不适合索引
2、很少作为查询条件的字段
3、表的记录不多
一般数据量达到300万-500万时考虑建立索引。
4、数据重复且分布平均的字段
由于大量的重复,索引对性能的提升很有限,比如年龄、性别…
数据库索引类型及实现方式?
B+树和B树比较
由于B+树在内部节点上不包含数据信息,因此在内存页中能够存放更多的key。数据存放的更加紧密,具有更好的空间局部性。因此访问叶子节点上关联的数据也具有更好的缓存命中率
B+树的叶子节点都是相连的,因此对整棵树的遍历只需要一次线性遍历叶子节点即可。而且由于数据顺序排列并且相连,所以便于区间查找和搜索。而B树则需要进行每一层的递归遍历。相邻的元素可能在内存中不相邻,所以缓存命中性没有B+树好。
但是B树也有优点,其优点在于,由于B树的每一个节点都包含key和value,因此经常访问的元素可能离根节点更近,因此访问也更迅速。
事务的隔离级别
读未提交(Read UnCommitted/RU)
⼜称为脏读,⼀个事务可以读取到另⼀个事务未提交的数据。这种隔离级别岁最不安全的⼀种,因为未提交的事务是存在回滚的情况。
读已提交(Read Committed/RC)
⼜称为不可重复读,⼀个事务因为读取到另⼀个事务已提交的修改数据,导致在当前事务的不同时间读取同⼀条数据获取的结果不⼀致。
可重复读(Repeatable Read/RR)
⼜称为幻读,⼀个事物读可以读取到其他事务提交的数据,但是在RR隔离级别下,当前读取此条数据只可读取⼀次,在当前事务中,不论读取多少次,数据任然是第⼀次读取的值,不会因为在第⼀次读取之后,其他事务再修改提交此数据⽽产⽣改变。因此也成为幻读,因为读出来的数据并不⼀定就是最新的数据。
串⾏化(Serializable)
所有的数据库的读或者写操作都为串⾏执⾏,当前隔离级别下只⽀持单个请求同时执⾏,所有的操作都需要队列执⾏。所以种隔离级别下所有的数据是最稳定的,但是性能也是最差的。数据库的锁实现就是这种隔离级别的更⼩粒度版本。
mysql性能优化
1、系统配置优化
1.1 保证从内存中读取数据
MySQL会在内存中保存一定的数据,通过LRU算法将不常访问的数据保存在硬盘文件中。
尽可能的扩大内存中的数据量,将数据保存在内存中,从内存中读取数据,可以提升MySQL性能。
扩大innodb_buffer_pool_size,能够全然从内存中读取数据。最大限度降低磁盘操作。
1.2 数据预热
默认情况,仅仅有某条数据被读取一次,才会缓存在 innodb_buffer_pool。
所以,数据库刚刚启动,须要进行数据预热,将磁盘上的全部数据缓存到内存中。
数据预热能够提高读取速度。
1.3 降低磁盘写入次数
增大redolog,减少落盘次数
innodb_log_file_size 设置为 0.25 * innodb_buffer_pool_size
通用查询日志、慢查询日志可以不开 ,bin-log开
生产中不开通用查询日志,遇到性能问题开慢查询日志
写redolog策略 innodb_flush_log_at_trx_commit设置为0或2
如果不涉及非常高的安全性 (金融系统),或者基础架构足够安全,或者事务都非常小,都能够用 0
或者 2 来减少磁盘操作。
1.4 提高磁盘读写性能
使用SSD或者内存磁盘
2、表结构设计优化
2.1 设计中间表
设计中间表,一般针对于统计分析功能,或者实时性不高的需求(OLTP、OLAP)
2.2 设计冗余字段
为减少关联查询,创建合理的冗余字段(创建冗余字段还需要注意数据一致性问题)
2.3 拆表
对于字段太多的大表,考虑拆表(比如一个表有100多个字段)
对于表中经常不被使用的字段或者存储数据比较多的字段,考虑拆表
2.4 主键优化
每张表建议都要有一个主键(主键索引),而且主键类型最好是int类型,建议自增主键(不考虑分布
式系统的情况下 雪花算法)。
2.5 字段的设计
数据库中的表越小,在它上面执行的查询也就会越快。
因此,在创建表的时候,为了获得更好的性能,我们可以将表中字段的宽度设得尽可能小。
尽量把字段设置为NOTNULL,这样在将来执行查询的时候,数据库不用去比较NULL值。
对于某些文本字段,例如“省份”或者“性别”,我们可以将它们定义为ENUM类型。因为在MySQL中,
ENUM类型被当作数值型数据来处理,而数值型数据被处理起来的速度要比文本类型快得多。这样,我
们又可以提高数据库的性能。
能用数字的用数值类型
sex 1 0
3、SQL语句及索引优化
3.1 EXPLAIN查看索引使用情况
3.2 SQL语句中IN包含的值不应过多
3.3 SELECT语句务必指明字段名称
3.4 当只需要一条数据的时候,使用limit 1
3.5 排序字段加索引
3.6 如果限制条件中其他字段没有索引,尽量少用or
or两边的字段中,如果有一个不是索引字段,会造成该查询不走索引的情况。
3.9 区分in和exists、not in和not exists
区分in和exists主要是造成了驱动顺序的改变(这是性能变化的关键),如果是exists,那么以外层表为
驱动表,先被访问,如果是IN,那么先执行子查询。所以IN适合于外表大而内表小的情况;EXISTS适合
于外表小而内表大的情况。
关于not in和not exists,推荐使用not exists,不仅仅是效率问题,not in可能存在逻辑问题。
3.10 使用合理的分页方式以提高分页的效率
3.12 不建议使用%前缀模糊查询
Spring
Spring 是分层的 full-stack(全栈) 轻量级开源框架,以 IoC 和 AOP 为内核,提供了展现层 SpringMVC 和业务层事务管理等众多的企业级应⽤技术,还能整合开源世界众多著名的第三⽅框架和类库,已经成为使⽤最多的 Java EE 企业应⽤开源框架。
Spring 的优势
⽅便解耦,简化开发
AOP编程的⽀持
声明式事务的⽀持
⽅便程序的测试
⽅便集成各种优秀框架
降低JavaEE API的使⽤难度
源码是经典的 Java 学习范例
Spring 的核⼼结构
Spring核⼼容器(Core Container) 容器是Spring框架最核⼼的部分,它管理着Spring应⽤中bean的创建、配置和管理。在该模块中,包括了Spring bean⼯⼚,它为Spring提供了DI的功能。基于bean⼯⼚,我们还会发现有多种Spring应⽤上下⽂的实现。所有的Spring模块都构建于核⼼容器之上。
Web 该模块提供了SpringMVC框架给Web应⽤,还提供了多种构建和其它应⽤交互的远程调⽤⽅案。 SpringMVC框架在Web层提升了应⽤的松耦合⽔平。
IOC
IoC Inversion of Control (控制反转/反转控制),注意它是⼀个技术思想,不是⼀个技术实现
为什么叫做控制反转?
控制:指的是对象创建(实例化、管理)的权利
反转:控制权交给外部环境了(spring框架、IoC容器)
IoC解决对象之间的耦合问题
Spring IoC基础
BeanFactory与ApplicationContext区别
BeanFactory是Spring框架中IoC容器的顶层接⼝,它只是⽤来定义⼀些基础功能,定义⼀些基础规范,⽽ApplicationContext是它的⼀个⼦接⼝,所以ApplicationContext是具备BeanFactory提供的全部功能的。
通常,我们称BeanFactory为SpringIOC的基础容器,ApplicationContext是容器的⾼级接⼝,⽐BeanFactory要拥有更多的功能,⽐如说国际化⽀持和资源访问(xml,java配置类)等等
Spring IOC⾼级特性
lazy-Init 延迟加载
FactoryBean 和 BeanFactory
BeanFactory接⼝是容器的顶级接⼝,定义了容器的⼀些基础⾏为,负责⽣产和管理Bean的⼀个⼯⼚,具体使⽤它下⾯的⼦接⼝类型,⽐如ApplicationContext;此处我们重点分析FactoryBean
Spring中Bean有两种,⼀种是普通Bean,⼀种是⼯⼚Bean(FactoryBean)FactoryBean可以⽣成某⼀个类型的Bean实例(返回给我们),也就是说我们可以借助于它⾃定义Bean的创建过程。
后置处理器
Spring提供了两种后处理bean的扩展接⼝,分别为 BeanPostProcessor 和BeanFactoryPostProcessor,两者在使⽤上是有所区别的。
bean生命周期:实例化 属性赋值 初始化(前置后置处理器,初始化方法等等)销毁
AOP
AOP: Aspect oriented Programming ⾯向切⾯编程/⾯向⽅⾯编程
解决问题:在不改变原有业务逻辑情况下,增强横切逻辑代码,根本上解耦合,避免横切逻辑代码重复
AOP本质:在不改变原有业务逻辑的情况下增强横切逻辑,横切逻辑代码往往是权限校验代码、⽇志代码、事务控制代码、性能监控代码。
AOP 术语
Joinpoint(连接点) ⽅法开始时、结束时、正常运⾏完毕时、⽅法异常时等这些特殊的时机点,我们称之为连接点,项⽬中每个⽅法都有连接点,连接点是⼀种候选点
Pointcut(切⼊点) 切入的方法总的描述
Advice(通知/增强) 它指的是切⾯类中⽤于提供增强功能的⽅法。前置通知 后置通知 异常通知 最终通知 环绕通知。
Target(⽬标对象) 它指的是代理的⽬标对象。即被代理对象。
Proxy(代理) 产⽣的代理类。即代理对象。
Weaving(织⼊) 它指的是把增强应⽤到⽬标对象来创建新的代理对象的过程。
Aspect(切⾯)
Spring 实现AOP思想使⽤的是动态代理技术
默认情况下,Spring会根据被代理对象是否实现接⼝来选择使⽤JDK还是CGLIB。当被代理对象没有实现任何接⼝时,Spring会选择CGLIB。当被代理对象实现了接⼝,Spring会选择JDK官⽅的代理技术,不过我们可以通过配置的⽅式,让Spring强制使⽤CGLIB。
spring中的设计模式?
1.Spring中的BeanFactory就是简单工厂模式的体现
2.实现了FactoryBean接口的bean是一类叫做factory的bean。其特点是,spring会在使用getBean()调用获得该bean时,会自动调用该bean的getObject()方法,所以返回的不是factory这个bean,而是这个bean.getOjbect()方法的返回值。
3.Spring依赖注入Bean实例默认是单例的。Spring的依赖注入(包括lazy-init方式)都是发生在AbstractBeanFactory的getBean里。
getBean的doGetBean方法调用getSingleton进行bean的创建。
4.SpringMVC中的适配器HandlerAdatper。
实现原理:
HandlerAdatper根据Handler规则执行不同的Handler。
Spring MVC
SpringMVC 全名叫 Spring Web MVC,是⼀种基于 Java 的实现 MVC 设计模型的请求驱动类型的轻量级Web 框架,属于 SpringFrameWork 的后续产品。
Spring MVC 本质可以认为是对servlet的封装,简化了我们serlvet的开发
原理流程:
第⼀步:⽤户发送请求⾄前端控制器DispatcherServlet
第⼆步:DispatcherServlet收到请求调⽤HandlerMapping处理器映射器
第三步:处理器映射器根据请求Url找到具体的Handler(后端控制器),⽣成处理器对象及处理器拦截器(如果 有则⽣成)⼀并返回DispatcherServlet
第四步:DispatcherServlet调⽤HandlerAdapter处理器适配器去调⽤Handler
第五步:处理器适配器执⾏Handler
第六步:Handler执⾏完成给处理器适配器返回ModelAndView
第七步:处理器适配器向前端控制器返回 ModelAndView,ModelAndView 是SpringMVC 框架的⼀个底层对 象,包括 Model 和 View
第⼋步:前端控制器请求视图解析器去进⾏视图解析,根据逻辑视图名来解析真正的视图。
第九步:视图解析器向前端控制器返回View
第⼗步:前端控制器进⾏视图渲染,就是将模型数据(在 ModelAndView 对象中)填充到 request 域
第⼗⼀步:前端控制器向⽤户响应结果
Spring MVC ⾼级技术
拦截器(Inteceptor)
1.1 监听器、过滤器和拦截器对⽐
Servlet:处理Request请求和Response响应
过滤器(Filter):对Request请求起到过滤的作⽤,作⽤在Servlet之前,如果配置为/*可以对所有的资源访问(servlet、js/css静态资源等)进⾏过滤处理
监听器(Listener):实现了javax.servlet.ServletContextListener 接⼝的服务器端组件,它随Web应⽤的启动⽽启动,只初始化⼀次,然后会⼀直运⾏监视,随Web应⽤的停⽌⽽销毁
作⽤⼀:做⼀些初始化⼯作,web应⽤中spring容器启动ContextLoaderListener
作⽤⼆:监听web中的特定事件,⽐如HttpSession,ServletRequest的创建和销毁;变量的创建、销毁和修改等。可以在某些动作前后增 加处理,实现监控,⽐如统计在线⼈数,利⽤HttpSessionLisener等。
拦截器(Interceptor):是SpringMVC、Struts等表现层框架⾃⼰的,不会拦截jsp/html/css/image的访问等,只会拦截访问的控制器⽅法(Handler)。
Spring Boot
约定优于配置
SpringBoot对上述Spring的缺点进行的改善和优化,基于约定优于配置的思想,可以让开发人员不必在配置与逻辑 业务之间进行思维的切换
springboot的自动配置,指的是springboot,会自动将一些配置类的bean注册进ioc容器,我们可以需要的地方使用@autowired或@resource
等注解来使用它。
实例的注入方式首先来看看 Spring 中的实例该如何注入,总结起来,无非三种:
属性注入
set 方法注入
构造方法注入
springboot: 简单、快速、方便地搭建项目;对主流开发框架的无配置集成;极大提高了开发、部署效率
SpringBoot原理深入及源码剖析
为什么导入dependency时不需要指定版本?
在Spring Boot入门程序中,项目pom.xml文件有两个核心依赖,分别是spring-boot-starter-parent和spring-boot-starter-web有约定
Spring Boot应用的启动入口是@SpringBootApplication注解标注类中的main()方法,@SpringBootApplication能够扫描Spring组件并自动配置Spring Boot,主要有三个注解
1.@SpringBootConfiguration注解
@SpringBootConfiguration注解表示Spring Boot配置类。
2.@EnableAutoConfiguration注解
@EnableAutoConfiguration注解表示开启自动配置功能,该注解是Spring Boot框架最重要的注解,也是实现自动化配置的注解。
@AutoConfigurationPackage:这个组合注解主要是@Import(AutoConfigurationPackages.Registrar.class),它通过将Registrar类导入到容器中,而Registrar类作用是扫描主配置类同级目录以及子包,并将相应的组件导入到springboot创建管理的容器中;
@Import(AutoConfigurationImportSelector.class):它通过将AutoConfigurationImportSelector类导入到容器中,AutoConfigurationImportSelector类作用是通过selectImports方法执行的过程中,会使用内部工具类SpringFactoriesLoader,查找classpath上所有jar包中的META-INF/spring.factories进行加载,实现将配置类信息交给SpringFactory加载器进行一系列的容器创建过程
3.@ComponentScan注解
@ComponentScan注解具体扫描的包的根路径由Spring Boot项目主程序启动类所在包位置决
定,在扫描过程中由前面介绍的@AutoConfigurationPackage注解进行解析,从而得到Spring Boot项目主程序启动类所在包的具体位置
自定义Stater
SpringBoot starter机制
starter是SpringBoot非常重要的一部分,可以理解为一个可拔插式的插件,正是这些starter使得使用某个功能的开发者不需要关注各种依赖库的处理,不需要具体的配置信息,由Spring Boot自动通过classpath路径下的类发现需要的Bean,并织入相应的Bean。
执行原理
第一步:获取并启动监听器
第二步:根据SpringApplicationRunListeners以及参数来准备环境
第三步:创建Spring容器
第四步:Spring容器前置处理
第五步:刷新容器
第六步:Spring容器后置处理
第七步:发出结束执行的事件
第八步:执行Runners
Redis
redistemplate使用原理 ops方法
生产中遇到的缓存问题(大厂常见面试题)
系统在某个时刻访问量剧增(热点新闻),造成数据库压力剧增甚至崩溃,怎么办?
什么是缓存雪崩、缓存穿透和缓存击穿,会造成什么问题,如何解决?
什么是大Key和热Key,会造成什么问题,如何解决?
如何保证 Redis 中的数据都是热点数据?
缓存和数据库数据是不一致时,会造成什么问题,如何解决?
什么是数据并发竞争,会造成什么问题,如何解决?
单线程的Redis为什么这么快?
Redis哨兵和集群的原理及选择?
在多机Redis使用时,如何保证主从服务器的数据一致性?
缓存的使用场景:
DB缓存,减轻DB服务器压力
提高系统响应
做Session分离
做分布式锁(Redis)
做乐观锁(Redis)
缓存的读写模式
缓存有三种读写模式
1.Cache Aside Pattern(常用)
高并发脏读的三种情况
1、先更新数据库,再更新缓存
update与commit之间,更新缓存,commit失败
则DB与缓存数据不一致
2、先删除缓存,再更新数据库
update与commit之间,有新的读,缓存空,读DB数据到缓存 数据是旧的数据
commit后 DB为新数据
则DB与缓存数据不一致
3、先更新数据库,再删除缓存(推荐)
update与commit之间,有新的读,缓存空,读DB数据到缓存 数据是旧的数据
commit后 DB为新数据
则DB与缓存数据不一致
采用延时双删策略
2.Read/Write Through Pattern
应用程序只操作缓存,缓存操作数据库。
3.Write Behind Caching Pattern
应用程序只更新缓存。
缓存通过异步的方式将数据批量或合并后更新到DB中
不能时时同步,甚至会丢数据
Redis数据类型选择和应用场景
1.string字符串类型
Redis的String能表达3种值的类型:字符串、整数、浮点数 100.01 是个六位的串
应用场景:
1、key和命令是字符串
2、普通的赋值
3、incr用于乐观锁
incr:递增数字,可用于实现乐观锁 watch(事务)
4、setnx用于分布式锁
当value不存在时采用赋值,可用于实现分布式锁
2.list列表类型
list列表类型可以存储有序、可重复的元素
获取头部或尾部附近的记录是极快的
应用场景:
1、作为栈或队列使用
列表有序可以作为栈和队列使用
2、可用于各种列表,比如用户列表、商品列表、评论列表等。
3.set集合类型
Set:无序、唯一元素
集合中最大的成员数为 2^32 - 1
应用场景:
适用于不能重复的且不需要顺序的数据结构
比如:关注的用户,还可以通过spop进行随机抽奖
4.sortedset有序集合类型
SortedSet(ZSet) 有序集合: 元素本身是无序不重复的
每个元素关联一个分数(score)
应用场景:
由于可以按照分值排序,所以适用于各种排行榜。比如:点击排行榜、销量排行榜、关注排行榜等。
5.hash类型(散列表)
Redis hash 是一个 string 类型的 field 和 value 的映射表,它提供了字段和字段值的映射。
6.bitmap位图类型
bitmap是进行位操作的
通过一个bit位来表示某个元素对应的值或者状态,其中的key就是对应元素本身。
bitmap本身会极大的节省储存空间。
应用场景:
1、用户每月签到,用户id为key , 日期作为偏移量 1表示签到
2、统计活跃用户, 日期为key,用户id为偏移量 1表示活跃
3、查询用户在线状态, 日期为key,用户id为偏移量 1表示在线
7.geo地理位置类型
geo是Redis用来处理位置信息的。
8.stream数据流类型
stream是Redis5.0后新增的数据结构,用于可持久化的消息队列。
几乎满足了消息队列具备的全部内容,包括:
消息ID的序列化生成
消息遍历
消息的阻塞和非阻塞读取
消息的分组消费
未完成消息的处理
消息队列监控
每个Stream都有唯一的名称,它就是Redis的key,首次使用 xadd 指令追加消息时自动创建。
Redis事务
Redis的事务是通过multi、exec、discard和watch这四个命令来完成的。
Redis的单个命令都是原子性的,所以这里需要确保事务性的对象是命令集合。
Redis将命令集合序列化并确保处于同一事务的命令集合连续且不被打断的执行
Redis不支持回滚操作
事务命令
multi:用于标记事务块的开始,Redis会将后续的命令逐个放入队列中,然后使用exec原子化地执行这个命令队列
exec:执行命令队列
discard:清除命令队列
watch:监视key
unwatch:清除监视key
Redis核心原理
Redis持久化
Redis有两种持久化方式:RDB和AOF
注意:Redis持久化不保证数据的完整性。
RDB
RDB(Redis DataBase),是redis默认的存储方式,RDB方式是通过快照( snapshotting )完成的。
这一刻的数据,不关注过程
触发快照的方式
- 符合自定义配置的快照规则
- 执行save或者bgsave命令
- 执行flushall命令
- 执行主从复制操作 (第一次)
RDB的优缺点
优点
RDB是二进制压缩文件,占用空间小,便于传输(传给slaver)
主进程fork子进程,可以最大化Redis性能,主进程不能太大,Redis的数据量不能太大,复制过程中主进程阻塞
缺点
不保证数据完整性,会丢失最后一次快照以后更改的所有数据
AOF
AOF(append only file)是Redis的另一种持久化方式。Redis默认情况下是不开启的。开启AOF持久
化后
Redis 将所有对数据库进行过写入的命令(及其参数)(RESP)记录到 AOF 文件, 以此达到记录数据库状态的目的,
这样当Redis重启后只要按顺序回放这些命令就会恢复到原始状态了。
AOF会记录过程,RDB只管结果
AOF原理
AOF文件中存储的是redis的命令,同步命令到 AOF 文件的整个过程可以分为三个阶段:
命令传播:Redis 将执行完的命令、命令的参数、命令的参数个数等信息发送到 AOF 程序中。
缓存追加:AOF 程序根据接收到的命令数据,将命令转换为网络通讯协议的格式,然后将协议内容追加到服务器的 AOF 缓存中。
文件写入和保存:AOF 缓存中的内容被写入到 AOF 文件末尾,如果设定的 AOF 保存条件被满足的话,fsync 函数或者 fdatasync 函数会被调用,将写入的内容真正地保存到磁盘中。
AOF 保存模式
Redis 目前支持三种 AOF 保存模式,它们分别是:
AOF_FSYNC_NO :不保存。
AOF_FSYNC_EVERYSEC :每一秒钟保存一次。(默认)
AOF_FSYNC_ALWAYS :每执行一个命令保存一次。(不推荐)
RDB与AOF对比
1、RDB存某个时刻的数据快照,采用二进制压缩存储,AOF存操作命令,采用文本存储(混合)
2、RDB性能高、AOF性能较低
3、RDB在配置触发状态会丢失最后一次快照以后更改的所有数据,AOF设置为每秒保存一次,则最多丢2秒的数据
4、Redis以主服务器模式运行,RDB不会保存过期键值对数据,Redis以从服务器模式运行,RDB会保存过期键值对,当主服务器向从服务器同步时,再清空过期键值对。
AOF写入文件时,对过期的key会追加一条del命令,当执行AOF重写时,会忽略过期key和del命令。
应用场景
内存数据库 rdb+aof 数据不容易丢
有原始数据源: 每次启动时都从原始数据源中初始化 ,则 不用开启持久化 (数据量较小)
缓存服务器 rdb 一般 性能高
在数据还原时
有rdb+aof 则还原aof,因为RDB会造成文件的丢失,AOF相对数据要完整。
只有rdb,则还原rdb
Redis数据量存储过大,性能突然下降,fork 时间过长 阻塞主进程,则只开启AOF
缓存过期和淘汰策略
Redis采用的默认内存释放策略是noeviction-不删除,达到最大内存时,如需更多内存(存入数据),则操作报错;默认的过期key删除策略则是惰性删除+定期删除的方案;
Redis性能高:
官方数据
读:110000次/s
写:81000次/s
长期使用,key会不断增加,Redis作为缓存使用,物理内存也会满
内存与硬盘交换(swap) 虚拟内存 ,频繁IO 性能急剧下降
maxmemory
不设置的场景
Redis的key是固定的,不会增加
Redis作为DB使用,保证数据的完整性,不能淘汰 , 可以做集群,横向扩展
缓存淘汰策略:禁止驱逐 (默认)
设置的场景:
Redis是作为缓存使用,不断增加Key
maxmemory : 默认为0 不限制
问题:达到物理内存后性能急剧下架,甚至崩溃,
在redis.conf中设置大小
expire数据结构
在Redis中可以使用expire命令设置一个键的存活时间(ttl: time to live),过了这段时间,该键就会自动被删除
删除策略
Redis的数据删除有定时删除、惰性删除和主动删除三种方式。
Redis目前采用惰性删除+主动删除的方式。
定时删除
在设置键的过期时间的同时,创建一个定时器,让定时器在键的过期时间来临时,立即执行对键的删除操作。需要创建定时器,而且消耗CPU,一般不推荐使用。
惰性删除
在key被访问时如果发现它已经失效,那么就删除它。
调用expireIfNeeded函数,该函数的意义是:读取数据之前先检查一下它有没有失效,如果失效了就删除它。
主动删除
在redis.conf文件中可以配置主动删除策略,默认是no-enviction(不删除)
LRU:LRU (Least recently used) 最近最少使用
Redis的LRU 数据淘汰机制
volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰
random随机:volatile-random,从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
LFU
LFU (Least frequently used) 最不经常使用,如果一个数据在最近一段时间内使用次数很少,那么在将来一段时间内被使用的可能性也很小。
volatile-lfu
allkeys-lfu
random
随机
volatile-random
从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
allkeys-random
从数据集(server.db[i].dict)中任意选择数据淘汰
ttl
volatile-ttl
从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
redis 数据集数据结构中保存了键值对过期时间的表,即 redisDb.expires。
TTL 数据淘汰机制:从过期时间的表中随机挑选几个键值对,取出其中 ttl 最小的键值对淘汰。
noenviction
禁止驱逐数据,不删除 默认
缓存淘汰策略的选择
allkeys-lru : 在不确定时一般采用策略。 冷热数据交换
volatile-lru : 比allkeys-lru性能差 存 : 过期时间
allkeys-random : 希望请求符合平均分布(每个元素以相同的概率被访问)
自己控制:volatile-ttl 缓存穿透
缓存问题
缓存穿透:
一般的缓存系统,都是按照key去缓存查询,如果不存在对应的value,就应该去后端系统查找(比如DB)。
缓存穿透是指在高并发下查询key不存在的数据(不存在的key),会穿过缓存查询数据库。导致数据库压力过大而宕机
解决方案:
1、对查询结果为空的情况也进行缓存,缓存时间(ttl)设置短一点,或者该key对应的数据insert了之后清理缓存。
问题:缓存太多空值占用了更多的空间
2、使用布隆过滤器。
缓存雪崩
当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如DB)带来很大压力。
突然间大量的key失效了或redis重启,大量访问数据库,数据库崩溃
解决方案:
1、 key的失效期分散开 不同的key设置不同的有效期
2、设置二级缓存(数据不一定一致)
3、高可用(脏读)
缓存击穿
对于一些设置了过期时间的key,如果这些key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑一个问题:缓存被“击穿”的问题,这个和缓存雪崩的区别在于这里针对某一key缓存,前者则是很多key。
缓存在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮。
解决方案:
1、用分布式锁控制访问的线程
使用redis的setnx互斥锁先进行判断,这样其他线程就处于等待状态,保证不会有大并发操作去操作数据库。
2、不设超时时间,volatile-lru 但会造成写一致问题
数据不一致
缓存和DB的数据不一致的根源 : 数据源不一样
如何解决
强一致性很难,追求最终一致性(时间)
保证数据的最终一致性(延时双删)
1、先更新数据库同时删除缓存项(key),等读的时候再填充缓存
2、2秒后再删除一次缓存项(key)
3、设置缓存过期时间 Expired Time 比如 10秒 或1小时
4、将缓存删除失败记录到日志中,利用脚本提取失败记录再次删除(缓存失效期过长 7*24)升级方案
通过数据库的binlog来异步淘汰key,利用工具(canal)将binlog日志采集发送到MQ中,然后通过ACK机制确认处理删除缓存。
Redis分布式锁的实现
主要用到的redis函数是setnx()
用SETNX实现分布式锁
Redis高可用方案
1.主从复制
Redis支持主从复制功能,可以通过执行slaveof(Redis5以后改成replicaof)或者在配置文件中设置slaveof(Redis5以后改成replicaof)来开启复制功能
作用:
读写分离
一主多从,主从同步
主负责写,从负责读
提升Redis的性能和吞吐量
主从的数据一致性问题
数据容灾:
从机是主机的备份
主机宕机,从机可读不可写
默认情况下主机宕机后,从机不可为主机
利用哨兵可以实现主从切换,做到高可用
哨兵(sentinel)是Redis的高可用性(High Availability)的解决方案:
由一个或多个sentinel实例组成sentinel集群可以监视一个或多个主服务器和多个从服务器。
当主服务器进入下线状态时,sentinel可以将该主服务器下的某一从服务器升级为主服务器继续提供服务,从而保证redis的高可用性。
3.集群与分区
分区是将数据分布在多个Redis实例(Redis主机)上,以至于每个实例只包含一部分数据。
分区的意义:
性能的提升
单机Redis的网络I/O能力和计算资源是有限的,将请求分散到多台机器,充分利用多台机器的计算能力可网络带宽,有助于提高Redis总体的服务能力。
存储能力的横向扩展
即使Redis的服务能力能够满足应用需求,但是随着存储数据的增加,单台机器受限于机器本身的存储容量,将数据分散到多台机器上存储使得Redis服务可以横向扩展。
单线程的redis为什么这么快:
redis在内存中操作,持久化只是数据的备份,正常情况下内存和硬盘不会频繁swap
maxmemory的设置+淘汰策略
数据结构简单,有压缩处理,是专门设计的
单线程没有锁,没有多线程的切换和调度,不会死锁,没有性能消耗
使用I/O多路复用模型,非阻塞IO;
构建了多种通信模式,进一步提升性能
Tomcat
Tomcat是⼀个Http服务器(能够接收并且处理http请求,所以tomcat是⼀个http服务器)
我们使⽤浏览器向某⼀个⽹站发起请求,发出的是Http请求,那么在远程,Http服务器接收到这个请求之后,会调⽤具体的程序(Java类)进⾏处理,往往不同的请求由不同的Java类完成处理。
Servlet接⼝和Servlet容器这⼀整套内容叫作Servlet规范。
Tomcat的两个重要身份
1)http服务器
2)Tomcat是⼀个Servlet容器
Tomcat 设计了两个核⼼组件连接器(Connector)和容器(Container)来完成 Tomcat 的两⼤核⼼功能。
连接器,负责对外交流: 处理Socket连接,负责⽹络字节流与Request和Response对象的转化;
容器,负责内部处理:加载和管理Servlet,以及具体处理Request请求;
Coyote 是Tomcat 中连接器的组件名称 , 是对外的接⼝。客户端通过Coyote与服务器建⽴连接、发送请求并接受响应 。
Nginx
Nginx 到底是什么?
Nginx 是⼀个⾼性能的HTTP和反向代理web服务器,核⼼特点是占有内存少,并发能⼒强
Nginx ⼜能做什么事情(应⽤场景)
Http服务器(Web服务器)
性能⾮常⾼,⾮常注重效率,能够经受⾼负载的考验。
⽀持50000个并发连接数,不仅如此,CPU和内存的占⽤也⾮常的低,10000个没有活动的连接才占⽤2.5M的内存。
反向代理服务器
负载均衡服务器
动静分离
Nginx 的特点:
跨平台:Nginx可以在⼤多数类unix操作系统上编译运⾏,⽽且也有windows版本
Nginx的上⼿⾮常容易,配置也⽐较简单
⾼并发,性能好
稳定性也特别好,宕机概率很低
Nginx启动后,以daemon多进程⽅式在后台运⾏,包括⼀个Master进程和多个Worker进程,Master进程是领导,是⽼⼤,Worker进程是⼲活的⼩弟。
master进程
主要是管理worker进程,⽐如:
接收外界信号向各worker进程发送信号(./nginx -s reload)
监控worker进程的运⾏状态,当worker进程异常退出后Master进程会⾃动重新启动新的worker进程等
worker进程
worker进程具体处理⽹络请求。多个worker进程之间是对等的,他们同等竞争来⾃客户端的请求,各进程互相之间是独⽴的。⼀个请求,只可能在⼀个worker进程中处理,⼀个worker进程,不可能处理其它进程的请求。worker进程的个数是可以设置的,⼀般设置与机器cpu核数⼀致。
nginx多进程模型好处:
每个worker进程都是独⽴的,不需要加锁,节省开销
每个worker进程都是独⽴的,互不影响,⼀个异常结束,其他的照样能提供服务
多进程模型为reload热部署机制提供了⽀撑
Mybatis
mybatis的一级缓存其实是会话缓存,sqlsession(mybatis的连接对象)可以对结果进行缓存,在同一个会话,多个相同条件的查询,会通过会话缓存,作用范围只局限于一个会话对象。
mybatis的二级缓存是namespace(对应mapper文件)的缓存,特点是mapper文件中的所有查询操作会进行结果的缓存。不局限于一个会话当中。二级缓存失效DML增删改操作,缓存失效清空。
对于查询多、commit少且用户对查询结果实时性要求不高,此时采用 mybatis 二级缓存技术降低数据库访问量,提高访问速度。
但不能滥用二级缓存,二级缓存也有很多弊端,从MyBatis默认二级缓存是关闭的就可以看出来。
二级缓存是建立在同一个 namespace下的,如果对表的操作查询可能有多个 namespace,那么得到的数据就是错误的。
二级缓存的使用原则
- 只能在一个命名空间下使用二级缓存
由于二级缓存中的数据是基于namespace的,即不同 namespace 中的数据互不干扰。
在多个namespace中存在对同一个表的操作,那么这多个namespace中的数据可能就会出现不一致现象。 - 在单表上使用二级缓存
如果一个表与其它表有关联关系,那么就非常有可能存在多个 namespace 对同一数据的操作。
而不同 namespace 中的数据相互干扰,所以就有可能出现多个 namespace 中的数据不一致现象。 - 查询多于修改时使用二级缓存
在查询操作远远多于增删改操作的情况下可以使用二级缓存。因为任何增删改操作都将刷新二级缓存,对二级缓存的频繁刷新将降低系统性能。
Elasticsearch
ES 的数据读取过程以及文档读写原理大致分析一下?
写数据过程
- 客户端通过hash选择一个node发送请求,这个node被称做coordinating node(协调节点),
- 协调节点对docmount进行路由,将请求转发给到对应的primary shard
- primary shard 处理请求,将数据同步到所有的replica shard
- 此时协调节点,发现primary shard 和所有的replica shard都处理完之后,就反馈给客户端
es读取过程
5. 客户端发送get请求到任意一个node节点,然后这个节点就称为协调节点,
6. 协调节点对document进行路由,将请求转发到对应的node,此时会使用随机轮询算法,在primary shard 和replica shard中随机选择一个,让读取请求负载均衡,
7. 接收请求的node返回document给协调节点,
8. 协调节点,返回document给到客户端
es搜索过程
9. 客户端发送请求到协调节点,
10. 协调节点将请求大宋到所有的shard对应的primary shard或replica shard ;
11. 每个shard将自己搜索到的结果返回给协调节点,返回的结果是dou.id或者自己自定义id,然后协调节点对数据进行合并排序操作,最终得到结果。
12. 最后协调节点根据id到个shard上拉取实际 的document数据,左后返回给客户端。
倒排索引
ElasticSearch中一个重要的概念 : 倒排索引(Inverted Index)也叫反向索引,有反向索引必有正向索引。通俗地来讲,正向索引是通过key找value,反向索引则是通过value找key。
关键词到文档id映射
运行原理?
Fastdfs
先开始我们采用把文件利用本地文件存储,(随着文件的增多),随后本地存储的一些问题显现出来,比如没有备份机制存在、成本较高(服务器同时计算和存储),遇到存储瓶颈,不适用集群环境等,所以使用分布式存储系统显得势在必行,我们了解到一些分布式文件系统如HDFS,FastDFS,HDFS适合大文件的存储,所以我们选择FastDFS,FastDFS是高性能的分布式文件系统,主要功能包含文件的存储、同步、访问(上传和下载),特别适用于以文件为主体的网络站点(图片分享和视频分享)。
FastDFS包含两个角色 tracker server 负责请求调度和集群管理,storage server 负责数据存储、读写、同步,fastDFS也可以集成FastDHT(文件去重)(FastDFS的storage server每次上传均计算文件的hash值,然后从FastDHT服务器上进行查找比对,如果没有返回,则写入hash,并将文件保存,如果有返回,则建立一个新的文件链接(软连接 ln -s),不保存文件。)和nginx为分布式文件系统提供Http服务支持、解决复制延迟问题(原理看情况说)
我们利用其javaAPI实现对图片以及书籍的上传以及下载。
Spring Cloud
Spring Cloud 是一个基于 Spring Boot 实现的云应用开发工具,它为基于 JVM 的云应用开发中的配置管理、服务发现、断路器、智能路由、微代理、控制总线、全局锁、决策竞选、分布式会话和集群状态管理等,是微服务的一种实现。
1.Spring Cloud Netflix
Spring Cloud Netflix 集成众多Netflix的开源软件:Eureka, Hystrix, Zuul, Archaius,组成了微服务的
最重要的核心组件。
2.Netflix Eureka
服务中心,用于服务注册与发现,一个基于 REST 的服务,用于定位服务。
3.Netflix Hystrix
熔断器,容错管理工具,旨在通过熔断机制控制服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。
4.Netflix Zuul
Zuul 是在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。
5.Netflix Archaius
配置管理API,包含一系列配置管理API,提供动态类型化属性、线程安全配置操作、轮询框架、回调机制等功能,可以实现动态获取配置。
6.Spring Cloud Config
配置中心,利用git集中管理程序的配置。
7.Spring Cloud Bus
事件、消息总线,用于在集群(例如,配置变化事件)中传播状态变化,可与Spring Cloud Config联合实现热部署。
8.Spring Cloud Ribbon
Ribbon是Netflix发布的负载均衡器,它有助于控制HTTP和TCP的客户端的行为。为Ribbon配置服务提供者地址后,Ribbon就可基于某种负载均衡算法,自动地帮助服务消费者去请求。
Spring Cloud架构实现
通过这张图,可以比较清楚的了解到各组件配置使用运行机制:
1、请求统一通过API网关(Zuul)来访问内部服务.
2、网关接收到请求后,从注册中心(Eureka)获取可用服务
3、由Ribbon进行均衡负载后,分发到后端具体实例
4、微服务之间通过Feign进行通信处理业务
5、Hystrix负责处理服务超时熔断
6、Turbine监控服务间的调用和熔断相关指标
fegin原理?
Nacos就是注册中⼼+配置中⼼的组合(Nacos=Eureka+Config+Bus)
Nacos功能特性
服务发现与健康检查
动态配置管理
动态DNS服务
服务和元数据管理(管理平台的⻆度,nacos也有⼀个ui⻚⾯,可以看到注册的
服务及其实例信息(元数据信息)等),动态的服务权重调整、动态服务优雅下线,都可以去做
Sentinel是⼀个⾯向云原⽣微服务的流量控制、熔断降级组件。
替代Hystrix,针对问题:服务雪崩、服务降级、服务熔断、服务限流
多线程
ReentrantLock是什么?
ReentrantLock是个典型的独占模式AQS,同步状态为0时表示空闲。当有线程获取到空闲的同步状态
时,它会将同步状态加1,将同步状态改为非空闲,于是其他线程挂起等待。在修改同步状态的同时,
并记录下自己的线程,作为后续重入的依据,即一个线程持有某个对象的锁时,再次去获取这个对象的
锁是可以成功的。如果是不可重入的锁的话,就会造成死锁。
ReentrantLock会涉及到公平锁和非公平锁,实现关键在于成员变量 sync 的实现不同,这是锁实现互斥
同步的核心。
CAS
volatile是java虚拟机提供的轻量级的同步机制具有以下特点:
1.1.保证可见性
2.2.不保证原子性
1.3.禁止指令重排
AQS全称 AbstractQueuedSynchronizer ,即抽象的队列同步器,是一种用来构建锁和同步器的框架。
独享锁/共享锁
互斥锁/读写锁
乐观锁/悲观锁
分段锁
Synchronized简介
线程安全是并发编程中的至关重要的,造成线程安全问题的主要原因:
临界资源, 存在共享数据
多线程共同操作共享数据
而Java关键字synchronized,为多线程场景下防止临界资源访问冲突提供支持, 可以保证在同一时刻,只有一个线程可以执行某个方法或某个代码块操作共享数据。
即当要执行代码使用synchronized关键字时,它将检查锁是否可用,然后获取锁,执行代码,最后再释放锁。而synchronized有三种使用方式:
synchronized方法: synchronized当前实例对象,进入同步代码前要获得当前实例的锁
synchronized静态方法: synchronized当前类的class对象 ,进入同步代码前要获得当前类对象的锁
synchronized代码块:synchronized括号里面的对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁
Synchronized原理
synchronized 是JVM实现的一种锁,其中锁的获取和释放分别是monitorenter 和 monitorexit 指令。
加了 synchronized 关键字的代码段,生成的字节码文件会多出 monitorenter 和 monitorexit 两条指令,并且会多一个 ACC_SYNCHRONIZED 标志位,
当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。
在方法执行期间,其他任何线程都无法再获得同一个monitor对象。其实本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需通过字节码来完成。
在Java1.6之后,sychronized在实现上分为了偏向锁、轻量级锁和重量级锁,其中偏向锁在 java1.6 是默认开启的,轻量级锁在多线程竞争的情况下会膨胀成重量级锁,有关锁的数据都保存在对象头中。
偏向锁:在只有一个线程访问同步块时使用,通过CAS操作获取锁
轻量级锁:当存在多个线程交替访问同步快,偏向锁就会升级为轻量级锁。当线程获取轻量级锁失败,说明存在着竞争,轻量级锁会膨胀成重量级锁,当前线程会通过自旋(通过CAS操作不断获取锁),后面的其他获取锁的线程则直接进入阻塞状态。
重量级锁:锁获取失败则线程直接阻塞,因此会有线程上下文的切换,性能最差。
JVM
堆
Java堆(Java Heap) 是虚拟机所管理的内存中最大的一块。 Java堆是被所 有线程共享的一块内存区域, 在虚拟机启动时创建。 此内存区域的唯一目的就是存放对象实例
年轻代(Young Gen):年轻代主要存放新创建的对象,内存大小相对会比较小,垃圾回收会比较频繁。年轻代分成1个Eden Space和2个Suvivor Space(from 和to)。
2)年老代(Tenured Gen):年老代主要存放JVM认为生命周期比较长的对象(经过几次的Young Gen的垃圾回收后仍然存在),内存大小相对会比较大,垃圾回收也相对没有那么频繁。
方法区(Method Area) 与Java堆一样, 是各个线程共享的内存区域, 它用于存储已被虚拟机加载 的类型信息、常量、 静态变量、 即时编译器编译后的代码缓存等数据。
双亲委派模型工作过程是:如果一个类加载器收到类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成。每个类加载器都是如此,只有当父加载器在自己的搜索范围内找不到指定的类时(即 ClassNotFoundException ),子加载器才会尝试自己去加载。
垃圾回收算法分类两类,第一类算法判断对象生死算法,如引用计数法、可达性分析算法 ;第二类收集死亡对象方法有四种,如标记-清除算法、标记-复制算法、标记-整理算法。
垃圾回收-对象是否已死
判断对象是否存活 -
引用计数算法
可达性分析算法
从起始点开始向下搜索到对象的路径。
搜索所经过的路径称为引用链(Reference Chain),当一个对象到任何GC Roots都没有引用链时,则表明对象“不可达”,即该对象是不可用的。
垃圾收集算法:
分代收集名为理论, 实质是一套符合大多数
程序运行实际情况的经验法则, 它建立在两个分代假说之上:
1) 弱分代假说(Weak Generational Hypothesis) : 绝大多数对象都是朝生夕灭的。
2) 强分代假说(Strong Generational Hypothesis) : 熬过越多次垃圾收集过程的对象就越难以消亡。
这两个分代假说共同奠定了多款常用的垃圾收集器的一致的设计原则: 收集器应该将Java堆划分 出不同的区域,
然后将回收对象依据其年龄(年龄即对象熬过垃圾收集过程的次数) 分配到不同的区 域之中存储。
在Java堆划分出不同的区域之后, 垃圾收集器才可以每次只回收其中某一个或者某些部分的区域 ——因而才有了“Minor GC”“Major GC”“Full GC”这样的回收类型的划分; 也才能够针对不同的区域安 排与里面存储对象存亡特征
相匹配的垃圾收集算法——因而发展出了“标记-复制算法”“标记-清除算 法”“标记-整理算法”等针对性的垃圾收集算法。
标记-清除算法:
标记-清除算法有两个不足之处:
第一个是执行效率不稳定, 如果Java堆中包含大量对 象, 而且其中大部分是需要被回收的, 这时必须进行大量标记和清除的动作, 导致标记和清除两个过 程的执行效率都随对象数量增长而降低;
第二个是内存空间的碎片化问题, 标记、 清除之后会产生大 量不连续的内存碎片, 空间碎片太多可能会导致当以后在程序运行过程中需要分配较大对象时无法找 到足够的连续内存而不得不提前触发另一次垃圾收集动作。
标记-复制算法:
它将可用 内存按容量划分为大小相等的两块, 每次只使用其中的一块。 当这一块的内存用完了, 就将还存活着 的对象复制到另外一块上面, 然后再把已使用过的内存空间一次清理掉。针对活下来的对象少。
但是这种算法也有缺点:
1)需要提前预留一半的内存区域用来存放存活的对象(经过垃圾收集后还存活的对象),这样导致可用的对象区域减小一半,总体的GC更加频繁了
2)如果出现存活对象数量比较多的时候,需要复制较多的对象,成本上升,效率降低
3)如果99%的对象都是存活的(老年代),那么老年代是无法使用这种算法的。
标记-整理算法
垃圾收集器
ZOOKER
MYSQL
事务特性
事务隔离级别 以及原理实现 redolog undolog
分布式事务 分库分表