面试题总结

一、Java SE*16

1.解释下java中的Stream()流

● Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。 使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

● Stream是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,负责存储数据,Stream流讲的是计算,负责处理数据!”

当项目中需要把 map 集合转换成 list 集合时,本来需要写很多代码,现在用 Stream()流可以很简单的就实现了

2.你认为的编码规范的风格是什么样子的

● 类名采用**大驼峰**的命名形式,
● 包名统一使用小写,
● **全局常量**和**类内常量**的命名采用字母全部大写,单词之间加**下画线**的方式
● **局部常量**则采用**小驼峰**的形式
● 可变变量一般常用**小驼峰**的命名形式,
● 在运算符、赋值、参数等之间使用**空格**来隔开各种元素之间的距离
● 缩进与空格
● 让一切东西都尽可能地“私有”——private。

3.hashMap的底层原理*6

HashMap基于哈希法(hashing)原理,我们通过put()和get()方法储存和获取对象。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,让后找到bucket位置来储存值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞了,对象将会储存在链表的下一个节点中。 HashMap在每个链表节点中储存键值对对象。

当两个不同的键对象的hashcode相同时会发生什么? 它们会储存在同一个bucket位置的链表中。键对象的equals()方法用来找到键值对。

因为HashMap的好处非常多,我曾经在电子商务的应用中使用HashMap作为缓存。因为金融领域非常多的运用Java,也出于性能的考虑,我们会经常用到HashMap和ConcurrentHashMap。

4.hashmap和hashtable区别?

HashMapHashtable
底层是一个哈希表底层是一个哈希表
线程不安全的集合线程安全的集合
速度快速度慢
可以存储null值,null键不能存储null值,null键
不同步同步

Hashtable和Vector集合一样,在jdk1.2版本之后被更先进的集合(HashMap,ArrayList)取代了

Hashtable的子类Properties(配置文件)依然活跃在历史舞台   xml
Properties集合是一个唯一和IO流相结合的集合


两者都是用key-value方式获取数据。Hashtable是原始集合类之一(也称作遗留类)。HashMap作为新集合框架的一部分在Java2的1.2版本中加入。它们之间有一下区别:

  ● HashMap和Hashtable大致是等同的,除了非同步和空值(HashMap允许null值作为key和value,而Hashtable不可以)。

  ● HashMap没法保证映射的顺序一直不变,但是作为HashMap的子类LinkedHashMap,如果想要预知的顺序迭代(默认按照插入顺序),你可以很轻易的置换为HashMap,如果使用Hashtable就没那么容易了。

  ● HashMap不是同步的,而Hashtable是同步的。

  ● 迭代HashMap采用快速失败机制,而Hashtable不是,所以这是设计的考虑点。

5.HashMap的扩容机制

默认的初始容量 16,加载因子0.75,扩容2倍
HashMap在JDK1.8之前:底层实现是数组+链表,扩容机制是当table中元素的个数已经达到阈值(table.length*0.75)时并且新添加[index]桶已经是非空,那么table.length需要扩容为2倍。

HashMap在JDK1.8之后:底层实现是数组+链表/红黑树,扩容机制(1)是当table中元素的个数已经达到阈值(table.length*0.75)时并且新添加[index]桶已经是非空,那么table需要扩容为2倍。(2)当添加到[index]下时,发现[index]下的链表结点个数已经达到8个,而table的长度未达到64,此时table.length也会扩容为2倍

6.hashset 和hashmap的区别

HashMapHashSet
实现了Map接口实现Set接口
存储键值对仅存储对象
调用put()向map中添加元素调用add()方法向Set中添加元素
HashMap使用键(Key)计算HashcodeHashSet使用成员对象来计算hashcode值,对于两个对象来说hashcode可能相同,所以equals()方法用来判断对象的相等性,如果两个对象不同的话,那么返回false
HashMap相对于HashSet较快,因为它是使用唯一的键获取对象HashSet较HashMap来说比较慢
HashSet底层其实是用HashMap实现存储的, HashSet封装了一系列HashMap的方法. 依靠HashMap来存储元素值,(利用hashMap的key键进行存储), 而value值默认为Object对象. 所以HashSet也不允许出现重复值, 判断标准和HashMap判断标准相同, 两个元素的hashCode相等并且通过equals()方法返回true.

7.set,list,map的区别*2

ListSetMap
继承自Collection接口继承自Collection接口和Collection接口同级
有序无序hashMap无序,LinkedHashMap有序
单值对象单值对象储存键值对的数据
可重复不可重复key是不允许重复的,value是可以重复的
查询效率高,增删效率低查询效率低,增删效率高查询和增删效率都高
1、List、Set都是继承自Collection接口,Map则不是

2、List特点:元素有放入顺序,元素可重复 ,Set特点:元素无放入顺序,元素不可重复,重复元素会覆盖掉,(注意:元素虽然无放入顺序,但是元素在set中的位置是有该元素的HashCode决定的,其位置其实是固定的,加入Set 的Object必须定义equals()方法 ,另外list支持for循环,也就是通过下标来遍历,也可以用迭代器,但是set只能用迭代,因为他无序,无法用下标来取得想要的值。) 

3、Set和List对比: 

	Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。 

	List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。 

4、Map适合储存键值对的数据

5、线程安全集合类与非线程安全集合类 :

	LinkedList、ArrayList、HashSet是非线程安全的,Vector是线程安全的;

	HashMap是非线程安全的,HashTable是线程安全的;

	StringBuilder是非线程安全的,StringBuffer是线程安全的。


8.arraylist和linkedlist主要区别*2

ArrayListVectorLinkedList
动态数组动态数组双向链表
增删慢增删慢增删快
查找快查找快查找慢
JDK1.7之前底层是默认长度为10的数组,默认扩容1.5倍,JDK1.7之后底层默认是空数组底层默认是长度为10的数组,默认扩容2.0倍当频繁在集合中插入、删除元素时,效率较高,但是查找遍历的效率较低

9.stringbuffer和stringbuilder的区别说一下/为什么是线程安全的

String不可变字符序列,字符串常量存储在常量池中,属于引用数据类型,底层使用 char 型数组
StringBuilder可变的字符序列,底层是长度为16的 char 型数组,默认扩容2倍+2,线程不安全,比StringBuffer的速度更快
StringBuffer和 StringBuilder类似,但是在所有的方法上都加了 synchronized 同步锁线程安全,适用于多线程
执行效率StringBuilder > StringBuffer > String

10.反射是什么?

反射就是动态加载对象,并对对象进行剖析。Java反射机制的作用:
(1)在运行时创建任意类型的对象
(2)在运行时获取任意类型的信息
(3)在运行时获取和设置任意属性值
(4)在运行时调用任意对象的方法

11.jdk用的是哪个版本?(1.8)

jdk1.8

12.jdk1.8和其他版本有哪些区别?

