Java面试题zs
2022-01-08 14:55:47 0 举报
AI智能生成
java面试题 全
作者其他创作
大纲/内容
zookeeper(2)
zookeeper工作原理
分布式协调组件
elasticsearch
ik分词器怎么配置的
dubbo
dubbo工作原理
第一层:service层,接口层,给服务提供者和消费者来实现的
第二层:config层,配置层,主要是对dubbo进行各种配置的
第三层:proxy层,服务代理层,透明生成客户端的stub和服务端的skeleton
第四层:registry层,服务注册层,负责服务的注册与发现
第五层:cluster层,集群层,封装多个服务提供者的路由以及负载均衡,将多个实例组合成一个服务
第六层:monitor层,监控层,对rpc接口的调用次数和调用时间进行监控
第七层:protocol层,远程调用层,封装rpc调用
第八层:exchange层,信息交换层,封装请求响应模式,同步转异步
第九层:transport层,网络传输层,抽象mina和netty为统一接口
第十层:serialize层,数据序列化层
第二层:config层,配置层,主要是对dubbo进行各种配置的
第三层:proxy层,服务代理层,透明生成客户端的stub和服务端的skeleton
第四层:registry层,服务注册层,负责服务的注册与发现
第五层:cluster层,集群层,封装多个服务提供者的路由以及负载均衡,将多个实例组合成一个服务
第六层:monitor层,监控层,对rpc接口的调用次数和调用时间进行监控
第七层:protocol层,远程调用层,封装rpc调用
第八层:exchange层,信息交换层,封装请求响应模式,同步转异步
第九层:transport层,网络传输层,抽象mina和netty为统一接口
第十层:serialize层,数据序列化层
工作流程
1.provider向注册中心注册
2.consumer从注册中心订阅服务,注册中心会通知consumer注册好的服务
3.consumer调用provider
4.consumer和provider都异步的通知监控中心
三大核心能力
1.面向接口的远程方法调用
2.智能容错和负载均衡
3.服务自动注册与发现
Linux
1.Linux查找日志但是这个日志很大我怎么查看最后几行信息或者最前面几行
tail -f -n 最后几行
tail命令和top命令
1.tail -f可以查询文件实时变化情况
2. top命令用于实时显示 process 的动态
其他问题
1.自我介绍
2.介绍一下做过的项目、得意的项目
电商项目
3.你们公司的架构
4.你们组的架构
2前端5后端3测试
5.当你的想法与当前项目不吻合,你怎么办(2)
6.你负责的哪些模块(3)
2.首页商品分类
使用自定义注解(AOP+分布式锁)实现首页分类数据的缓存
布隆过滤器
3.商品详情页
1.页面静态化
2.异步编排
4.单点登录
5.购物车
6.订单
7.你知道dao领域吗?
8.你们项目组的架构,来了一个新的需求,从哪下手
9.Maven的生命周期
1.clean
2.default
3.site
10.算法题,开心数
11.手写一个除单例以外的设计模式
12.你们项目和一个单体boot项目的区别
1.分布式执行效率快
2.容灾能力更强
3.容纳的数据量更大
13.上线流程
14.测试流程
15.代码量
JavaSE
1.解释下java中的Stream流
针对集合迭代器的增强,能够更高效的完成聚合,过滤,或者大批量的操作,流与lambda表达式结合,编码效率大大提高
类型转换非常方便,比如我想把一个List转换成Map,不用流的话我需要写很多代码,流只需要一行代码就能搞定
类型转换非常方便,比如我想把一个List转换成Map,不用流的话我需要写很多代码,流只需要一行代码就能搞定
2.hashMap(5)
1.HashMap是基于哈希表对Map接口的实现,HashMap]与HashTable相比允许使用null值和null键(源码的注释),并且是线程不安全的
2.HashMap初始化是在首次调用put()方法时,默认创建长度为16的数组,负载因子0.75
源码注释:在时间和空间上做的一个权衡;加载因子大于0.75会减少头部以上的空间,增加遍历花费的时间
3.jdk7底层结构只有:数组+链表。jdk8底层:数组+链表+红黑树
4.插入元素时,先调用hash()方法,获取hash值:元素角标(n-1) & hash(n是集合长度)
根据hash值来决定插入的值存放在哪个位置hash = (key==null) ? 0 : (h = key.hashCode()) ^ (h>>>16)
根据hash值来决定插入的值存放在哪个位置hash = (key==null) ? 0 : (h = key.hashCode()) ^ (h>>>16)
如果hash值一样就以链表形式存储
hash算法
h = key的hashCode值 异或 h>>>16
5.存取无序
6.HashMap继承AbstractMap实现Map;设计上的一个小错误
7.初始容量必须为2的n次方
因为如果是2的n次方 可以极大减少HashMap碰撞的次数,可以使得元素更加均匀的分配到数组中。
初始容量给负数会报错;给超过2的30次方(最大值)不会报错,会分配最大容量。
8.如果初始容量不给2的n次方:会返回一个大于给定值的最小的2的n次方的一个值
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
9.当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组长度 > 64时,反树化值为6
根据泊松分布概率统计得出的结论,8的时候概率已经几乎为0了
10.put方法
1.手动执行map.put()
2.先初始化一个table[] 也就是HashMap
3.会根据元素的Hash值判断该位置是否有值
4.如果没有,直接newNode,新建一个元素
5.如果有值并且当前已经是链表了,就要遍历链表并存值
6.如果有值并且已经树化,就要用树的方式存值
7.key的Hash相同了,会进行值覆盖
补充:LinkedHashMap: HashMap保证成对元素唯一,并且查询速度很快,可是成对元素存放进去是没有顺序的,那么我们要保证有序,还要速度快怎么办呢?
在HashMap下面有一个子类LinkedHashMap,它是链表和哈希表组合的一个数据存储结构。
在HashMap下面有一个子类LinkedHashMap,它是链表和哈希表组合的一个数据存储结构。
4.set list map的区别
1.list有序可重复,set无序不可重复,map是键值对,map中的key相当于一个set
2.list,set继承Collection,map单独的一个顶层接口
3.set查询慢,增删快,增加和删除不会引起元素位置改变,list和数组类似,可以动态增长,查找效率高。
Map和Set关系补充 :Set的内部实现其实是一个Map。即HashSet的内部实现是一个HashMap,TreeSet的内部实现是一个TreeMap,LinkedHashSet的内部实现是一个LinkedHashMap。
5.反射是什么
1.通过反射可以知道任意一个类的所有属性和方法
2.通过反射可以调用任意一个对象的所有方法和属性
3.这是一种动态获取信息和动态调用对象的方法的功能
4.获取类对象
1.类型名.class
2.对象.getClass()
3.Class.forName(类型全类名)
4.ClassLoader的类加载器.loadClass(全类名)
6.jdk 1.8和其他版本的区别
1.lambda表达式
2.Stream流
3.函数式接口
4.永久代-->元空间
7.ArrayList和LinkedList主要区别(2)
1.Array是动态数组的数据结构,Link是链表的数据结构
2.查找Array效率高(get,set)、增删(add,remove)Link效率高
3.Array需要手动设置固定大小的容量,Link动态随数据量的变化而变化
8.集合的线程安全问题
1.ArrayList
Vector、Collections.synchronizedList()、CopyOnWriteArrayList
2.HashSet
CopyOnWriteArraySet、Collections.synchronizedSet()
3.HashMap
HashTable(不能存储null)、ConcurrentHashMap、Collections.synchronizedMap()
9.stringbuffer和stringbuilder区别?为什么是线程安全
1.stringBuilder速度快,线程不安全
2.stringBuffer速度慢,线程安全
10.设计模式
1.单例模式
2.工厂模式
3.装饰模式
4.代理模式
11.深拷贝和浅拷贝的理解
1.深拷贝和浅拷贝就是指对象的拷贝,一个对象存在两种类型的属性,一种是基本数据类型,一种是实例对象的引用
2.浅拷贝:只会拷贝基本数据类型的值,以及实例对象的引用,并不会复制一份引用地址所指向的对象,内部类属性指向的是同一个对象
3.深拷贝:既拷贝基本数据类型的值,也会针对实例对象的引用地址所指向的对象进行复制,内部的类执行指向的不是同一个对象
MySQL
1.使用MySQL存数据的原因
1.开源免费,同时也支持收费版
2.可以跨平台
3.用起来也特别的方便
2.MySQL的优化
1.SQL优化一般在哪些字段上面加索引
单表优化
1.选择过滤性好的字段(手机号,身份证)
2.组合索引的时候,过滤性好的字段越靠前越好
3.组合索引遇到范围查询时,尽量把这个字段放在索引次序的最后面
4.选择组合索引的时候,尽量包含where中更多的索引
5.尽量避免索引失效的情况
1.计算、函数导致索引失效
2.范围条件右边的列索引失效
3.!=或者<>索引失效
4.is not null不能使用索引,is null 可以
5.like以通配符%开头索引失效
6.类型转换索引失效
多表优化
1.左外、右外连接,要保证被驱动表的关联字段被索引
2.选择小表作为驱动表(但是具体哪个表还是需要根据实际业务决定)
3.inner join时,mysql自动选择小表或者连表字段没有索引的表作为驱动表
4.straight_join 可以优化inner join 强制使用一张表作为驱动表(straight_join前面的表)
3.MySQL优化的具体场景
1,遇到子查询转为连表查询
2.排序、分组优化
1.排序必须加limit过滤(limit10)
2.排序后面的字段要和创建索引时顺序一致
3.排序时字段不要一个升序一个降序(年龄升,id降)
4.group by没有过滤条件,也可以用上索引
5.group by 先排序在分组,遵循最佳左前缀法则
4.数据库隔离级别(2)
1.READ_UNCOMMITTED:读未提交
2.READ_COMMITTED:读已提交
3.REPEATABLE_READ:可重复读 (默认)
可能出现幻读
乐观锁+版本机制解决(MVCC)
4.SERLALIZABLE:串行化
5.whrer和having的区别
1.where不能用聚合函数、having中可以使用聚合函数(SUM, COUNT, MAX, AVG)
2.where是一个约束声明,用来约束来自数据库的数据,having是一个过滤声明
3.where是在结果返回之前起作用的,having是在查询返回结果集以后,对查询结果进行的过滤
6.sql语句的执行顺序
1.from
2.join
3.on
4.where
5.group by
6.avg,sum等聚合函数
7.having
8.select
9.distinct
10.order by
11.limit
7.聚簇索引和非聚簇索引的区别
聚簇索引:将数据存储与索引放到了一块、并且是按照一定的顺序组织的,找到索引也就找到了数据,
非聚簇索引叶子节点不存储数据、存储的是数据行地址,也就是说根据索引查找到数据行的位置再取磁盘查找数据,这个就有点类似一本书的目录,
2.非主键索引都是非聚簇索引
3.MyISAM引擎不支持聚簇索引,只有主键索引
1、查询通过聚簇索引可以直接获取数据,相比非聚簇索引需要第二次查询(非覆盖索引的情况下)效率要高
2、聚簇索引对于范围查询的效率很高,因为其数据是按照大小排列的
3、聚簇索引适合用在排序的场合,非聚簇索引不适合
2、聚簇索引对于范围查询的效率很高,因为其数据是按照大小排列的
3、聚簇索引适合用在排序的场合,非聚簇索引不适合
8.索引优化的type值
system->const->eq_ref->ref->range->index->all
9.mysql的引擎
innodb和myisam的区别
1.Innodb支持事务,MyISAM不支持
2.Innodb支持外键,MyISAM不支持
3.Innodb是聚簇索引,使用B+Tree作为索引结构,MyISAM是非聚簇索引
10.mysql怎么优化(explain)
1、设计良好的数据库结构, 允许部分数据冗余, 尽量避免 join 查询, 提高效率。
2、选择合适的表字段数据类型和存储引擎, 适当的添加索引。
3、MySQL 库主从读写分离。
4、找规律分表, 减少单表中的数据量提高查询速度。
5、书写高效率的 SQL。不要使用 SELECT * FROM TABEL,用什么字段就查什么字段
6.show profiles查看最近的SQL执行时间
11.复合索引,如何考虑怎么创建
1.主键、where后面字段、外键或者关联字段、排序、分组字段;优先组合索引
12.索引失效
1.模糊查询LIKE以%开头
2.数据类型错误(类型转换)
3.函数sum...
4.索引列的null(空)
5.索引列进行四则运算
6.最佳左前缀(复合索引不按索引列最左开始查找)
7.全表查找预计比索引更快
13.mysql的视图会影响性能吗?
不会影响性能
复杂复杂的sql语句,提高复用性,只保存sql逻辑,不保存任何查询结果
14.索引失效解决办法
针对失效的原因对sql进行优化
15.mysql慢日志查询
1.日志分析工具mysqldslow
16.事务的4个特性(ACID)
1.原子性
事务是不可再分的
2.一致性
操作前与操作后数据保持一致
3.隔离性
多个事务执行时,互不影响
4.持久性
一旦执行成功,永久保存到数据库
17.事务并发引起的问题
1.脏读
可以读取到另一个事务未提交的数据
2.不可重复度
一个事务可以读取另一个事务已提交的数据,单条记录前后不匹配
3.幻读
读取到另一个事务已提交的数据,读取的数据前后多了或少了
18.mysql的分库分表(mycat拦截sql语句)
1.垂直拆分
根据业务进行拆分,把一张表的字段给拆分,一张是数据不经常发生变化的,一张是数据经常发生变化的
2.水平拆分
一般是数据量太大,需要拆分,分担压力
19.char最大长度是255字符,varchar(动态长度)最大长度是65535个字节。
SSM
1.Spring是怎么解决Bean之间的循环依赖的(2)
1.构造器循环依赖
属性注入失败
2.feild属性注入循环依赖
1.@Scope("prototype")多例
属性注入失败
2.单例
属性注入成功
3.三级缓存
1.一级缓存 singletonObjects
存放已经经历了完整生命周期的Bean对象
2.二级缓存 earlySingletonObjects
存放早期暴露出来的Bean对象,Bean的生命周期未结束(属性还未填充完)
3.三级缓存 singletonFactories
存放可以生成Bean的工厂
4.debug查看
1.A创建过程中需要B,于是A将自己放到三级缓存里面,去实例化B
2.B实例化的时候发现需要A,于是B先查一级缓存,没有,再查二级缓存,还是没有,再查三级缓存
找到了A然后把三级缓存里面的这个A放到二级缓存里面,并删除三级缓存里面的A
找到了A然后把三级缓存里面的这个A放到二级缓存里面,并删除三级缓存里面的A
3.B顺利初始化完毕,将自己放到一级缓存里面(此时B里面的A依然是创建中状态)
然后回来继续创建A,此时B已经创建结束,直接从一级缓存里面拿到B,然后
完成创建,并将A自己放到一级缓存里面。
然后回来继续创建A,此时B已经创建结束,直接从一级缓存里面拿到B,然后
完成创建,并将A自己放到一级缓存里面。
5.总结
1.Spring创建bean主要分为两个步骤,创建原始Bean对象,接着去填充对象属性和初始化
2.每次创建之前,都会从缓存中查下有没有该bean,因为是单例,只能有一个
3.Spring解决循环依赖依靠的是Bean的“中间态”的一个概念,即对象已经实例化但是还初始化,实例化的过程又是通过构造器创建的,如果A还没创建出来怎么可能提前暴露,所以构造器的循环依赖问题无法解决
6.核心方法
1.getSingleton():获取单例对象
2.doCreateBean():创建对象
3.polulateBean():初始化对象
4.addSingleton():添加单例对象到某个容器
7.其他衍生问题
1.为什么构造器注入属性无法解决循环依赖问题?
由于spring中bean的创建过程是先实例化,再初始化将实例化好的对象暴露出去,提供给其他对象调用,然后使用构造器注入,如果是必须要构造器完成对象的初始化的操作,那就陷入死循环了。
2.一级缓存能不能解决循环依赖问题
不能,在三个级别的缓存中存储的对象是有区别的,一级缓存是实例化并且初始化完成的对象,二级缓存是实例化但是还未初始化的对象,如果只有一级缓存,在并发操作下,可能取到实例化但未初始化的对象。
3.二级缓存能不能把解决循环依赖问题
理论上可以,但是三级缓存中存放匿名内部类的原因是需要创建代理对象,比如A类需要创建代理对象,A类实例化在三级缓存中存放的是生成具体对象的一个匿名内部类,这个类可能是代理类也可能是普通对象,使用三级缓存可以保证无论是否需要代理对象,都可以保证使用的是同一个对象。
2.Spring事务传播机制(一个事务方法被另一个事务方法调用时,这个事务方法该如何进行)
1.REQUIRED:支持当前事务,如果事务不存在,就新建事务
2.SUPPORTS:支持当前事务,如果事务不存在,就以非事务方式运行
3.MANDATORY:支持当前事务,如果事务不存在,就抛出异常
4.REQUIRES_NEW:挂起当前事务,以新事务运行
5.NOT_SUPPORTS:挂起当前事务,以非事务方式运行
6.NEVER:以非事务运行,如果事务存在,就抛出异常
7.NESTED:嵌套事务,基于jdbc3.0提高的SavePoint技术实现
3.@Component注解部分场景不生效问题
问题:@Component这个注解把该类注入到spring容器中了,但是在拦截器之中不生效,再在Utils使用到dao,我们就不能直接注入了
解决方案:使用ApplicationContextAware解决问题。而ApplicationContextAware实现了这个接口的bean,
当spring容器初始化的时候,会自动的将ApplicationContext注入进来
当spring容器初始化的时候,会自动的将ApplicationContext注入进来
4.SpringMVC执行流程
1.请求到达DispatcherServlet,doDispatch()方法
2.DispatchServlet收到请求调用HandlerMapping处理器映射器
3.处理器映射器找到具体的处理器,生成处理器对象及处理器拦截器(集合)一并返回给DispatchServlet
4.DispatchServlet调用HandlerAdapter处理器适配器
5.HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)
6.Controller执行完成返回ModelAndView
7.DispatcherServlet将ModelAndView传给ViewReslover视图解析器
8.ViewReslover解析器后返回具体视图
9.DispatchServlet根据View进行视图渲染(将模型数据填充至视图中),响应给用户
5.Spring源码看过吗?
IOC
DI
三种方式
1.setter注入
2.构造方法
3.接口注入(Spring不支持)
创建对象四种方式
1.构造方法
<bean class="类的全路径" ><construct-args ></bean>
2.静态工厂
<bean class="静态工厂全路径" factory-method="getUser"/>
UserFactory.getUser();
class UserFactory{
User getUser(){
return new User();
}
}
class UserFactory{
User getUser(){
return new User();
}
}
3.实例化工厂
UserFactory factory = new UserFactory();
User user = factory.getUser()
<bean id="factory" class="UserFactory类的全路径" />
<bean id="user" factory-ref="factory" factory-method="getUser"/>
User user = factory.getUser()
<bean id="factory" class="UserFactory类的全路径" />
<bean id="user" factory-ref="factory" factory-method="getUser"/>
4.工厂bean FactoryBean接口
<bean id="User" class="FactoryBean的实现类的全路径"/>
四种注解
1.对象初始化注解
@Controller、@Service、@Repository、@Component
2.依赖注入
@Autowised、@Resource、@Qualifier
@Resource
装配顺序
1.如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到就抛出异常
2.如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛异常
3.如果指定了type,则从上下文中找类型匹配的唯一bean进行装配,找不到或找到多个,都会抛出异常
4.如果都没有指定,就自动按照byName方式进行装配;如果没有匹配,就回退一个原始类型进行匹配。
@Autowired
默认根据类型进行匹配,如果Spring上下文中存在不止一个接口类型的bean时,就会抛出异常
@Qualifier
配合@Autowired使用,可以指定一个名字,通过名字进行匹配
3.生命周期
@PostConstruct、@PreDestroy
RabbitConfig的初始化方法上@PostConstruct
4.作用域
@Scope(Singleton、prototype、request、session、globalsession)
AOP
基于动态代理
jdk代理
实现InvocationHandler接口的invoke方法 Proxy:newProxyInstance
cglib代理
实现MethodInterceptor接口的intercept方法:Enhance.create
相关概念
目标对象
被增强的对象(源对象)
代理对象
增强后的对象
连接点
可以被增强的点,spring只支持方法级别的连接点
切点
被增强的连接点
通知
@Before
前置通知:目标方法执行前执行
@AfterReturning
返回后通知:目标方法正常返回之后执行
@AfterThrowing
异常后通知:目标方法抛出异常后执行
@After
后置通知,最终通知:类似于finally。5.3之前在return之前执行,5.3之后在最后执行
@Around
环绕通知:在前后都可以执行
1.Object类型的返回值
2.必须要有一个ProceedingJoinPoint连接点类型的参数
3.必须抛出一个Throwable异常
4.必须手动执行目标方法
切面
@Aspect
在切面中可以定义通知方法
6.AOP是什么,项目中哪里用到
自定义缓存注解:基于分布式锁和AOP实现
7.mybatis单表查询和多表查询有什么区别嘛?
1.resultType,resultMap
2.association对一,collection对多
3.javaType,ofType对多
10.mybatis的resultMap可以继承吗?怎么实现
和java一样extends
11.resultMap和resultType区别是什么,作用是什么,假如JavaBean里有List字段该怎么使用
1.resultMap可以自定义结果集比如一些映射或者级联关系的结果
2.resultType返回的是一个Pojo类的结果集使用
3.有List字段就使用resultMap,然后指定一个collection的关联关系
12.#{}和${}的区别
1.#{}是预编译的,${}不是预编译的
2.#{}预编译成?,${}是直接字符串拼接的
3.预编译可以防止SQL注入
4.#{}不需要做数据类型转换,${}如果传入数值类型需要'${a}'转换成字符串
5.传入一个参数时#{}可以使用任意参数名接收参数,而${}默认使用${value}接收,在Mapper接口使用@Param("")指定参数名
13.mybatis使用公共的sql怎么使用
1.提取公共SQL,指定一个id,别处要使用就refid="id"
14.怎么样让一个对象交给Spring来管理
1.配置文件的形式
2.@Bean注解
3.BeanFactory
Redis
512M
2.Redis怎么保证数据一致性
1.双写模式
因为程序出异常mysql可以回滚,redis不会回滚,所以不管先写哪个都会出现数据不一致
2.失效模式
先删redis或者先写mysql:单线程没问题,多线程会出现线程不一致
3.双删模式★
删缓存-->写mysql-->提交事务-->再次异步删除缓存(借助AOP后置通知)
4.阿里开源canal中间件(主从复制思想,伪装成一个从机,然后通过读取Mysql中继日志,进行数据同步)
3.redis数据结构有哪些?
1.String
序列化方式保存,最大支持512mb的字符串
2.set
value值为null的一个hash结构
3.zset
底层结构是hash,value值用来存分数,key用来存值,可以按照分数对key进行排序
4list:双向连表
添加顺序有序,两端操作效率高
5.hash:由多对kv组成
一般用来描述对象
4.哪些地方用到redis
1.登录校验的token存储在redis中
2.购物车商品实时价格
3.用户的购物车信息
5.分布式锁
6.订单的唯一ID
5.redis缓存会出现哪些问题
缓存问题(2)
1.缓存穿透
访问不存在的key
2.缓存雪崩
大量key同时过期
3.缓存击穿
热点key过期
6.redis分布式锁怎么解决死锁
1.添加过期时间防止死锁,set key value ex 30 nx
2.可重入,hash + lua脚本 防止误删并且保证了原子性
3.解锁:先判断再删除
7.redis分布式锁过期时间到了怎么办?
自动续期
先尝试获取锁,获取到锁,执行以下步骤
1.判断自己的锁是否存在
2.是自己的锁则重置过期时间
8.还有哪些地方用到了分布式锁
1.为了防止缓存击穿,添加分布式锁
4.首页的分类信息:一二三级分类
9.redis为什么性能好
1.基于内存
2.非阻塞IO,多路IO复用
10.redis对于过期key的删除策略
1.被动删除
惰性删除,读/写一个已经过期的key时,会直接删除这个key
2.主动删除
定期淘汰一批已过期的key
3.当前已用内存超过maxmemory值时,触发主动清除策略
11.redis内存淘汰策略
1.针对有过期时间的key/针对所有key,使用lru算法淘汰
LRU:上一次使用时间离现在最久的
2.针对有过期时间的key/所有的key,使用lfu算法
LFU:最近使用频率低的
12.redis持久化
1.RDB:二进制快照方式,直接把内存中的数据持久化到一个dump.rdb文件中
1.效率高,节省磁盘
2.无法保证数据的高可用性:比如系统在定时持久化之前宕机,导致数据丢失
3.RDB是通过fork子进程来协助完成数据持久化,当数据集较大时,可能会导致服务器停止服务几百毫秒,甚至一秒
2.AOF:日志拼接方式把执行的命令写到一个.aof文件中
1.效率低,占用磁盘
2.可以保证数据的高可用性,哪怕是在写入的过程中服务器宕机,也不会破坏日志文件中已经存在的内容,在redis下一次启动之前,可以通过,redis-check-aof工具来帮助我们解决数据一致性问题
13.redis扩容机制
14.redis缓存,怎么判断缓存失效
1.代码逻辑写死
15.redis哈希槽
Redis集群有16384个哈希槽,每个key通 过CRC16校验后对16384取模来决定放置哪个槽,集群的每个节点负责一部分hash槽。
16.redis安全机制
1.禁止外网访问 bind 127.0.0.1
2.设置密码
RabbitMQ
1.在项目的哪里用到了RabbitMQ
1.在对首页及搜索页面商品进行增删改操作的时候,向es同步数据时使用了。
2.购物车中价格变化,用缓存+MQ实现了实时价格
3.订单提交的分布式事务:MQ+补偿机制
4.延时队列和死信队列实现定时关单、定时解锁库存
5.用户注册成功后,如果三天没有登录就发短信进行提醒
6.用户发起退款,如果三天没有得到处理则通知相关运营人员
2.相关概念
1.消息中间件
2.延时队列
3.死信队列
3.Kafka了解过吗?
用过类似的消息中间件,RabbitMQ。
4.MQ如何防止消息丢失
1.生产者确认
2.消费者确认:basicAck
3.消息持久化
1.交换机持久化、队列持久化、消息持久化(三者都要持久化):重启服务消息、队列、交换机依然存在
5.如何保证消息不被重复消费
1.如果是数据库的写入操作,就先根据查询一下,如果已经存在,就不再执行insert操作,可以执行update
2.如果是写入redis,redis的set操作本身具有幂等性,所以不用担心
3.生成一个唯一id,防止表单重复提交
6.几百万的消息持续积压几小时,如何解决?
如果说积压了几百万的数据的话,这个时候修复了消费者,让他恢复速度,那么也得等几个小时才能恢复,所以我们采取的措施是临时的紧急扩容
1.先修复消费者的问题,确保其恢复消费速度,然后将现有的消费者都停掉
2.新建一个topic,partition是原来的10倍,临时建立好原先10倍或者20倍的queue数量
3.然后写一个临时的分发数据的consumer程序,部署上去消费积压的数据,消费之后不做耗时的处理,直接轮询写入临时建立好的10倍数量的queue
4.接着临时征用10倍的机器来部署consumer,每一批consumer消费一个临时的queue的数据
5.这个做法相当与临时将队列和消费者资源扩容10倍,以正常10倍的速度来消费数据
6.等快速消费完积压的数据之后,就恢复原先的部署架构,重新用原先的消费者机器来消费消息
7.如果这些消息的设置了TTL的就意味着大量的消息丢失,如何解决?
Seata
1.Seata的实现原理
以项目为例
1.用户发送请求,到达新增的方法,此时触发TM,发送一个开启全局事务的请求给TC,
TC收到请求之后生成一个全局事务和一个唯一标识XID,XID会在微服务的调用中传播
TC收到请求之后生成一个全局事务和一个唯一标识XID,XID会在微服务的调用中传播
2.当这个新增的方法开始调用其他微服务的方法时,RM会携带XID给TC,TC会给这个XID的全局事务
注册一个分支事务
注册一个分支事务
3.整个调用链执行完毕之后,TC才算是完成了整个事务的注册工作,对应的新增方法也执行完毕
4.新增方法执行完毕后,TM会携带XID向TC发起事务的提交或回滚,TC会命令各个分支事务的提交或回滚
2.Seata的工作流程
1.TM向TC注册一个全局事务,TC生成一个全局事务,并返回一个全局事务的唯一标识XID
2.XID会在微服务的调用链路中传播
3.RM向TC注册一个本地事务,作为XID相对应的全局事务的一个分支事务
4.TM向TC发起XID相应的全局事务的提交或回滚
5.TC驱动RM完成XID相应的全局事务下的所有分支事务的提交和回滚
JUC
1.多线程synchronized和lock的区别
1.synchronized是JDK提供的关键字,lock是一个接口
2.lock支持读共享,轻量级锁,更加灵活;synchronized重量级锁(读写排他)
3.lock可以避免出现死锁(tryLock()),synchronized无法避免死锁
4.lock支持中断,synchronized不支持中断
5.都是可重入锁
6.lock支持非公平和公平锁,sychronized只支持非公平
7.lock发生异常时需要手动释放锁,synchronized发生异常自动释放锁
8.lock性能高,sychronized性能低
2.synchronized和volatile的区别
1.volatile只能保证变量的修改可见性不能保证原子性,sychronized可以保证原子性和可见性
2.volatile不会造成线程的阻塞,sychronized可能会造成线程的阻塞
3.volatile只能用在变量上,sychronized可以用在变量、方法、和类上
4.volatile本质是JVM当前变量在工作内存中的值是不确定的,需要从主内存中读取,
sychronized是锁定当前变量,只有当前线程可以访问,其他线程会被阻塞
sychronized是锁定当前变量,只有当前线程可以访问,其他线程会被阻塞
3.线程池参数(2)
1.核心线程数corePoolSize
1.cpu密集型
项目主要用于算术运算:cpu核数+1
2.IO密集型
主要是逻辑运算:核数/(1-0.9)
2.最大线程数maximumPoolSize
3.多余空闲线程存活时间keepAliveTime
4.存活时间的单位unit
5.任务队列workQueue
1.ArrayBlockingQueue:规定大小的阻塞队列(FIFO)
2.LinkedBlockingQueue:大小不固定的阻塞队列,大小有限制,但不指定大小(FIFO)
3.PriorityBlockingQueue:类似于Link,但是排序不是FIFO,自然排序或者根据构造函数的Comparator决定
4.SynchronizedQueue:特殊的阻塞队列,操作必须是放和取交替完成
6.线程工厂threadFactory,一般使用默认的
7.拒绝策略handler(2)
1.AbortPolicy默认的,抛异常
2.CallerRunsPolicy调用者运行,将某些任务回退到调用者
3.DiscardOldestPolicy:抛弃队列中等待最久的任务,把当前任务加入队列中,尝试提交当前事务
4.DiscardPolicy:丢弃无法处理的任务,如果允许任务丢失,这是最好的策略
4.线程池的创建方式
Executors.newFixedThreadPool:创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待;
Executors.newCachedThreadPool:创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程;
Executors.newSingleThreadExecutor:创建单个线程数的线程池,它可以保证先进先出的执行顺序;
Executors.newScheduledThreadPool:创建一个可以执行延迟任务的线程池;
Executors.newSingleThreadScheduledExecutor:创建一个单线程的可以执行延迟任务的线程池;
Executors.newWorkStealingPool:创建一个抢占式执行的线程池(任务执行顺序不确定)【JDK 1.8 添加】。
ThreadPoolExecutor:最原始的创建线程池的方式,它包含了 7 个参数可供设置。
Executors.newCachedThreadPool:创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程;
Executors.newSingleThreadExecutor:创建单个线程数的线程池,它可以保证先进先出的执行顺序;
Executors.newScheduledThreadPool:创建一个可以执行延迟任务的线程池;
Executors.newSingleThreadScheduledExecutor:创建一个单线程的可以执行延迟任务的线程池;
Executors.newWorkStealingPool:创建一个抢占式执行的线程池(任务执行顺序不确定)【JDK 1.8 添加】。
ThreadPoolExecutor:最原始的创建线程池的方式,它包含了 7 个参数可供设置。
5.如何查看死锁
1.通过jstack命令查看:jstack/jvisualvm命令中显示发生了死锁的线程
2.或者两个线程去操作数据库时,数据库发生了死锁,可以查询死锁情况的SQL
1.查询是否锁表
show OPEN TABLES where In_user > e;
2.查询进程
show processlist
3.查询正在锁的事务
select * from information_schema.innodb_locks
4.查看等待锁的事务
select * from information_schema.innodb_lock_waits
6.死锁原因及如何避免死锁
1.死锁原因
1.一个资源每次只能被一个线程使用
2.一个线程在阻塞等待某个资源时,不释放已占有的资源
3.一个线程已获得的资源,在未使用完之前,不能被强行剥夺
4.若干线程形成头尾相接的循环等待资源关系
2.注意
1.注意加锁顺序,保证每个线程按同样的顺序进行加锁
2.注意加锁时限,可以针对锁设置一个超时时间
3.要注意死锁检查,这是一种预防机制,确保在第一时间发现死锁并解决
7.就绪--->运行
join,yeild
8.线程状态/生命周期
1.创建
2.就绪
Thread.start()
3.运行
CPU执行线程 run方法执行
4.等待
Object.wait()
Object.notify()
5.休眠
Thread.sleep(long time)
6.消亡
run执行完毕,Thread.destroy()
9.线程分类
普通线程
守护线程
JVM
1.OOM异常的原因和解决方法
1.栈溢出异常,可能导致OutOfMemory
2.堆溢出异常
1.超大对象,通常是一个大数组
2.超出预计的访问量,比如各种秒杀活动
3.内存泄漏,大量对象引用没有释放,JVM无法对其自动回收
3.元空间异常
解决
1.如果是因为超大对象,比如一次性查询了数据库全部结果,需要对结果集进行过滤
2.添加内存条
3.如果是内存泄漏,需要找到持有的对象,修改代码设计,比如关闭没有释放的连接
2.什么情况下导致栈溢出异常
1.局部数组过大
2.递归调用层次太多
3.指针或数组越界
3.项目中用的GC算法是什么?
1.引用计数法(基本不用)
实现简单、解决不了循环引用问题,容易造成内存泄漏
2.标记清除法(老年代)
实现简单,遍历两边堆内存,效率低容易造成内存碎片
3.标记整理清除法(老年代)
解决了内存碎片问题,劣势遍历三遍,效率更低
4.复制算法(年轻代)
效率高,浪费内存空间
4.JVM结构
5.JVM怎么保证出栈顺序
遵循"先进后出/后进先出"原则,栈顶就是当前的方法,执行完毕就弹出栈,依次执行下面的方法
6.JVM垃圾回收判定
1.引用计数法:当一个变量或者实例没有被引用时(四种引用)
1.强引用:Object obj = new Object();
2.软引用:SoftReference<Object> sf = new So...;
在系统将要OOM之前,回收
3.弱引用:WeakReference<Object> wf = new ...;
GC工作时,无论内存是否够,都会回收
4.虚引用:PhantomReference<Object> pf = new ...;
为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知
2.可达性分析算法:扫描堆中的对象,看是否能够沿着GC Root对象为起点的引用链找到该对象,找不到,表示可以回收
7.JVM堆中老年区保存的都是什么对象?
大对象或者长期存活的对象
1.经过15次Minor GC,如果一个对象还活着,就会从From区跑到老年代
2.当一个对象超过指定的大小时,
-XX:PretenureSizeThreshold(设置一个大对象的标准)
3.当伊甸园区经过minorGC之后,存活的对象大小之和+from区存活大小之和如果大于to区大小,直接从伊甸园区跑到老年代
8.类加载器分类
1.启动引导类加载器
2.拓展类加载器
3.应用类加载器
4.自定义类加载器
9.双亲委派机制
1.当某个类加载器要加载.class文件时,会先把这个任务委托给自己的上一级加载器
作用
1.保证系统核心类库不被随意篡改,沙箱安全机制
2.避免类的重复加载
10.方法区
存静态变量、类信息、常量
JDK1.7:永久代PermGen
JDK1.8:元空间Metaspace
运行时常量池
1.运行时常量池:非字符串常量池
2.string table:保存string信息的
3.JDK1.7以后,字符串常量池跑堆中,运行时常量池还在方法区
SpringBoot
1.Boot和微服务架构有啥区别
1.微服务是一种概念,boot是一种具体实现
2.SpringBoot启动过程说一下(2)
3.SpringBoot怎么使用的,使用了多久
1.自动配置
2.依赖管理,只需引入一个框架的场景启动器即可使用
SpringCloud
1.SpringCloud有哪些组件
1.服务注册与发现,配置nacos
为什么不用eureka
因为eureka sc2的时候停止维护。只能作为注册中心,如果想要配置还需要单独加一个Spring Cloud Config。
nacos集中配置,利用namespace隔离配置,版本回滚,中间件加的越多越不稳定。
nacos集中配置,利用namespace隔离配置,版本回滚,中间件加的越多越不稳定。
2.负载均衡服务调用Ribbon
未来替换方案Spring Cloud LoadBalancer
Ribbon VS Nginx
Nginx是服务器负载均衡,客户端所有请求都会交给nginx,
由nginx实现转发请求。即负载均衡是由服务器端完成的
由nginx实现转发请求。即负载均衡是由服务器端完成的
Ribbon本地负载均衡,在调用微服务接口的时候,会在注册中心获取服务列表
然后缓存到JVM本地,从而在本地实现RPC远程服务调用
然后缓存到JVM本地,从而在本地实现RPC远程服务调用
核心组件Irule
3.服务接口调用OpenFeign(支持MVC注解)
1.执行原理
1.SpringCloud为每个FeignClient生成一个代理对象
2.代理对象会分析类与方法上的注解,然后判断出服务名与请求方法名的路由
3.从注册中心获取指定服务名的所有真实地址
4.利用负载均衡策略选择一个最佳地址,利用RestTemplate进行调用
5.等待结果返回
2.对于服务的地址,每次请求都需要去注册中心获取吗?
不是每次都需要,Feign维护了一个服务与地址的关系清单List<>,当我们使用Nacos(RPC),
注册中心的某个服务的地址变化时,nacos将数据实时推送(长连接)给SpringCloud;
然后更新关系清单,确保我们每次服务调用这个清单都是最新的
注册中心的某个服务的地址变化时,nacos将数据实时推送(长连接)给SpringCloud;
然后更新关系清单,确保我们每次服务调用这个清单都是最新的
3.如果选择的最佳地址由于某种原因无法使用该如何处理?
Feign会进行重试,可以进行配置:重试次数,超时时间,请求处理时间
4.服务与服务之间的调用是否会经过网关?
默认不会,Feign会从注册中心根据服务名查询服务地址,然后发起请求
4.断路器Hystrix,SpringCloud Alibaba Sentinel
服务降级Fallback
服务熔断Breaker
服务限流Flowlimit
5.网关gateway
1.Route(路由)
2.Predicate(断言)
3.Filter(过滤)
4.请求过滤,统一鉴权,负载均衡
6.分布式链路追踪 sleuth+zipkin
2.分布式锁的设计细节(2)
实现方式
1.基于redis★:setnx
2.基于关系型数据库:唯一键索引
3.基于zookeeper:结构类似linux,文件路径唯一性
实现步骤
1.使用setnx保证独占排他,客户端获取锁,服务器立马宕机会导致死锁
2.set key value ex 30 nx,解决了死锁问题,并且保证了加锁和设置过期时间的原子性
3.解决不可重入导致的死锁问题:hset类似于嵌套map的结构,
第一个key是锁的名字,第二个key是uuid用于确保可重入,
加锁时lockName和uuid都相同就将value+1。
第一个key是锁的名字,第二个key是uuid用于确保可重入,
加锁时lockName和uuid都相同就将value+1。
4.防误删:删除之前先判断是否是自己的锁:
hexists判断key+uuid确认是否是自己的锁,
如果value的值大于1就-1,如果=0就完成解锁。
hexists判断key+uuid确认是否是自己的锁,
如果value的值大于1就-1,如果=0就完成解锁。
5.自动续期:Timer定时任务;每过三分之一时间,就重新执行expire key 30
3.redlock讲一下你的理解
1.和zookeeper一样的一个少数服从多数的思路,假如部署5台redis,获取锁的时候,超过半数获取到锁,就算成功成功获取到锁
2.锁必须要有一个超时强制释放机制,避免服务器宕机,锁无法得到释放
3.取锁之前和取锁之后都获取当前时间,endTime-beginTIme > TTL(超时时间),表示刚获取到锁就过期了,获取锁失败
4.如果获取锁失败,那么要在所有redis节点执行释放锁操作。避免由于超时情况,有些节点获取到锁,无法得到释放
4.redlock的弊端
1.宕机重启之后,2个客户端拿到同一把锁。---延时重启
2.时钟跳跃
时钟跳跃可能会导致机器宕机后立马重启
3.客户端大延迟(比如full GC),2个客户端拿到同一把锁
5.前端调用接口,后端寻找对应微服务的具体链路
网关经过各个过滤器之后将符合断言的请求路由到具体的微服务接口
6.CAP解释一下
1.一致性
2.可用性
3.分区容错性
7.threadlocal怎么传递参数的
1.通过set方法可以直接存入一个参数
2.get方法可以直接拿出参数
3.真正的局部变量是ThreadLocalMap<ThreadLocal,载荷值>
4.先获取当前线程,T t
5.ThreadLocalMap map = getMap(t)
8.网关怎么配置路由
1.路由id:唯一标识
2.uri:lb://ums-service
3.predicates:
- Path=/ums/**
- Path=/ums/**
4.filter过滤器
负载均衡配置
9.网关过滤器有哪些
1.全局过滤器
实现GlobalFilter,顺序实现Ordered
2.局部过滤器
1.继承AbstractGatewayFilterFactory抽象类
2.在需要过滤的微服务路由中配置该过滤器
3.定义一个读取配置的静态内部类
4.实现shortcutFieldOrder(),指定接收参数字段顺序
5.实现shortcutType(),指定读取字段的结果集类型
6.构造方法:super(KeyValueConfig.class);指定静态内部类作为抽象类的泛型
项目
1.购物车的具体流程(4)
1.需求分析
1.购物车比较特殊,是读多写多的一个场景,所以处理方式和别的业务有区别
采用redis+mysql的方式进行存储:不管有没有登录都将用户信息存入数据库,方便以后进行数据分析
查询时,从redis查询提高查询速度;写入时,同步操作redis,异步操作mysql进行写入。
查询时,从redis查询提高查询速度;写入时,同步操作redis,异步操作mysql进行写入。
2.用户信息使用拦截器同一获取
springboot自定义拦截器
1.编写自定义拦截器实现HandlerInterceptor接口,重写前置方法
2.编写配置类实现WebMvcConfigurer接口,重写addInterceptor方法,配置拦截路径
3.将用户的信息存入ThreadLocal线程局部变量中,确保用户信息可以在微服务的调用中传递下去
1.先获取当前线程,T t
2.通过getMap(t)方法获取局部变量ThreadLocalMap
3.ThreadLocal真正的局部变量就是Map,key是当前的ThreadLocl,value是传入的变量
4.通过调用get,set方法就可以完成参数的传递
3.新增购物车
1.判断是否登录
1.未登录:使用UUID生成userKey作为用户id进行存储
2.已登录:使用该用户的userId进行存储
2.判断该用户的购物车中是否有该商品
1.没有:新增
调用其他的微服务查询该商品的销售属性、营销属性、库存信息等,并将价格存入实时价格缓存中
2.有:更新数量
3.新增时将商品价格加入实时价格缓存,因为用户将商品加入购物车后,商品的价格可能会有变化。缓存+MQ;
商品价格改变后发送消息给购物车微服务,向redis进行价格同步,mysql中的价格采用xxl-job定时任务每隔一个星期更新一次
商品价格改变后发送消息给购物车微服务,向redis进行价格同步,mysql中的价格采用xxl-job定时任务每隔一个星期更新一次
4.对mysql的操作使用SpringTask的异步任务完成的(本地异步性能高)双写、如果追求高一致性就双删
1.编写一个类实现AsyncConfigurer
1.配置线程池
1.核心线程数
2.最大线程数
3.空闲线程的存活时间
4.存活时间单位
5.工作队列
6.线程工厂
7.拒绝策略
2.配置异步异常处理器
2.新建一个异步的service类,因为同一个service中,使用分布式事务会有问题
4.查询购物车
1.不管有没有登录,都先用userKey查询未登录的购物车
2.判断登录状态,userId == null,直接返回未登录的购物车
3.已登录:判断未登录购物车是否为空,为空:就直接返回已登录购物车
4.非空:就把未登录的购物车合并到已登录的购物车中(判断是否有相同的商品)
1.相同:更新数量(此时未登录购物车中商品依然存在,后续需要删除)
2.不相同:新增购物车记录(将userKey设置为userId即可)
5.查询并返回已登录购物车
6.清空未登录的购物车(相同商品依然存在未登录购物车中,所以需要删除)
2.订单的具体流程
1.订单确认(异步编排优化获取)
1.用户的收货地址列表
2.商品清单
3.购物积分
4.库存、营销信息、销售属性
5.当前页生成一个唯一的orderToken确保订单提交的幂等性
页面携带一份,redis存一份,提交时进行比较,提交完成后删除。
页面携带一份,redis存一份,提交时进行比较,提交完成后删除。
1.分布式id:主机和进程的机器码/timeId
1.雪花算法
2.用户id+时间戳
3.手机号+时间戳
4.uuidgenerator(百度)
5.leaf(美团)
6.mybatis提高的idworker
2.订单提交(异步编排)
1.防重
1.根据页面传过来的orderToken和redis中的orderToken进行比较
2.验证通过后要把redis中orderToken进行删除,为了保证验证和删除操作的原子性,使用lua脚本
2.验总价
1.把页面上的总价格和数据库中的总价格进行比较,防止因为一些促销活动的过期导致价格不一致的问题
3.限购数量
4.验库存并锁库存(防超卖)
1.验库存和锁库存在同一个接口中完成,保证原子性
2.获取仓库的集合,根据大数据提供的接口获取离该用户最近的一个仓库,对库存进行加锁
3.锁完库存立马发送延时消息25小时后自动解锁库存,防止系统异常导致库存永久被锁
5.创建订单
1.由于远程调用可能会超时,所以也需要发送消息给订单微服务,将订单修改为失败订单
2.订单提交后,立刻发送一个延时消息24小时未支付,自动关单,将订单修改为无效订单
6.异步删除购物车中记录
发送消息给购物车微服务删除购物车中对应的记录
3.退单
1.当用户点击确认退单时
订单由待发货--->无效订单
2.发送消息给订单微服务去更新订单的状态
3.发送消息给库存微服务更新的数量
3.游客数据怎么存的
使用UUID随机生成的user-key作为数据库的userId存放在数据库中。
4.当用户添加购物车,但后续商品价格浮动,如何展示给用户
实时价格:使用缓存+MQ的最终一致性
5.你们订单号的实现(雪花算法),有什么问题吗?
idworker:主机和进程的机器码
6.你们Controller层的接口是否直接暴露给前端
先经过网关
7.介绍下SpringSecurity在你们项目中的使用
8.授权中心怎么做的,大概流程说一下
1.获取用户的请求路径,判断该路径是否在拦截名单中;不在名单中就直接放行
2.获取请求中的token,判断是否同步请求;同步请求从cookie中获取,异步请求从请求头中获取
(一个网站有很多cookie,使用cookie方式传递token,网络传输压力太大)
(一个网站有很多cookie,使用cookie方式传递token,网络传输压力太大)
3.判断token是否为空,为空就设置状态码302并重定向到登录页面
4.使用公钥解析token,并判断IP地址是否相同,不相同就重定向到登录界面(解析出现异常也重定向到登录)
5.将登录信息传递给后续服务,后续服务不用再解析JWT了
6.放行
9.分布式锁在你们项目中的使用
1.为了防止缓存击穿,添加的分布式锁,基于Redisson实现
10.核心线程数你们配置的多少?
1.CPU密集型
核数+1
2.IO密集型
核数/(1-0.9)
4核;40个
11.项目的事务用的什么
1.后台系统使用seata
2.前台系统MQ的最终一致性+补偿机制
12.细节
1.商品表10w记录
2.用户20w
3.订单8w
4.服务器60-70台
nginx:2
nacos:3
微服务:40左右
mysql:12
redis:主从哨兵:5
rabbitmq:2
ES:3
5.并发600-1000,甲方数据拿不到
6.系统出问题平时怎么查看?
arthas组件
7.生产环境部署:docker
1.开发人员将代码提交到Gitlab代码仓库
2.Jenkis从Gitlab中拉取项目源码,编译并打成jar包,然后构建成Docker镜像,将镜像上传到Harbor私有仓库
3.Jenkins发送SSH远程命令,让生产部署服务器到Harbor私有仓库拉取镜像到本地,然后创建容器
4.用户就可以访问容器了
13.数据库表的设计
1.三大范式
第一范式:字段原子性
第二范式:主键关联性
第三范式:字段之间不出现传递关系
2.根据页面分析所需的数据,设置一个冗余字段备用(以购物车为例)
id,user_id,sku_id,check,title,default_image,price,count,store,sale_attrs,sales,extra_attr
0 条评论
下一页