1.微服务是什么
微服务是一种分布式架构,分布式架构就是把服务拆分。在传统单体架构中,我们把所有服务都写在一起,随着业务的扩大我们的代码耦合度越来越高,后期维护起来也比较不方便。
微服务就是把模块拆分,把我们的项目分离成多个子项目,每个子项目之间独立开发部署,子项目也有自己独立的功能,这些独立的子项目就形成了微服务,不同的子项目就形成了一个微服务集群。
2.分布式事务
在了解分布式事务之前,先了解事务的概念,事务一般指的是数据库事务,具有4个特性:原子性、一致性、隔离性、持久性。
原子性:单个事务执行,要么成功,要么失败
一致性:事务运行前后的完整性必须保持一致
隔离性:防止事务并发执行
持久性:事务结束后不会丢失
数据库事务有这4个特性,分布式事务也不例外。
在微服务架构下,服务之间通过RPC远程调用。对于单机事务来说,多了网络通信这一不确定因素,原来有成功失败两种结果,现在变成成功失败未知3种结果。分布式事务就是在这种不确定性通信下实现的事务特性。网络波动,容易导致信息丢失,机器宕机,消息乱序,信息错误,不可靠的tcp.
分布式事务的方案:
2PC、3PC。
2PC是一种协议,它的作用保证在分布式系统中每个节点要不都提交事务,要么都取消事务。3PC引入超时机制来解决2PC的同步阻塞问题。
3.mysql和oracle中的事务隔离级别区别
mysql事务自动提交,mysql中的innodb支持事务
orcale使用commit手动提交事务。
1、脏读
脏读是指在一个事务处理过程里读取了另一个事务未提交的数据。
2、不可重复读
不可重复读:一个事务中多次查询时结果不一样,这是由于在查询间隔,被另一个事务修改并提交了
3、虚读(幻读)
幻读是事物中一种非独立的现象,幻读和不可重复读都是读取已提交的数据
mysql有4种隔离级别
可重复读(默认):可避免脏读、不可重复读的发生
读未提交 :最低级别,任何情况都无法保证
读已提交 :可避免脏读
串行化:可避免脏读、不可重复读、幻读的发生。
oracle存在两种隔离级别
读已提交(默认),可避免脏读
串行化:可避免脏读、不可重复读、幻读的发生。
oralce不会出现脏读,天生读写不阻塞。
没有并发就没有锁,在事务中隔离级别越高,效率越低。
orcale,mysql的区别
1、MySQL有自增型主键,创建表的时候可以通过指定auto_increment实现主键自增。Oracle没有自增型主键,一般通过序列实现。
2、MySQL默认的事务隔离级别是可重复读,而Oracle默认是读已提交。
3、MySQL在存储引擎是MyISAM的时候是不支持事务的,存储引擎是InnoDB的时候才支持事务,而Oracle都支持事务。
4、MySQL的字符串是用双引号包着,而Oracle用的是单引号。
5、MySQL和Oracle都用group by进行分组,而Oracle使用group by 的时候,select查询的字段必须包含group by 使用到的字段,不然就会报错。
6、MySQL使用limit进行分页,而Oracle没有limit,需要用rownum和嵌套查询实现分页。
7、MySQL中有null值和空字符串,而Oracle中只有null值,没有空字符串。
8、MySQL日期格式转换是用Date_format( ,‘%Y-%m-%d’),而Oracle用to_char( ,‘yyyy-mm-dd’)或者to_date
4.线程的创建方式
1)继承Thread类创建线程
public class MyThread extends Thread{//继承Thread类
public void run(){
//重写run方法
}
}
public class Main {
public static void main(String[] args){
new MyThread().start();//创建并启动线程
}
}
2)实现Runnable接口创建线程
public class MyThread2 implements Runnable {//实现Runnable接口
public void run(){
//重写run方法
}
}
public class Main {
public static void main(String[] args){
//创建并启动线程
MyThread2 myThread=new MyThread2();
Thread thread=new Thread(myThread);
thread().start();
//或者 new Thread(new MyThread2()).start();
}
}
3)使用Callable和Future创建线程
public static class MyThread3 implements Callable<Integer> {
@Override
public Integer call() throws Exception {
return 15 + 16;
}
}
public class Main {
public static void main(String[] args) {
MyThread3 th = new MyThread3();
//使用Lambda表达式创建Callable对象, FutureTask类来包装Callable对象
/*FutureTask<Integer> future = new FutureTask<>(
() -> 5 + 6
);*/
FutureTask<Integer> future = new FutureTask<>(new MyThread3());
//实质上还是以Callable对象来创建并启动线程
new Thread(future, "有返回值的线程").start();
try {
//get()方法会阻塞,直到子线程执行结束才返回
System.out.println("子线程的返回值:" + future.get());
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
4)使用线程池例如用Executor框架
线程池的创建的4种方式
newCachedThreadPool创建可缓存线程池、
newFixedThreadPool创建定长线程池、
newScheduledThreadPool创建定长线程池、
newSingleThreadExecutor创建单线程化线程池。
5.Spring的生命周期
普通new一个对象无引用时会被垃圾回收机制回收,而由IOC托管的对象,由spring生命周期容器控制。
Spring的生命周期,就是bean的生命周期,从bean开始装载,默认已单例实例化。
1、实例化bean(调用构造方法)
对于BeanFactory容器,当客户向容器请求时(bean此时未初始化),需要注入另一个未初始化依赖。容器调用createBean进行实例化
ApplicationContent容器,启动结束,便实例化所有的bean
BeanDefinition对信息实例化
BeanWrapper设置对象属性接口(避免使用反射器)
2、属性注入
设置依赖对象(依赖注入)实例化对象被封在BeanWrapper中,Spring根据BeanDefinition中信息进行依赖注入,注入Aware接口:Spring会检测对象是否实现Aware接口,将Aware实例注入给bean
BeanPostProcessor对象自定义处理
3、初始化bean(有多种方式可以指定bean的初始化方法,如init方法)
initializingBean init-method
DisposableBean destroy-method
4、使用
5、销毁
总结:实例化bean—》注入属性—》调用init—》bean可用了—》销毁
6.zuul网关
统一入口:未全部为服务提供一个唯一的入口,网关起到外部和内部隔离的作用,保障了后台服务的安全性。
鉴权校验:识别每个请求的权限,拒绝不符合要求的请求。
动态路由:动态的将请求路由到不同的后端集群中。
减少客户端与服务端的耦合:服务可以独立发展,通过网关层来做映射。
(ip白名单,routes路由配置)
7.对redis的理解
redis是一个高性能的基于Key-Value结构存储的NoSQL开源数据库。大部分公司采用Redis来实现分布式缓存,用来提高数据查询效率。
redis是单线程应用程序,占用内存较少,防止多线程相互竞争CPU带来上下文切换,无锁操作性能更高,相对而言对硬件成本相对低廉。
redis五种类型:string(字符串),hash(哈希),list(列表),set(集合),sort set (有序集合)
redis支持分布式集群,磁盘持久化:第一种持久化方式RDB 存在磁盘上,每隔一段时间进行持久化操作。第二种是AOF ,把指令记下来,下次启动再重复执行,每次进行命令都会持久化。
redis中缓存穿透、雪崩、击穿
① 缓存穿透:大量请求根本不存在的key
② 缓存雪崩:redis中大量key集体过期
③ 缓存击穿:redis中一个热点key过期(大量用户访问该热点key,但是热点key过期)
穿透解决方案:
① 缓存穿透:
对空值进行缓存
设置白名单
使用布隆过滤器
网警
②雪崩解决方案:
进行预先的热门词汇的设置,进行key时长的调整
实时调整,监控哪些数据是热门数据,实时的调整key的过期时长
使用锁机制
③击穿解决方案:
进行预先的热门词汇的设置,进行key时长的调整
实时调整,监控哪些数据是热门数据,实时的调整key的过期时长
使用锁机制(只有一个线程可以进行热点数据的重构)
8.为什么使用消息队列,使用消息队列的好处
为什么使用消息队列
异步处理,流量控制,服务解耦
消息队列数据丢失怎么办
丢消息的关键点有3个:
Producer 发送消息的过程
消息队列的消息存储
Consumer 消费消息的过程
数据被重复消费了咋办
保证消息不被重复消费的关键是保证消息队列的幂等性
(幂等性一般指 producer 投递了多少消息,consumer 就消费了多少消息,不会发生消息丢失或者消息重复的情况)
一个请求,请求了10次,2次错的,8次对的怎么排查。
一个程序掉进后端了打印log了卡住了咋办
9.有关SqlSessionFactory
SqlSessionFactory一旦被创建,应该在应用执行期间都存在.在应用运行期间不要重复创建多次,建议使用单例模式.SqlSessionFactory是创建SqlSession的工厂.
10.java虚拟机
java虚拟机是一种抽象化的计算机,通过在实际计算机上仿真模拟各种计算机功能来实现的。jvm有自己完善的硬件结构,处理器,堆栈,寄存器,指令系统。jvm屏蔽了OS相关信息,使java程序只需生成在java虚拟机上运行的目标代码,就可以在不同平台上修改。
jvm从程序到源码的3个步骤: .java文件,class文件,机器码
11.java内存模型
java采用共享内存并发模型,主内存+多个工作内存
12.索引类型
1.普通索引:普通索引是最基本的索引,它没有任何限制,值可以为空;仅加速查询。
2.唯一索引:唯一索引与普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。
3.主键索引:主键索引是一种特殊的唯一索引,一个表只能有一个主键,不允许有空值。
4.组合索引:组合索引指在多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用组合索引时遵循最左前缀集合。
5.全文索引:全文索引主要用来查找文本中的关键字,而不是直接与索引中的值相比较。fulltext索引跟其它索引大不相同,它更像是一个搜索引擎,而不是简单的where语句的参数匹配。
13.oracle的存储过程
在oracle中,存储过程是为了完成特定功能的sql语句集,经编译后存储到数据库中,经过一次编译再次调用不需要再次编译,用户通过指定的存储过程的名字并给出参数来调用存储过程。
13.做过的项目难点
定时任务,a.b服务器都有,Redis做了锁的配置
xxljob是有的这个锁的控制机制的
14.面试项目相关
发送防疫短信:首先创建一个活动,进行条件选择,比如出行时间,目的地,是否进行营销触达。选定之后,进行控件拉取,控件包括3个流程,首节点包括出行、人群分组等,中间节点主要是业务方面,如短信控件、双城权益、邮件控件,最后节点是流程结束节点。 一个活动可以对应一个控件流程
做了一个营销触达,非营销触达。意思就是新增退订字段,在推Q的时候,加上这个字段,在接收数据的时候判断这个字段,如果字段是Y那么就是过滤退订旅客,触达成功给没退订的人会发送短信。反之为N只给所有人发短信
客票换开,第一次换开需要生成客票号,第二次换开生成新客票号,新生成的客票,需要关联原始的客票号。写一个
递归调用
Redis先删一下,再去存入数据库