一、接口的默认方法静态方法(1.9加了私有方法)
二、Lambda 表达式
三、函数式接口
四、使用 :: 关键字来传递方法或者构造函数引用
五、新的Stream API

13.IO说一下?

数据的传输,可以看做是一种数据的流动,按照流动的方向,以内存为基准,分为输入input 和输出output ,即流向内存是输入流,流出内存的输出流。

Java中I/O操作主要是指使用java.io包下的内容,进行输入、输出操作。输入也叫做读取数据,输出也叫做作写入数据。

IO流分类
根据数据的流向分为:输入流和输出流。

输入流 :把数据从其他设备上读取到内存中的流。
输出流 :把数据从内存 中写入到其他设备上的流。
根据数据的类型分为:字节流和字符流。

字节流 :以字节为单位,读写数据的流。
字符流 :以字符为单位,读写数据的流。

14.java里面"=="和"equals"的区别

"=="是直接比较的两个对象的堆内存地址,如果相等,则说明这两个引用实际是指向同一个对象地址的。

Object的equals()方法实际上效果和==相同,如果重写了equals()方法就按照重写的规则比较。

15.手写一个冒泡排序


int temp;//定义一个临时变量
for (int i = 1; i < arr.length; i++) {
			for (int j = 0; j < arr.length - i - 1; j++) {
				if (arr[j] > arr[j + 1]) {
					temp = arr[j];
					arr[j] = arr[j + 1];
					arr[j + 1] = temp;
				}
			}
		}

16.new String(“123”)创建了几个对象

当常量池中不存在123时,创建了两个对象。
当常量池中存在123时,创建了一个对象。

二、MySQL*16

1.你们使用Mysql存放数据的原因

 体积小、速度快、总体拥有成本低,开源; 支持多种操作系统

2.Mysql的优化*3

1.观察服务器运行状态是否存在周期性波动
2.如果存在周期性波动,加缓存更改缓存失效策略
3.如果不存在周期性波动,或者加缓存后仍然有延迟或者卡顿,就开启慢查询,使用 explain 查看 SQL 的执行细节
4.如果 SQL 的等待时间长,就调优服务器参数
5.如果 SQL 的执行时间长,就优化索引、优化多表 join,优化数据库表
6.如果不是 SQL 的问题,说明 SQL 到达瓶颈,此时需要进行读写分离优化、分库分表优化

3.Mysql优化的具体场景

using filesort:     排序字段没有用到索引,使用了文件外排序:需要优化
					如果出现了where 过滤和order by排序的话,要为where后边的条件和order by建立组合索引。

4.sql优化,一般在哪些字段上加索引

主键、where 后面字段、外键 或者 关联字段、排序、分组字段、

5.数据库用的是mysql吗?

6.数据库事务隔离级别?*2

事务隔离级别脏读不可重复读幻读
读未提交(read-uncommitted)
读已提交(read-committed)×
可重复读(repeatable-read)××
串行化(serializable)×××
脏读:
事务A读取了事务B更新的数据,事务B未提交并回滚数据,那么A读取到的数据是脏数据
不可重复读:
事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。
幻读:
系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A更改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。
  小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表。

7.where和having的区别

wherehaving
where 是一个约束声明,用来约束数据库的数据having 是一个过滤声明,对查询结果进行过滤
where 用在返回结果集之前(group by)having 用在查询结果集之后
where 中不能使用聚合函数having 后可以使用聚合函数

8.sql语句的执行顺序

1.首先去缓存查询,如果查到则返回结果
2.如果缓存没查到,则会进入解析器,进行语法解析、语义解析、生成语法树
3.然后进入查询优化器进行索引优化、多表join优化等,生成执行计划
4.最后,执行器真正的负责了MySQL中数据的存储和提取,执行器执行完毕后会返回结果,并把结果存入缓存

9.聚簇索引和非聚簇索引的区别?

聚簇索引非聚簇索引
索引的字段一般是主键ID,而且要自增索引的字段不是主键ID
索引即数据,不需要进行回表操作需要进行回表操作
不用主动定义,默认就有,且只有一个需要主动定义,一张表可以有多个非聚簇索引
排序和范围查找速度快增删改效率较高

10.数据库没有的字段,你设计类的时候怎么处理的?

使用注解 @TableField(exist = false) 表示当前属性不是数据库的字段

11.@tableLogic注解是什么?

@TableLogic:表示逻辑删除      
效果:在字段上加上这个注解再执行BaseMapper的删除方法时,删除方法就会变成修改

12.索引优化type等级是哪几个?你对索引优化有什么了解?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MzeyXR5a-1640358639501)(C:\Users\xyh\AppData\Roaming\Typora\typora-user-images\image-20211223164754670.png)]

type显示的是访问类型,是较为重要的一个指标,结果值从最好到最坏依次是: 
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL 
system > const > eq_ref > ref > range > index > ALL
一般来说,得保证查询至少达到range级别,最好能达到ref。


索引的本质:索引是数据结构。你可以简单理解为“排好序的快速查找数据结构”。
优势:
	类似大学图书馆建书目索引,提高数据检索的效率,降低数据库的IO成本
	通过索引列对数据进行排序,降低数据排序的成本,降低了CPU的消耗
劣势:索引列占用空间,降低更新表的速度

13.mysql的引擎 innodb和myisam的主要区别

MyISAM引擎Innodb引擎
是否支持事务不支持支持
是否支持行锁不支持支持
是否支持外键不支持支持
是否支持表锁支持支持
是否支持XA协议不支持支持
读的性能
是否支持聚簇索引不支持支持
数据的组织结构3个文件2个文件
是否支持MVCC机制不支持支持
适用场景从机slave主机master

14.复合索引 如何考虑怎么创建

 即一个索引包含多个列
 当出现where查询条件,并且需要排序的时候。或者需要根据多个条件查询的时候,需要创建复合索引
 随表一起建索引:
CREATE TABLE customer (
  id INT(10) UNSIGNED  AUTO_INCREMENT ,
  customer_no VARCHAR(200),
  customer_name VARCHAR(200),
  PRIMARY KEY(id),
  KEY (customer_name),
  UNIQUE (customer_name),
  KEY (customer_no,customer_name)
);
单独建索引:
CREATE INDEX idx_customer_no_name ON customer(customer_no,customer_name); 
删除索引:
DROP INDEX idx_customer_no_name  on customer ;

15.MySQL的视图了解吗?使用视图会影响性能吗

1.视图是将一段查询 SQL 封装为一个虚拟的表。 这个虚拟表只保存了 SQL 逻辑,不会保存任何查询结果。
2.封装复杂sql语句,提高复用性
3.逻辑放在数据库上面,更新不需要发布程序,面对频繁的需求变更更灵活
4.使用视图会大大提升性能

16.MySQL索引失效的情况和解决办法*2

索引失效的情况:
	1.全值匹配,比如索引的字段为性别这种没有区分度的字段
	2.最佳左前缀原则,检索数据时从联合索引的最左边,一旦跳过某个字段,索引后面的字段都无法被使用
	3.主键插入顺序,如果主键不自增的话,会造成页面分裂,损耗性能
	4.计算、函数、类型转换会导致索引失效
	5.不等于(!= 或者<>)会导致索引失效
	6.范围条件右边的列会导致索引失效
	7.is not null无法使用索引,is null可以使用索引
	8.like以通配符%开头会导致索引失效
	9.OR 前后存在非索引的列,索引失效 ,只要有条件列没有进行索引,就会进行 全表扫描
解决办法:
	1.在where中使用不到的字段,不要设置索引
	2.数据量小的表最好不要使用索引
	3.有大量重复数据的列上不要建立索引
	4.避免对经常更新的表进行过多的索引
	5.不建议用无序的值作为索引
	6.删除不再使用或者很少使用的索引
	7.不要定义冗余或重复的索引
	8.限制索引的数目

三、Spring*7

1.Spring是怎么解决Bean之间的循环依赖的

Spring三级缓存
Spring 在创建 bean 的时候并不是等它完全完成,而是在创建过程中将创建中的 bean 的 ObjectFactory 提前曝光(即加入到 singletonFactories 三级缓存中)这样,一旦下一个 bean 创建的时候需要依赖 bean ,则从三级缓存中获取
多例模式不能解决循环依赖

2.spring事务传播机制

事务传播行为类型说明
REQUIRED支持当前事务,如果不存在,就新建一个
SUPPORTS支持当前事务,如果不存在,就不使用事务
MANDATORY支持当前事务,如果不存在,抛出异常
REQUIRES_NEW如果有事务存在,挂起当前事务,创建一个新的事务
NOT_SUPPORTED以非事务方式运行,如果有事务存在,挂起当前事务
NEVER以非事务方式运行,如果有事务存在,抛出异常
NESTED如果当前事务不存在,就新建一个事务运行,如果存在,则嵌套事务执行

3.@Component注解部分场景不生效问题

application类和 @Component的类不在一个工程目录下,因此不被加载

1.出现问题的原因:@Component这个注解把该类注入到spring容器中了,但是在拦截器之中不生效,再在Utils使用到dao,我们就不能直接注入了

 解决方案:使用ApplicationContextAware解决问题。而ApplicationContextAware实现了这个接口的bean,当spring容器初始化的时候,会自动的将ApplicationContext注入进来

4.介绍下SpringSecurity你们在项目中的使用

spring security 的核心功能主要包括:

认证 (你是谁)
授权 (你能干什么)
攻击防护 (防止伪造身份)
     其核心就是一组过滤器链,项目启动后将会自动配置。最核心的就是 Basic Authentication Filter 用来认证用户的身份,一个在spring security中一种过滤器处理一种认证方式。

5.Spring源码看过没?

看过,也只是看过

6.Aop是什么?项目用到了吗?

概念AOP 也就是面向切面编程,是面向对象的一种补充,在不修改源代码的基础上对业务功能进行增强
作用减少系统的重复代码、降低系统的耦合度、提高了系统的可维护性、常用于事务、日志处理等
原理基于动态代理( JDK 的动态代理、Cglib动态代理)
AOP实现缓存:
	1.自定义缓存注解@GmallCache和注解的属性(类似于事务@Transactional)
	2.编写切面类,使用环绕通知实现缓存的逻辑封装
	3.在业务方法上添加自定义注解,即可完成缓存功能

7.spring的IOC和AOP讲一下

IOC(Inverse of Control)控制反转    一种设计思想 
		Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。
DI(Dependecy Injection)依赖注入		
		由容器动态的将某个依赖关系注入到组件之中
AOP(Aspect Oriented Programming) 面向切面编程
	
AOP的作用和优势:

    作用:从定义中来看,就是为了在程序运行期间,不修改源码对已有方法进行增强。

    优势:减少重复代码 提交了开发效率 维护方便

    实现方式: 就是动态代理的技术

    具体的作用:实现事务的控制 日志 和 安全模块

    动态代理:

    实现动态代理的两种常用的方式:

    基于接口的动态代理:JDK动态代理
    基于继承的动态代理:CGlib动态代理

四、SpringMVC*1

1.springMVC执行流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KhJGy1US-1640358639503)(C:\Users\xyh\Desktop\知识点\springmvc原理.png)]

(1)用户向服务器发送请求,请求被springMVC 前端控制器 DispatchServlet 捕获;
(2)DispatcherServle 对请求 URL 进行解析,得到请求资源标识符(URL),然后根据该 URL 调用 HandlerMapping将请求映射到处理器 HandlerExcutionChain;
(3)DispatchServlet 根据获得 Handler 选择一个合适的HandlerAdapter 适配器处理;
(4)Handler 对数据处理完成以后将返回一个 ModelAndView()对象给 DisPatchServlet;
(5)Handler 返回的 ModelAndView() 只是一个逻辑视图并不是一个正式的视图, DispatcherSevlet  通过ViewResolver 试图解析器将逻辑视图转化为真正的视图View;
(6)DispatcherServle 通过 model 解析出 ModelAndView()中的参数进行解析最终展现出完整的 view 并返回给客户端;

五、Mybatis*5

1.mybatis单表查询和多表查询有什么区别嘛?

2.mybatis里resultMap 可以继承吗?怎么实现

可以继承,定义B的ResultMap的时候,可以使用extends属性来继承A的ResultMap

3.resultMap 和resultType的区别 作用是什么(这里问到了假如我的JavaBean里有List字段我该使用什么)

自动映射 ,通过resultType来指定要映射的类型即可。 
Select age+height as age_height from student ;
<result property=”ageHeight” column=”age_height”/>
自定义映射 通过resultMap来完成具体的映射规则,指定将结果集中的哪个列映射到对象的哪个属性。

4.mybatis用什么接收参数的#{}和${}的区别

#{}${}
预编译处理字符串替换
处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值处理 ${}时,就是把{}内的值替换成变量的值
可以有效的防止SQL注入,安全有 SQL 注入的风险,不安全

5.mybatis使用公共的sql是怎么使用的(这个问题不了解他想问的什么 没回答出来)

1.使用sql标签抽取重复出现的SQL片段
<sql id="mySelectSql"> select emp_id,emp_name,emp_age,emp_salary,emp_gender from t_emp </sql>
2.使用include标签引用声明的SQL片段
<include refid="mySelectSql"/>

六、Linux*2

1.Linux我要查找日志但是这个日志很大我怎么查看日志最后几行的信息或者前面的信息*2

netstat -anp | less          # 分屏查看
cat  test.log | tail -n 20  # 查看test.log倒数20行
cat  test.log | head -n 20  # 查看test.log前20行
tail -n 5 word 				 # 查看文件末尾5行的内容

2.Linux的tail命令了解吗 还有top

tail:
	1.使用 -F 参数实时查看文件末尾新增的内容
	2.使用 -n 参数可以查看文件末尾的内容
top:
	1.实时查看系统运行情况和健康状态
	2.参数 -d ,间隔秒数
	3.参数 -i ,不显示任何闲置或者僵死进程	
	4.参数 -p 进程id ,通过进程id监控单一进程

七、Redis*20

1.Redis中的一个key的value是多少

512M

2.redis怎么保证数据的一致性?

1.双写模式(写数据库,写缓存)
	1.先写redis:写redis成功了(新)  -->  mysql失败了(旧) -->  数据不一致
	2.先写mysql:写mysql成功了(未提交)	-->  写redis成功了(新) --> 代码异常或者服务器宕机了,mysql会回滚(旧) --> 数据不一致
2.失效模式(缓存失效(删除缓存),写数据库——删除redis	高并发下出现数据不一致)
	1.先删redis:
		a用户:先删了redis(空)  			-->   再去写mysql  						--> 提交(mysql 新)
		b用户:					查询数据 --> 先查redis --> 再查myql,放入redis(旧)
	2.先写mysql
		a用户:写mysql成功了 (未提交)-->  删除redis(空)							-->					提交数据(新)
		b用户:										查询数据 --> redis --> mysql,放入redis(旧)
3.双删模式
	1.删除缓存(常规操作——防止旧数据堆积)
	2.写mysql
	3.提交事务
	4.异步删除(借助于AOP后置通知)(保险操作——防止中间操作改变数据)

4.canal中间件-阿里开源
	基于mysql的binlog日志
	canal的工作原理就是把自己伪装成MySQL slave,
	模拟MySQL slave的交互协议向MySQL Mater发送 dump协议,
	MySQL mater收到canal发送过来的dump请求,
	开始推送binary log给canal,然后canal解析binary log,再发送到存储目的地,
	比如MySQL,Kafka,Elastic Search等等。

3.redis数据结构有哪些

1.Redis字符串(String): String的数据结构为简单动态字符串
2.Redis列表(List) : 底层是个双向链表,对两端的操作性能很高,通过索引下标的操作中间的节点性能会较差
3.Redis集合(Set) : 功能与 list 类似是一个列表的功能,特殊之处在于 set 是可以自动排重的
4.Redis哈希(Hash): Redis hash是一个string 类型的 field 和 value 的映射表,hash特别适合用于存储对象
5.Redis有序集合Zset(sorted set): zset与普通集合set非常相似,不同之处是有序集合的每个成员都关联了一个评分,可以实现排序
6.Bitmaps:合理地使用操作位能够有效地提高内存使用率和开发效率
7.HyperLogLog:是一种用来做基数统计的算法
8.Geospatial:该类型,就是元素的2维坐标,在地图上就是经纬度

4.redis的set和zset有什么区别

zset与普通集合set非常相似,是一个没有重复元素的字符串集合。不同之处是有序集合的每个成员都关联了一个评分,可以实现排序

5.redis缓存会出现哪些问题?你是怎样去解决的?*2

缓存写的一致性问题——双删模式

缓存常见问题:并发读
1.缓存穿透:大量请求访问不存在的数据,由于数据不存在,对应的缓存可能就没有,请求就会直达数据库,导致mysql服务器宕机。
		  解决方案:数据即使为null也缓存 	布隆过滤器
			
2.缓存雪崩:由于缓存时间相同,导致大量缓存数据同时过期,此时请求就会直达数据库
		  解决方案:给缓存时间添加随机值
	
3.缓存击穿:一个热点的key过期,此时大量请求直达数据库
		  解决方案:加分布式锁

6.分布式锁的原理

借助于redis中的命令setnx(key, value),key不存在就新增,存在就什么都不做。同时有多个客户端发送setnx命令,只有一个客户端可以成功,返回1(true);其他的客户端返回0(false)。

1. 加锁:多个客户端同时尝试获取锁(setnx)
2. 解锁:获取成功,执行业务逻辑,执行完成释放锁(del)
3. 重试:其他客户端等待重试

7.redis分布式锁怎么解决死锁?

1.设置锁的过期时间
2.实现可重入锁
具体见分布式锁的设计细节

8.分布式锁的设计细节

实现方式可靠性实现复杂度性能推荐使用
基于 Redis 实现较高简单最高
基于关系型数据库实现最高中等较差
基于 zookeeper 实现较高困难中等
1. 加锁:多个客户端同时尝试获取锁(setnx)
    1.独占排他:setnx 保证独占排他  问题:没有过期时间可能会导致死锁	不可重入
    2.防死锁(过期时间):set key value ex 30 nx   加锁和过期时间之间可以保证原子性  问题:不可重入 
    3.可重入:hash + lua脚本  实现可重入 问题:自动续期 
	4.自动续期:Timer定时器 + lua脚本	 判断自己的锁是否存在,存在则重置过期时间,实现自动续期

2. 解锁:获取成功,执行业务逻辑,执行完成释放锁(del)
	防误删(判断) 判断和删除原子性  可重入解锁
	1.初步:del指令	简单释放锁  问题:可能会导致误删
	2.先判断再删:lua脚本  防止误删同时保证了原子性	不可重入
	3.可重入:hash + lua脚本 
	4.续期:取消定时器
	
3. 重试:其他客户端等待重试
	递归
问题解决方法
setnx 刚好获取到锁,业务逻辑出现异常,导致锁无法释放 ,可能导致死锁问题设置过期时间,自动释放锁,set 时直接指定过期时间(保证加锁原子性)
当业务逻辑的执行时间大于设置的锁的过期时间,可能会导致释放其他服务器的锁,出现误删的情况setnx 获取锁时,设置一个唯一的 UUID,释放前获取这个值,判断是否自己的锁
删除操作仍然缺乏原子性,判断 UUID正确 后,执行删除操作前,锁刚好过期,这时仍然会出现误删的情况使用官方推荐使用的 LUA 脚本保证删除的原子性(保证解锁原子性)
程序的子任务代码也加锁,需要等待锁释放之后,再次获取锁成功,才能继续往下执行,可能出现死锁问题基于 Redis Hash + lua脚本 实现可重入锁
当业务逻辑的执行时间大于设置的锁的过期时间,多个线程执行业务逻辑,有线程安全问题和误删问题设置锁的过期时间自动续期(Timer定时器 + lua脚本),判断自己的锁是否存在,存在则重置过期时间
集群情况下锁机制可能失效使用RedLock算法(红锁算法,redis特有的)

9.redis分布式锁如果过期时间到了怎么办?

自动续期:设置锁的过期时间自动续期(Timer定时器 + lua脚本),判断自己的锁是否存在,存在则重置过期时间

10.还有哪些地方用到了分布式锁?*2

验库存、锁库存(保证原子性)

11.redlock讲一下你的理解?

解决集群下锁失效

RedLock的基本思路就是为锁准备多个副本,避免Redis主从切换的时候,数据丢失。


1. RedLock算法详细解释
    (1)跟ZK一样,多数派的思路。假设部署了5台Redis,获取锁的时候,从5台机器获取,只要超过半数(其中3台)获取到锁,就算获取锁成功。允许最多2台宕机。
    (2)锁必须有一个超时强制释放机制,假设10秒。避免客户端宕机,锁永远无法释放。
    (3)取每台机器的锁之前,记一下当前时间beginTime;取锁之后,再记一下当前时间endTime。 
         if endTime - beginTime > TTL(10秒),意味着刚获取到锁,就已经过期了,获取锁失败。
    (4)如果获取锁失败,也就是没有超过半数,则在所有Redis节点上执行释放锁操作。(为什么是释放所有节点,而不是只释放成功的那些节点?还是前面说的网络2将军问题,未成功的那些节点,可能只是返回客户端的时候超时了,但实际已经加了锁)
    (5)如果第(4)步里面释放节点失败,只能依赖第(2)步里面,锁的强制释放机制。

2. 表面看起来,这个算法蛮完美,解决了前面单机版Redis分布式锁的缺陷,那还存在什么问题呢?
问题1:  宕机重启之后,2个客户端拿到同一把锁。-  延迟重启
假设5个节点是A, B, C, D, E,客户端1在A, B, C上面拿到锁,D, E没有拿到锁,客户端1拿锁成功。 此时,C挂了重启,C上面锁的数据丢失(假设机器断电,数据还没来得及刷盘)。客户端2去取锁,从C, D, E 3个节点拿到锁,A, B没有拿到(还被客户端1持有),客户端2也超过多数派,也会拿到锁。
为此,Redis作者提出了延迟重启的办法:重启的时候,不立马重启,等待TTL时间之后再重启,保证这台机器此前参与的那些锁,全部过期,一笔勾销。

问题2: 时钟跳跃
刚上面讨论的方案严格依赖时钟,而5台机器上面的时钟是可能有误差的。
时钟跳跃的意思就是:实际时间只过了1s钟(假设),但系统里面2次时间之差可能是1分钟,也就是系统之间发生了跳跃。发生这种情况,可能是运维人员认为修改了系统时间。
时钟跳跃会产生2个后果:
(1)延迟重启机制失效。时钟跳跃可能导致机器挂了立马重启,从而出现上面的问题。
(2)时钟跳跃导致客户端拿到锁之后立马失效。endTime - beginTime 差值太大。这虽然不影响正确性,但影响拿锁的效率。
那么时钟回拨呢?endTime - beginTime会成为负值,不影响算法的正确性。

问题3: 客户端大延迟(比如full GC),2个客户端拿到同一把锁。
理论上,一切有超时强制释放机制的锁,都可能产生这个问题。服务端把锁强制释放了,但是客户端的代码并没有执行完,卡在了某个地方(比如full GC,或者其它原因导致进程暂停),这把锁被分配给了另外一个客户端。
针对这个问题,Redis又提出了watch dog机制。大致意思就是,锁快要到期之前,发现客户端业务逻辑还没执行完,就给锁续期,避免锁被强制释放,分配给另外一个客户端。但是,锁续期本身是个网络操作,也没办法保证续期一定成功!

从这个案例中,可以得到2个重要启示:
(1)在分布式系统中,严格依赖每台机器本机时钟的算法,都可能有风险。
(2)一切具有“超时强制释放机制”的锁,都可能导致客户端还在持有锁的情况下,锁被强制释放。

12.redlock的弊端呢?

参考11题

13.redis为什么性能好?

纯内存KV操作
内部是单线程实现的(不需要创建/销毁线程,避免上下文切换,无并发资源竞争的问题)
异步非阻塞的I/O(多路复用)

14.redis扩容机制说一下

15.redis的消息是怎么传递的?

Redis 的发布订阅,实际开发时发布订阅方面的需求还是要找专门的消息队列产品来完成

16.redis集群搭建过吗

搭建过

17.redis缓存机制 RDB和AOF的使用场景和优缺点

RDBAOF
定义指定的时间间隔内将内存中的数据集快照写入磁盘以日志的形式来记录每个写操作(增量保存
使用场景对数据完整性和一致性要求不高更适合使用 适合大规模的数据恢复对数据完整性和一致性要求比较高适合使用
优点节省磁盘空间
恢复速度快
1.备份机制更稳健,丢失数据概率更低
2.是可读的日志文本,可以处理误操作
缺点会丢失最后一次快照后的所有修改更占用磁盘空间、速度慢、存在bug、同步写入效率低

18.你们缓存用的是什么?你们redis有做持久化么?是用什么来做的?

19.你们不是用reids来做缓存嘛,如果缓存失效就会查数据库对吧,那你是怎么判断缓存失效的?

20.redis在项目中哪些地方用到

1.商品刚加入购物车时缓存实时价格
2.将当前用户的购物车信息存入Redis
3.把分类列表的查询结果放入缓存
4.把库存的锁定信息保存到 redis 中
5.将防重的唯一标识、订单号存入 Redis 缓存
6.购物车异步写入数据库异常时,会将异常的userId缓存到Redis,将来定时任务定时进行 MySQL 数据同步

八、RabbitMQ*5

1.RabbitMQ 在你们项目中哪里用到了

1、新增商品、修改商品时发送消息给search微服务执行索引库同步数据库
3、商品价格修改后发送消息给购物车微服务同步实时价格,用于比价
4、新增商品、修改商品、库存为零时发送消息给商品详情页,商品详情页重新生成静态页面
5、进行短信验证时,MQ异步发短信验证码给用户
6、order微服务订单创建成功后发送消息给购物车微服务删除购物车
7、order微服务如果订单创建失败发消息给wms微服务,wms微服务立马释放库存;发送消息给oms微服务标记订单为无效订单
8、order微服务订单创建成功,发送延时消息,将来定时关单并解锁库存
9、order微服务关单成功发送消息解锁库存
10、wms 微服务锁库存成功,发送延时消息,将来定时解锁库存
12、payment微服务支付成功发送消息给oms,更新订单状态为已支付
12、更新订单状态成功,发送消息给wms减库存、加销量
13、更新订单状态成功,发送消息给ums更新用户积分

2.RabbitMQ 消息中间件和延时队列加死信队列

1.声明延时交换机
2.声明延时队列
	1.x-message-ttl:指定TTL时间
	2.x-dead-letter-exchange:死信转发所需的死信交换机(DLX)
	3.x-dead-letter-routing-key:转发死信时的routingKey(DLK)
3.延时队列绑定到延时交换机
4.声明死信交换机(DLX)
5.声明死信队列(DLQ)
6.死信队列绑定到死信交换机,rontingKey 要包含第2步绑定的 DLK。

3.RabbitMQ的如何防止消息丢失

生产者确认:
none-不确认 simple-同步确认阻塞 性能不高 correlated-异步监听方法确认
spring.rabbitmq.publisher-confirm-type=none/simple/correlated
spring.rabbitmq.publisher-returns=true  # 确认消息是否到达队列

消费者确认
none-不确认模式,只要消费者获取到消息,就被确认
auto-自动确认模式,如果消费者程序没有异常,就被确认,如果有异常,会无限重试
manual-手动确认模式,代码确认:channel.basicAck/basicNack/basicReject()
spring.rabbitmq.listener.simple.acknowledge-mode=none/auto/manual

4.RabbitMQ持久化的类型

1.声明队列必须声明为支持消息持久化的队列
2.声明交换机必须声明为支持消息持久化的交换机
3.发送消息的时候,必须支持为可持久化的消息

5、你们项目的事务用的什么 (我没说seata我说的MQ最终一致性)

MQ最终一致性:
	1.创建订单时,如果防重、验价、限购、验库存、锁存库都完成了,但是保存的订单的时候如果发生异常,
	2.此时会发送消息,oms获取消息标记为无效订单;wms获取消息解锁库存

九、SpringBoot*5

1.springboot启动流程说一下? *2

2.springboot你们项目是怎么使用的?用了多久?

3.Boot也可以搭建集群啊

4.微服务与Boot的区别*2

5.springboot哪些注解会生成Bean?

@Component
@Bean

十、SpringClound*1

1.SpringCloud有哪些组件

组件介绍
Eureka-注册中心Eureka 采用了CS 的设计架构,Eureka Server 作为服务注册功能的服务器,它是服务的注册与发现中心
Ribbon-负载均衡Spring Cloud Ribbon是基于 Netflix Ribbon实现的一套客户端负载均衡的工具(Ribbon+RestTemplate)
OpenFeign-声明式调用远程方法Feign旨在使用编写 Java Http 客户端变得更容易,只需创建一个接口并在接口上添加注解即可(接口+注解)
Hystrix-熔断、降级、监控(断路器)Hystrix 是一个用于处理分布式系统的延迟和容错的开源库
hystrixDashboard-服务监控记录所有通过 Hystrix 发起的请求的执行信息,并以统计报表和图形的形式展示给用户
Gateway-网关Gateway提供了一种简单而有效的方式来对微服务进行路由,也提供了一些强大的过滤器功能
Sleuth-链路跟踪Spring Cloud Sleuth提供了一套完整的服务跟踪的解决方案 并且兼容支持了zipkin(负责展现)

十一、分布式事务*3

1.Seata的实现原理

Seata 的三个组件:
1.TC:事务协调器,负责维护全局事务的运行状态,协调并驱动全局事务的提交和回滚
2.TM:事务管理器,负责开启一个全局事务,并最终发起全局事务的提交或回滚的决议
3.RM:资源管理器,负责接收事务协调器的指令,并驱动分支事务的提交和回滚
Seata的实现原理:
1. TM 向 TC 申请开启一个全局事务,全局事务创建成功后生成一个全局唯一的 XID
2. XID 会在微服务调用链路的上下文中传播
3. RM 向 TC 申请注册一个分支事务,并将其纳入到 XID 对应的全局事务的管辖
4. TM 向 TC 发起针对 XID 的全局提交或回滚的决议
5. TC 会调度 XID 管辖下的全部分支事务完成提交或回滚

2.CAP解释一下

Consistency(一致性):在分布式系统中,在同一时刻数据是否有同样的值
Avalibility(可用性):集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求
Partition tolerance(分区容错性):分布式系统大多分布在多个子网络中,每个子网络就相当于一个区,分区容错性就是区间通信可能会失败
由于网络会出现延迟丢包等问题,所以分区容错性是无法避免的,所以分布式框架中一般选择满足CP或者AP

3.seata核心注解?

@GlobalTransaction

十二、JVM*7

1.oom异常的原因和解决方法

异常的原因(OOM):
	1.堆溢出异常:针对年轻代、老年代整体 Full GC 后,内存空间还是放不下新产生的对象	
	2.元空间异常:方法区中加载的类太多了(典型情况是框架创建的动态类太多,导致方法区溢出)
	3.栈溢出异常:申请栈空间不足时
解决方法(JVM调优):
	1.堆空间和元空间调优:堆空间和元空间的参数设置
	2.GC调优:优先使用	G1 垃圾回收器
	3.代码调优:尽量用动态字符串(StringBuffer)、不用的大对象及时释放(流、connection)、少使用 try...catch 、少嵌套 for循环
	4.硬件调优:加内存条、选择合适的磁盘、调优服务器性能

2.什么情况下会导致栈溢出异常

栈溢出异常:方法每调用一次都会在栈空间申请一个栈帧,用来保存本次方法执行所用的数据,但是一个没有退出机制的递归调用,会不断的申请栈空间,而又不释放空间,这样就会耗尽栈空间,导致栈溢出异常

3.你们项目用的GC算法是什么?

G1垃圾回收器

4.jvm结构有哪些?

1.功能区(绿色)
类加载器:文件系统加载 class文件
垃圾回收器:对方法区,Java堆,直接内存进行垃圾回收
字节码执行引擎:将 JVM 可识别的字节码转换为操作系统可识别的机器码
2.线程私有区(蓝色)
Java栈:创建线程的时候栈被创建
本地方法栈:用于本地方法调用
PC寄存器:指向 JVM 下一条执行指令的地址
3.线程共享区(橙色)
方法区:存放类的方法代码、变量名、方法名、访问权限、返回值等
堆:用于存放实例对象
直接内存:指的是直接去内存条申请内存,而不是在JVM内存中申请,一般用于 NIO

5.jvm怎么保证出栈顺序

6.jvm垃圾回收判定

7.jvm堆中老年区保存的都是什么对象?

保存的主要是生命周期很长的对象,例如:IOC容器对象、线程池对象、数据库连接池对象等等

十三、JUC*8

1.多线程synchronized和lock区别?

synchronizedLock
synchronized是 Java 的关键字,通过修饰代码块或者方法实现同步Lock 是一个类,通过调用这个类的 API 实现同步访问
悲观独占排他互斥 玩法简单不够灵活悲观独占排他互斥 玩法灵活不够简单
不用考虑加解锁的次数(可重入锁)要考虑加解锁的次数(可重入锁)
公平锁(排队)非公平锁(抢一下再去排队)
不支持响应中断支持响应中断
线程间通信方式不同 wait(),notify(),notifyAll()Condition:await() ,signal()
synchronized不需要手动释放锁,系统会自动释放对锁的占用Lock 必须手动的释放锁,否则可能会出现死锁的现象
synchronized 是阻塞同步Lock 底层机制 AQS(volatile + CAS)是非阻塞同步
不支持共享锁支持共享锁

2.synchronized和volatile的区别

1.volatile本质:是java虚拟机(JVM)当前变量在工作内存中的值是不确定的,需要从主内存中读取;synchronized则是锁定当前的变量,只有当前线程可以访问到该变量,其他的线程将会被阻塞。
2.volatile只能实现变量的修改可见性,并不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性。
3.volatile只能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的。
4.volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。

3.线程池参数?*3

参数说明
corePoolSize线程池的核心线程数
maximuPoolSize线程池能容纳的最大线程数
keepAliveTime空闲线程的存活时间
unit存活时间的单位
workQueue存放提交但未执行任务的队列
hreadFactory创建线程的工厂
handler等待队列满后拒绝策略

4.threadlocal怎么传递参数的?

5.线程池的创建方式

1.继承 Thread 类:继承 Thread 类、重写 Thread类的 run() 方法、创建 Thread 类的子类对象、调用 start 方法
2.实现Runnable接口:创建一个实现Runnable接口的类、实现Runnable接口中的抽象方法run()、创建实现类的对象,作为参数传递到Thread类的构造器中、创建Thread类的子类对象,调用start方法
3.实现Callable接口
创建一个实现Callable接口的实现类、实现Callable接口的call()方法、创建Callable接口的实现类的对象,作为参数传递到FutureTask构造器中,创建FutureTask类的对象,作为参数传递给Thread、创建Thread类的子类对象,调用start方法
4.使用线程池(常用、优点)
提高响应速度(减少了创建新线程的时间)、降低资源消耗(重复利用线程池中线程,不需要每次都创建)、便于线程管理

6.线程池4个拒绝策略?

1.AbortPolicy(默认):
当线程数大于maximumPoolSize+workQueue 数时,直接抛出RejectedExecutionException异常阻止系统正常运行。
2.CallerRunsPolicy:
“调用者运行”一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量。当线程数大于maximumPoolSize+workQueue 数时,谁调用的线程就返回给谁。
3.DiscardOldestPolicy:
抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交当前任务。
4.DiscardPolicy:
直接丢弃任务,不予任何处理也不抛出异常。如果允许任务丢失,这是最好的一种方案。

7.线程池队列你了解哪些

ArrayBlockingQueue:由数组结构组成的有界阻塞队列LinkedBlockingQueue:由链表结构组成的有界阻塞队列(大小默认值为integer.MAX_VALUE)DelayQueue:使用优先级队列实现的延迟无界阻塞队列,只有当其指定的延迟时间到了,才能够从队列中获取到该元素(阻塞消费者)PriorityBlockingQueue:支持优先级排序的无界阻塞队列,生产者生产数据的速度绝对不能快于消费者消费数据的速度,否则时间一长,会最终耗尽所有的可用堆内存空间。SynchronousQueue:不存储元素的阻塞队列,也即单个元素的队列。LinkedTransferQueue:由链表组成的无界阻塞队列。LinkedBlockingDeque:由链表组成的双向阻塞队列。

8.项目核心线程数你们配置的多少

core-size: 8 # 核心线程数,线程池创建时候初始化的线程数。默认为 8 。

十四、Java高级*10

1.算法题 ,开心数,您的想法是什么

2.你都熟悉那些设计模式

工厂模式Spring 通过 BeanFactory 或者 ApplicationContext 创建 Bean 对象的时候用到了工厂模式
单例模式Spring 中 bean 的作用域默认为是单例模式的(singleton)
代理模式Spring AOP 基于动态代理,如果要代理的对象实现了某个接口,
SpringAOP 会使用 JDK 的动态代理创建代理对象;
如果对象没有实现接口,Spring AOP会使用 CGLib 生成一个被代理对象的子类作为代理对象
模板方法模式Spring 中的 jdbcTemplate 等以 Template 结尾的对数据库操作的类就使用了模板方法模式
适配器模式SpringAOP 的增强(Advice)使用了适配器模式、SpringMVC的 HandlerAdapter 也是适配器模式
观察者模式定义的对象有一对多的依赖关系,当一个对象的状态发生改变时,
所有依赖于它的对象都会得到通知被自动更新,Spring事件驱动模型就是观察者模式一个很经典的应用

3.手写一个除了单例之外的设计模式

4.dubbo的工作原理

Apache Dubbo是一款高性能的Java RPC框架,可以和Spring框架无缝集成。RPC并不是一个具体的技术,而是指整个网络远程调用过程。
工作流程:
1.服务容器启动,加载,运行
2.服务提供者在启动时,向注册中心注册自己提供的服务
3.服务消费者在启动时,向注册中心订阅自己所需的服务
4.注册中心会把服务提供者的地址列表给消费者
5.消费者会基于负载均衡算法选择一台提供者调用
6.服务提供者和消费者,会定时的把调用次数和调用时间发送给监控中心进行数据统计

5.zookeeper的工作原理

6.Zookeeper了解吗?

Zookeeper 是 Dubbo官方推荐使用的服务注册中心

7.Kafaka了解过吗?

8.Maven的生命周期?

9.你知道dao领域吗?

10.ik分词器怎么配置的?

main.dic:单词词典
stopword.dic: 停用词,这里只记录了英文的一部分单词,比如: a、an、and、are、as、at、be、but、by等

十五、项目*19

1.介绍下你最你最得意的项目*3

2.项目负责的模块和流程*4

3.添加购物车的具体流程*4

添加购物车流程:
	1.获取用户的登录信息,登录取 userId ,未登录取 userKey
	2.Map<UserId/userKey,Map<skuId, cartJson>> 根据外层的key获取内层的map结构(购物车)
	3.判断购物车里是否包含该商品
	4.如果包含则更新商品数量
	5.如果不包含则新增商品
	6.缓存实时价格到 Redis ,后续用于比价
	7.将购物车信息缓存到 Redis

4.redis如何实现购物车

1.项目采用 Redis + MySQL 实现购物车功能
2.查询时,从 Redis 查询提高性能;写入时,采用双写模式,异步写入 MySQL 中的数据用于数据统计,对可靠性要求不严格
3.为防止数据库数据偏差较大,异步写入 MySQL 发生异常的异常信息(userId)会被缓存到 Redis,后续通过 xxl-job 定时任务进行数据同步
4.Redis 的数据结构采用 hash 结构,Map<UserId/userKey,Map<skuId, cartJson>>

5.讲一下购物车模块的后端逻辑大概是什么样的,比如我下一个订单,选择货物这些的交互。

6.后台的库存是我在加到购物车时就会变化,还是提交订单时才会变化?*2

提交订单时变化

7.举个场景,当用户添加购物车,但后续商品价格有了浮动,如何展示给用户

购物车价格浮动解决方案:
	1.设计购物车字段时 加入一个 实时价格字段
	1.添加购物车时会有一个加入购物车时的价格和一个实时价格,实时价格会被缓存到 Redis
	2.当后续价格发生改变时,通过 MQ 异步通知 cart 微服务进行价格同步,会重新设置 Redis 中的实时价格
	3.计算加入购物车时的价格和 Redis 中缓存的实时价格 就可以得到价格浮动

8.添加订单的具体流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jQpcofkB-1640358639512)(C:\Users\xyh\Desktop\提交订单流程.png)]

配置网关过滤器和拦截器:
	1.使用拦截器统一拦截用户的登录信息,并将用户的拦截信息通过 ThreadLocal 线程变量传递给后续服务
	2.通过网关的局部过滤器,拦截未登录状态下提交订单跳转到登录页面
	
订单确认页:
	1.由于存在大量的远程调用,所以使用异步编排做优化
	2.要生成防重的唯一标识,缓存在 Redis 中,防止重复下单
	
提交订单:
	1.验证 Redis 中防重的唯一标识,由于验证完要立即删除,为保证原子性,使用 luo 脚本实现
	2.验总价:页面总价格  和 数据库实时总价
	3.验库存并锁库存(原子性),锁库时发送延时消息,将来定时解锁库存
	4.创建订单
		1.订单创建成功,发送延时消息,将来定时关单并解锁库存
		2.订单创建异常,发送消息回滚,oms获取消息标记为无效订单,wms获取消息解锁库存(MQ最终一致性解决订单创建的分布式事务问题)
		3.订单创建成功,MQ 异步删除购物车中对应的商品

9.你们订单号的实现(雪花算法),有什么问题吗

10.有用户表嘛?有哪些字段?

用户ID、用户名、密码、盐、昵称、手机号、邮箱、头像、性别、生日、职业、个性签名、用户等级、用户来源、购物积分、状态、注册时间

11.userkey和userID对应的表里哪个字段?

12.游客的数据是怎么存的

制作一个userKey放入cookie中

13.合并的时候如何做,如何判断用户,(回答:不用管,无所谓)

配置拦截器:
	1.由于购物车的增删改查分为登录状态和未登录状态
	2.所以使用拦截器统一拦截用户的登录信息,并将用户的拦截信息通过 ThreadLocal 线程变量传递给后续服务
	3.取出 ThreadLocal 线程变量中用户的登录信息即可判断用户的登录状态和登录信息
	
查询购物车流程:
	1.先查询未登录状态的购物车,查询时要查询实时价格缓存
	2.判断是否登录,未登录则直接返回
	3.已登录则合并购物车
		1.获取登录状态的购物车
		2.判断是否存在未登录状态的购物车,有则遍历合并
			1.判断登录购物车是否存在该商品,有更新,没有则新增
	4.合并完成之后要删除未登录的购物车
	5.查询登录状态所有的购物车信息,查询时要查询实时价格缓存

14.前端调用接口,后端寻找对应微服务的的具体链路

15.你们Controller层的接口是否直接暴露给前端

16.你们项目和一个单体的Boot项目有啥区别

17.授权中心这个模块是怎么做的?大概流程说一下?

18.网关怎么配置路由

19.网关过滤器有哪些?

十六、其他*9

1.自我介绍*5

2.讲一下前几份工作时间和地点

3.你们公司的架构

4.你们小组的架构*2

5.你们项目组的架构,你们来一个新的需求,从哪入手

6.当你的想法于项目当前的项目不吻合,你怎么办*2

7.项目我能网上查到吗?

8.项目做了多久?

9.你还有什么想问我的

640358639512)]

配置网关过滤器和拦截器:
	1.使用拦截器统一拦截用户的登录信息,并将用户的拦截信息通过 ThreadLocal 线程变量传递给后续服务
	2.通过网关的局部过滤器,拦截未登录状态下提交订单跳转到登录页面
	
订单确认页:
	1.由于存在大量的远程调用,所以使用异步编排做优化
	2.要生成防重的唯一标识,缓存在 Redis 中,防止重复下单
	
提交订单:
	1.验证 Redis 中防重的唯一标识,由于验证完要立即删除,为保证原子性,使用 luo 脚本实现
	2.验总价:页面总价格  和 数据库实时总价
	3.验库存并锁库存(原子性),锁库时发送延时消息,将来定时解锁库存
	4.创建订单
		1.订单创建成功,发送延时消息,将来定时关单并解锁库存
		2.订单创建异常,发送消息回滚,oms获取消息标记为无效订单,wms获取消息解锁库存(MQ最终一致性解决订单创建的分布式事务问题)
		3.订单创建成功,MQ 异步删除购物车中对应的商品

9.你们订单号的实现(雪花算法),有什么问题吗

10.有用户表嘛?有哪些字段?

用户ID、用户名、密码、盐、昵称、手机号、邮箱、头像、性别、生日、职业、个性签名、用户等级、用户来源、购物积分、状态、注册时间

11.userkey和userID对应的表里哪个字段?

12.游客的数据是怎么存的

制作一个userKey放入cookie中

13.合并的时候如何做,如何判断用户,(回答:不用管,无所谓)

配置拦截器:
	1.由于购物车的增删改查分为登录状态和未登录状态
	2.所以使用拦截器统一拦截用户的登录信息,并将用户的拦截信息通过 ThreadLocal 线程变量传递给后续服务
	3.取出 ThreadLocal 线程变量中用户的登录信息即可判断用户的登录状态和登录信息
	
查询购物车流程:
	1.先查询未登录状态的购物车,查询时要查询实时价格缓存
	2.判断是否登录,未登录则直接返回
	3.已登录则合并购物车
		1.获取登录状态的购物车
		2.判断是否存在未登录状态的购物车,有则遍历合并
			1.判断登录购物车是否存在该商品,有更新,没有则新增
	4.合并完成之后要删除未登录的购物车
	5.查询登录状态所有的购物车信息,查询时要查询实时价格缓存

14.前端调用接口,后端寻找对应微服务的的具体链路

15.你们Controller层的接口是否直接暴露给前端

16.你们项目和一个单体的Boot项目有啥区别

17.授权中心这个模块是怎么做的?大概流程说一下?

18.网关怎么配置路由

19.网关过滤器有哪些?

十六、其他*9

1.自我介绍*5

2.讲一下前几份工作时间和地点

3.你们公司的架构

4.你们小组的架构*2

5.你们项目组的架构,你们来一个新的需求,从哪入手

6.当你的想法于项目当前的项目不吻合,你怎么办*2

7.项目我能网上查到吗?

8.项目做了多久?

9.你还有什么想问我的

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值