面试总结

面试总结

关于redis问题:

缓存穿透:

使用redis的常用场景是通过key先去redis中获取数据,没有再去数据库中查询然后存入redis中,当这种情况下可以利用一个缓存根本查不到的key,让代码频繁访问数据库,而且数据库也查不到值返回null无法存入redis中,相当于缓存中间层失效了,而高并发可能会压垮数据库,所以解释为穿透了;
在这里插入图片描述在这里插入图片描述

解决方案:
简单粗暴法: 如果数据库查询的数据返回空或者异常,我们存入一个空字符串进redis中,并设置过期时间及时清理,可能有小部分并发请求刚进来能够去读取数据库,大部分还是能够走redis;
在这里插入图片描述
布隆过滤器:相当于提前定义好一个超大的map集合,进来的数字先到百万集合里查找一遍,查不到则直接过来掉;
在这里插入图片描述

缓存雪崩:

是指我们大部分的缓存设置了同样的过期时间,同时过期了或者redis服务器突然挂掉了,导致高并发请求全去数据库里查询,从而压垮数据库;
解决方案:
给每个key增加一个随机值,比如1-5分钟随机值,从而避免缓存大规模失效;
搭建集群;

缓存击穿(热点key):

非常的热点key突然失效了,导致大规模并发直接访问数据库导致数据库被压垮;
解决方案:
利用redis的分布式锁,先锁住其他请求,再去访问数据库获取数据存入缓存;

总结:缓存击穿是巨大热点数据造成,缓存穿透更针对是被别人主动攻击造成;

关于rabbitMq问题:

消息丢失:百分百投递
消息重复消息:redis set实现幂等性
消息顺序性:
大规模消息积压问题:MQ没得到ack响应无论是自动还是手动,这些消息会堆积在Unacked消息里,导致MQ大量消息堆积,越来越慢;

关于分布式锁问题:

一个合理的分布式必须具备:
1.互斥性,在任意时刻,都只能一个客户端持有锁;
2.不会发生死锁,即使客户端发生异常没有及时释放锁,也不会影响后续客户端加锁;
3.具有容错性,挂掉个别redis没关系,还能保持正常运行;
4.解铃还须系铃人,加锁和解锁必须是同一个客户端;

我们重点考虑互斥性、防止死锁、解铃还须系铃人三点;

redis实现:

1.maven引入jedis
2. 使用jedis setnx方法
String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
在这里插入图片描述

关于数据锁问题:

乐观锁实现:

乐观锁概念:每次在提交时判断,vesion是否已经被修改;
Mysql实现乐观锁:在表字段里添加vesion字段,每次修改时先获取版本号,update set vesion = vesion+1 where vesion =

常用方法:乐观锁+自旋(自循环直到更新成功);

悲观锁实现:

悲观锁概念:同一时刻,只能有一个程序操作该数据;
Mysql实现悲观锁:
1.取消mysql自动提交机制;
2.开启手动事物
3.上锁操作:select * from a where id = 1 FOR UPDATE;
4.Where id = 1 非常重要,不对主键上锁可能会引起表锁;
5.执行逻辑
6.提交事物;

悲观锁上锁时够支持读,但是不支持写;特别注意:数据库引擎必须用InnoDB;

Spring boot自动配置原理:

@SprongBootApplication注解进入,可以看到自动配置注解@enableAutoConfiguration,

再进入可以看到自动配置导入选择器@AutoConfigurationImportSelector
再进入可以看到方法getCandidateConfigurations(),获取备选的配置,从METE-INFO/spring.factories读取配置文件列表

基于这点,我们自定义start可以再METE-INFO下新建spring.factories 指定配置文件路径,
配置文件引入实体配置类自动装配属性;
别忘了要新增工程修改pom添加spring-boot-autoconfigure依赖

hashMap底层实现

1.7:数组+单向链表
1.8: 数组+单向链表+红黑树 当大于8个节点是数组+红黑树
在这里插入图片描述
在这里插入图片描述
解释为什么是数组+链表:
数组结构是连续的,方便根据下标寻址,但是插入删除难;
链表结构是松散的,插入删除容易,但是寻址难;

hashMap 默认16个长度的数组,hashMap按hash(key.hashCode())%len
获取数组下标,也就是key的哈希值对数组的长度取余得到;比如12%16,28%16得到的数组下标都是12;

重要概念:Entry类,由4个重要的属性组成(hash,key,value,next)

存在同一个下标内的entry是类似压栈操作,加入A先进入,B再进去,那么B.next = A;
在这里插入图片描述

1.hashMap中 put()方法存值原理:先根据key调用hashCode()方法,返回的hashCode用于找到bucket桶位置;
2.HashMap可以使用null作为key

3.到了 1.8 以后,就引用了红黑树,1.8以后这个链表只让挂7个元素,超过七个就会转成一个红黑树进行处理(最多是64,超多64 就会重新拆分)。
    当红黑树下挂的节点小于等于6的时候,系统会把红黑树转成链表。 Node 在jdk1.8之前是插入l链表头部的,在jdk1.8中是插入链表的尾部的。
hashMap 扩容:
    hashMap 会根据 当前的hashMap 的存储量达到 160.75=12 的时候,就是扩容 162=32 依次类推下去。2倍扩容。
    扩容原理类型创建新的hashMap,重新计算对应桶位置,重新排列到链表中;

抽象化:

具体体现在抽象类和接口,抽象类是对根源的抽象,比如男人,女人,那么可抽象出人;接口抽象是对动作行为的抽象,比如人都要吃饭,可以用筷子吃,可以用叉子吃,那么如何吃饭可以抽象出一个类来;

Java抽象类:没有具体的内容来描绘一个类;
抽象类是对根源的抽象,抽象类只能够被继承,只能够被用作基类,不能被实例化;

ConcurrentHashMap底层实现:

1.7之前:思路其实跟hashmap差不多,但是因为它支持并发,引入了segment数组,segment数组继承了ReentrantLock来进行加锁,保证每个代码段的安全,进而实现全局线程安全;

并发度:默认是16,因为ConcurrentHashMap默认有16个segment;
也就是默认最多支持16个并发;

1.8之后:同样引入了红黑树;

6大设计原则:抽象编程

开闭原则:对扩展开放,对修改关闭;在程序需要拓展时,不去修改原有的代码,就能够对代码进行拓展,通常我们需要使用接口和抽象类去抽象化;

开闭原则是面向对象最基础的设计原则,其他五个原则都是开闭原则的具体形态;
如何实现开闭原则:
1.抽象约束,通过抽象类或者接口;
2.元数据控件模块,通俗的说就是配置参数;
3.封装变化,在设计层面,将相同的变化封装到同一个抽象中,将不同的变化封装到不同的抽象中;
4.在系统功能层面,可提供新的实现类重新实现功能;也可继承原始抽象类,对原始方法进行拓展;

单一职责原则:

	简单来说就是一个接口或类只有一个职责,只负责一件事。
	单一职责的优点:

1.类的复杂性降低,代码可读性提高,可维护性提高;
2.变更引起的风险降低;

里氏替换原则:

从抽象化到具体化的实现过程,就必须引入里氏替换原则;
里氏替换原则是指任何基类可使用的地方,子类一定可用;
里氏替换原则是对开闭原则的补充,因为实现开闭原则的关键步骤是抽象化,
而基类与子类的继承关系就是抽象化的具体实现;
里氏替换原则为良好的继承定义了一个规范,其中规范包含了4层意思:

1.子类必须完全实现父类的方法;
2.子类可以有更多自己的属性和方法;
3.覆盖或实现父类的方法时,输出的结果可以被缩小;(假设父类输出T类,子类输出S类,那么S必须是T的子类)、

依赖倒置原则:

		1.具体业务实现叫做高层模块,基础属性和方法叫做低层模块;
		new出来的实例称为细节,抽象类或接口称为抽象;
		2.高层模块不应该依赖于底层模块,二者都应该依赖于抽象;
		3.抽象不应该依赖于细节;
		4.细节应该依赖于抽象;

https://www.jianshu.com/p/c899300f98fa
为何称为倒置:人认识事物的惯性思维是从具体 到 抽象,但是依赖倒置是先从抽象开始考虑问题;将类与类打交道,改为抽象与类打交道;

接口隔离原则:

接口隔离原则,并不能狭义的理解为interface定义的接口,
而是一种更广泛的概念,可以是接口,抽象类,实体;
1.客户端不应该依赖于它不需要的接口;
2.类间的依赖关系应该建立在最小的接口上;

在这里插入图片描述
接口隔离原则通俗来讲,接口尽量细化,同时接口尽量的少;
那么与单一职责原则的区别呢;都是要求接口要细化,但是单一原则没有要求接口尽量的少;
根据接口隔离原则拆分接口时,必须先满足单一职责原则;

迪米特法则:

1.最少知道原则,一个对象对其他对象有最少的了解;遵循迪米特法则只要做到只和朋友交流,并且减少对朋友的了解;
2.那么朋友是什么意思呢:朋友一般在继承和实现中体现;
3.从代码的角度解释:只暴露应该暴露的属性和方法,既在编写代码时指定封装的权限,公有、保护、私有;
4.从依赖的角度,只依赖应该依赖的对象;

控制反转和依赖注入:

控制反转(IOC):将传统的由程序来直接控制对象的调用权,交给外部容器去管理;
控制反转可以说是依赖倒置的升级,去解决对象依赖问题;
为什么叫做控制反转:控制意味着谁来控制对象的创建,是程序本身还是第三方容器?
反转意味着从常规思维的程序本身转变到第三方;

依赖注入:控制反转是一种思维方式的变化,那么依赖注入就是具体的实现实践;

总结为:通过依赖注入实现控制反转;

MySQL索引原理:

https://blog.csdn.net/u013235478/article/details/50625677?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-2&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-2

底层使用B+树实现,B+代表平衡(balance),B+树是由二叉查找树,平衡二叉树(AVLTREE),平衡多路查找树(B- Tree);

二叉树:左子树的键值小于根的键值,右子树的建大于根键值;
在这里插入图片描述在这里插入图片描述

查找层级为N的节点,查找次数也为N

深度越深查询速度越慢;这时就需要引入平衡二叉树概念;

平衡二叉树:在符合二叉树的前提下,任何节点的两子树的高度差为1;
如果对平衡树进行插入或增删节点,可能导致平均树失去平衡;失去平衡的姿态概括为:LL(左左)、RR(右右)、LR(左右)、RL(右左)
解决方法:通过旋转解决;

平衡多路查找树(B- Tree): 注意这里的“-” 不是减的意思,只是代表分隔
	是为磁盘等外存设备设计的一种平衡查找树;
	理解为节点不是一个个的数,而是一个磁盘块;
	那么首先明白磁盘概念:
		系统从磁盘读取数据到内存中是以磁盘块为基本单位,
		位于同一个磁盘块的数据会被一次性读取出来;

B-Tree结构是为了让系统更高效的找到数据所在的磁盘块;
在这里插入图片描述
每个节点占用一个磁盘块,一个节点有2个升序的主键和相应的Data和3个指向子树节点的指针;

在这里插入图片描述
B-Tree相比平衡树减少了节点个数,因为数据是在磁盘中,一个磁盘中可以有多个数据;

B+ Tree
是在B- Tree 的优化,使其更适合实现外存储索引结构,InnoDB存储引擎就是用B+Tree实现索引结构的;

在这里插入图片描述
与B-树对比:
因为B-树每个节点都包含key和data,当data过大时,又会出现节点过多的情况,那么B+树,根节点和子节点只存储key和指针,那么子节点能够存储更多的key,减少深度;叶子节点才会存储key和data;并且叶子节点是链式环结构,又叫顺序指针,当找到10,可以按照顺序接着往下找,极大加快了查询速度;
B+Tree能进行2种查找方式:根据主键范围查找,根据根节点随机查找;
B+Tree的索引可分为2类:聚集索引和辅助索引;聚集索引的B+Tree叶子节点存储的整张表的行记录数据;辅助索引B+Tree存储的是聚集索引键,当通过聚集索引查找数据时,会先查找到主键再通过主键查找到行数据;

MyISAM索引实现:

MyISAM引擎使用B+Tree作为索引结构,
叶子节点存的是Data数据行的地址;
主键索引与辅助索引在结构上没有任何区别,Data都是存地址;

InnoDB索引实现:

InnoDB也是使用B+Tree作为索引,但是实现方式不同,
InnoDB的叶子节点Data中存的是完成的行数据;

在这里插入图片描述
叶子节点包含了完整的行记录,主键+完整的Data行数据,这种索引叫做聚集索引;
InnoDB的辅助索引,与主键索引不同,它Data里存的是主键的值;

在这里插入图片描述
在这里插入图片描述
聚集索引这种实现方式使得按主键的搜索十分高效,但是辅助索引搜索需要检索两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录。

了解不同存储引擎的索引实现方式对于正确使用和优化索引都非常有帮助,例如知道了InnoDB的索引实现后,
就很容易明白为什么不建议使用过长的字段作为主键,因为所有辅助索引都引用主索引,过长的主索引会令辅助索引变得过大。
数据库引擎:数据库引擎是存储、处理和保护的核心服务;·····

ISAM引擎:

不支持事务处理、不支持外键、不能够容错、也不支持索引;但是查询快;

InnoDB存储引擎:

支持事务,并实现了四种隔离级别;支持行锁和外键;	

索引类型 唯一索引和主键索引区别:

Mysql 常见索引:主键索引、唯一索引、普通索引、全文索引、组合索引;

唯一索引值必须唯一,但是可以为空
主键索引值必须唯一,且不能为空
全文索引:仅可用于MyISAM表,用于在一篇文章中,检索文本信息的内容;针对较大的数据,全文索引很耗时耗空间;
组合索引:最左原则;

分布式锁要自己实现需要注意哪几点:

1.互斥性;
2.防死锁;
3.解铃还须系铃人
4. 设置超时时间与解锁时,保证原子性
5. 保证加锁时设置值和超时时间,解锁时比较key和删除key是原子操作;

索引有哪些结构 树结构为什么查询快:

b-树
b+树
hash

Io和nio的区别:

在这里插入图片描述

1.什么叫面向字节流,什么叫面向缓冲区呢?
IO读取数据和写入数据是面向字节流的,流的意义在于没有缓存,如同我们站在流水线面前,
数据通过传送到依次达到我们面前,我们没法直接从流中读取之前或之后的数据;

NIO是面向缓冲区,读取数据时可先将数据读取到一个稍后处理的缓冲区,需要时可在缓冲区前中后移动,
相比IO读取增加了灵活性;

但是增加了灵活性是有代价的,我们必须管理好缓冲区,比如新缓存的数据不能覆盖掉以前的缓存数据,

2.阻塞和非阻塞
IO读写数据时,如果数据还没准备好,那么读写数据时就会被阻塞,直到数据准备好;

NIO非阻塞,NIO通过Selector,通过调用select来获取已经准备好的Channel去执行;
NIO的重要概念:
	缓冲区:读写数据都是先存入缓冲区;
	通道Channel:通道理解为传输工具,需要通过它将数据读取或写入缓冲区;
	选择器Selector:选择器是NIO最重要的类,提供了选择已经就绪任务的能力;
	Selector会不断的轮询注册在上面的所有channel,如果某个channel为读写等事件做好准备,
	那么就处于就绪状态,通过Selector可以不断轮询发现出就绪的channel,进行后续的IO操作

Netty怎么实现nio的:

netty主要是对NIO进行了封装优化,

幂等性处理方法:

1.redis set简单实现
2.如果是对数据的修改,可以用乐观锁;
3.结合MQ使用的话,可以检测是否成功消费状态;

加解密算法:

对称算法:
非对称算法:

事务的传播行为:

为了解决本地事务中,业务层之间的调用;

分为2大类:
支持当前事务:
Required:如果当前有事务,则加入当前事务,如果没有则新建一个事务;
SUPPORTS:如果当前有事务,则加入当前事务,如果没有就以非事务运行;
MANDATORY:强制事务,如果当前有事务,则加入当前事务,如果没有则抛出异常;
不支持当前事务:
REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起;
NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起;
NEVER:以事务方式运行,如果当前存在事务则抛出异常;

其他情况:nested:嵌套事务,无论是否已经有事务,都会嵌套一个新事务,当外层事务回滚,都回滚,但是内层事务可以单独回滚;

事物那些情况会失效:

1.@Transactional 只能作用公共类上;

2.事务中抛出了未检测异常,派生于Error或者RuntimeException(比如空指针,1/0)的异常,事务会回滚;
加入方法中的异常被捕获,没有抛出,是不会回滚的,
如果是自定义异常又想回滚,可以在注解声明
@Transactional(rollbackFor=Exception.class)

3.自调用,只有目标方法被外部调用,目标方法事务才会生效;

在这里插入图片描述
同一个类中没用@Transactional 注解的方法调用同类中,有@Transactional 注解的方法,事务不会生效;
4.在使用@Transactional 注解的方法中使用多线程是不被事务控制的;因为线程不属于spring管理;
解决:应该调用接口,在接口中使用事务注解;

springMvc的运行流程:

1、  用户发送请求至前端控制器DispatcherServlet。

2、  DispatcherServlet收到请求调用HandlerMapping处理器映射器。

3、  处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),
生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。

4、  DispatcherServlet调用HandlerAdapter处理器适配器。

5、  HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。

6、  Controller执行完成返回ModelAndView。

7、  HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。

8、  DispatcherServlet将ModelAndView传给ViewReslover视图解析器。

9、  ViewReslover解析后返回具体View。

10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。

11、 DispatcherServlet响应用户。

SQL的执行顺序:

编写sql的顺序是:select from join on where group by having order by limit

执行顺序:首先肯定要from 找到对应的表,
join on也是找到对应的表,where进行筛选,
group by having进行进一步筛选,再进行查询select,
再排序order by limit

FROM → JOIN → ON →
 WHERE → GROUP BY → HAVING → SELECT → ORDER BY→ LIMIT

hashmap扩容时死循环问题:

redis为什么快:

ES二次打分:

主要是利用FunctionScoreQueryBuilder,对文档的结果进行二次排序
1.设置权重weight:相当于对评分做加法;
2.field_value_factor:相当于对评分做乘法;
3.random_score 随机
4.衰减函数,一般时间或范围(经纬度)字段,用此函数;

    /**
     * 设置二次打分规则
     * @param searchName
     * @return
     */
    private NativeSearchQueryBuilder setShopNativeSearchQueryBuilder(String searchName,NativeSearchQueryBuilder nativeSearchQueryBuilder){
        /*搜索商品-------------------------------*/
        /*
         * 商品匹配规则: 优先名称匹配 > 能够匹配到搜索词中的类目 > 销量 > 付款转化率 > 商品评分
         * */
        List<FunctionScoreQueryBuilder.FilterFunctionBuilder> filterFunctionBuilders = new ArrayList<>();
        //2.商品搜索名称最匹配的权重最大
        filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                QueryBuilders.matchQuery("shopName", searchName).analyzer("ngramSearchAnalyzer"),
                ScoreFunctionBuilders.weightFactorFunction(50)));
        //4.销量
        filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                ScoreFunctionBuilders.fieldValueFactorFunction("salesCountSum")
                        .modifier(FieldValueFactorFunction.Modifier.LN1P).factor(2f)));
        FunctionScoreQueryBuilder.FilterFunctionBuilder[] builders = new FunctionScoreQueryBuilder.FilterFunctionBuilder[filterFunctionBuilders.size()];
        filterFunctionBuilders.toArray(builders);
        FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(builders)
                .scoreMode(FiltersFunctionScoreQuery.ScoreMode.SUM);
        nativeSearchQueryBuilder.withQuery(functionScoreQueryBuilder);
        return nativeSearchQueryBuilder;
    }

join() 和 sleep() 区别?

sleep(long)方法在睡眠时不释放对象锁
  join(long)方法在等待的过程中释放对象锁

synchronized和lock以及volatile的区别

参考:https://www.cnblogs.com/baizhanshi/p/6419268.html
synchronized 与 lock:
1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;

2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;

3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;

4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。

5)Lock可以提高多个线程进行读操作的效率。

按照锁的相关概念:
1.Lock与synchronized都是可以重入锁;
2.Lock是可中断锁,synchronized不是;
3.公平锁指:能根据请求锁的顺序去获得锁;
Loc默认是非公平锁,但可以设置为公平锁,synchronized不是;

synchronized 与 volatile:

1、volatile不会进行加锁操作:

volatile变量是一种稍弱的同步机制在访问volatile变量时不会执行加锁操作,
因此也就不会使执行线程阻塞,
因此volatile变量是一种比synchronized关键字更轻量级的同步机制。

2、volatile不如synchronized安全:

在代码中如果过度依赖volatile变量来控制状态的可见性,
通常会比使用锁的代码更脆弱,也更难以理解。
仅当volatile变量能简化代码的实现以及对同步策略的验证时,
才应该使用它。一般来说,用同步机制会更安全些。

3、volatile无法同时保证内存可见性和原子性:

加锁机制(即同步机制)既可以确保可见性又可以确保原子性,
而volatile变量只能确保可见性,
原因是声明为volatile的简单变量如果当前值与该变量以前的值相关,
那么volatile关键字不起作用,
也就是说如下的表达式都不是原子操作:“count++”、“count = count+1”。

直接调用Start 与 run的区别:

Start会创建一个新的线程,再执行run方法;
而直接调用run方法,只是相当于普通方法去执行,并不会开启多线程;

手写单例模式,懒汉式,饿汉式(双重检测)

volatile文档:https://www.cnblogs.com/dolphin0520/p/3920373.html
饿汉式:

class Singleton {
	//直接new出来
    private static Singleton singleton = new Singleton();
    
    private Singleton(){}//构造方法

    public static  Singleton getInstance() {//get方法
        return singleton;
    }
}

赖汉式的双重检验机制:
	JVM实现可以自由的进行编译器优化。而我们创建变量的步骤:
		1、申请一块内存,调用构造方法进行初始化。
		2、分配一个指针指向这块内存。
要理解为什么要加volatile,首先要理解new Singleton()做了什么。
new一个对象有几个步骤。
1.class对象是否加载,如果没有就先加载class对象,
2.分配内存空间,初始化实例,
3.调用构造函数,
4.返回地址给引用。而cpu为了优化程序,可能会进行指令重排序,
打乱这34这几个步骤,导致实例内存还没分配,就被使用了。

再用线程A和线程B举例。线程A执行到new Singleton(),开始初始化实例对象,
由于存在指令重排序,这次new操作,先把引用赋值了,还没有执行构造函数。
这时时间片结束了,切换到线程B执行,线程B调用new Singleton()方法,
发现引用不等于null,就直接返回引用地址了,然后线程B执行了一些操作,
就可能导致线程B使用了还没有被初始化的变量。
```java
public class SingletonClass { 
	//加volatile 是为了防止重排序

  private volatile static SingletonClass instance = null; 

  public static SingletonClass getInstance() { 
    if (instance == null) { 
      synchronized (SingletonClass.class) { 
        if(instance == null) { 
          instance = new SingletonClass(); 
        } 
      } 
    } 
    return instance; 
  } 
  private SingletonClass() { 
  } 
}


## Linux命令:
Vi有几种模式:
命令行模式:打开vi一进来就是命令行模式,可以控制屏幕光标移动,字符或字或行的删除,移动或复制某段进入插入模式,或者到底行模式;
插入模式:命令行模式按下i,进入插入模式
底行模式:在命令模式下,按下:冒号,进入底行模式;底行模式可以查找字符串,列出行号,或者跳到某一行

Vi 退出命令: 
:q! 不保存文件,强制退出;
:w 保存文件,不退出vi命令
:wq 保存并退出;

Vi 修改要撤回
在命令模式下(按Esc进入命令模式),按下U撤销;ctrl+r 恢复撤销
Vi 怎么搜索字符串
在命令模式下,按下/ 某某字符串;N是查找下一个;
Vi 怎么删除行
在插入模式下 按下dd,删除光标所在行;
Linux权限:
怎么新增用户
怎么授权
怎么分组
怎么删除用户
## chmod 777  777是什么意思;

## 手写冒泡排序或者二分查找;
冒泡:
```java
    public static void main(String[] args) {
     int [] arr = new int[] {7,3,9,5,6,8,1};
     for(int i =0;i<arr.length-1;i++){
         for(int j = 0; j<arr.length-1-i;j++){
             if(arr[j] > arr[j+1]){
                 int t = arr[j];
                 arr[j] = arr[j+1];
                 arr[j+1] = t;
             }
         }
     }
    }

二分查找:


    public static int recursionBinarySearch(int [] arr,int key,int low,int high){
    	//校验
        if(key<arr[low] || key > arr[high] || low > high){
            return -1;
        }
        //获取中间位置
        int middle = (low + high ) /2;
        if(arr[middle] > key){
        //递归,在左边,改变high值
            return recursionBinarySearch(arr,key,low,middle-1);
        }else if(arr[middle] < key){
        //递归,在右边,改变low值
            return recursionBinarySearch(arr,key,middle+1,high);
        }else {
        //运气好正好在中间,直接返回
            return middle;
        }
    }

Spring中@Component与@Bean的区别

Redis的过期策略与淘汰机制?

可设置maxmemory来限制大小,当占用内存超过了maxmemory 时,
可设置redis淘汰策略;

redis3.0支持6种淘汰策略:

	no-eviction:不删除策略,当达到maxmemory直接报错;
	volatile-lru:从设置了过期时间的数据集中,删除最少使用的;能保障未
	设置过期时间的有效性;
	volatile-ttl:从设置了过期时间的数据集中,删除剩余过期时间短的;
	volatile-random:从设置了过期时间的数据集中,随机删除;
	allkeys-lru:从全部的数据中挑选最少使用的删除;
	allkeys-random:从全部数据中,随机删除;

hashCode碰撞;

eureka心跳机制原理,心跳机制默认多少秒:

服务者在启动后,默认30秒向eureka发送心跳包;
Eureka 默认90秒未检测到心跳包,则认为服务宕机,注销服务;

lua删除报错怎么办

linux cpu使用率,内存,磁盘使用率

cpu使用率:top
在这里插入图片描述
内存:free -h
在这里插入图片描述

redis 分布式锁,运行时间大于超时时间怎么处理

https底层实现

乐观锁会遇到什么问题

mybatis的工作原理

同步锁的底层实现和优化过程

es的倒排索引原理

怎么实现将分库分表的数据聚合

redis选举机制和实现原理

雪花算法 时间戳问题解决

在这里插入图片描述

当是同个时间的同台机器生成ID,可根据末尾的12个序列号解决;

优点:所有ID是递增的,并且不会重复;
高性能高可用:生成时不依赖于数据库,完全在内存中生成;

缺点:完全依赖于时间,当服务器时间回拨,可能会出现重复;

漏桶算法 令牌桶算法

漏桶算法:漏桶(Leaky Bucket)算法思路很简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水(接口有响应速率),当水流入速度过大会直接溢出(访问频率超过接口响应速率),然后就拒绝请求,可以看出漏桶算法能强行限制数据的传输速率;

令牌桶算法:令牌通算法,是按照一定的速率,把token放入桶中,当有请求进来就去桶里拿token,当桶满时,不再放入tokwn,当token被拿完时,拒绝请求;

两者比较:漏桶算法是控制出水的速度,当大量请求一次进来时,桶溢出了会全部抛出,
令牌通算法,是积攒token,当缓慢请求时,可以积攒很多,来应付大规模请求,减少丢弃的请求;

nginx限流怎么配置

布隆过滤器实现原理

布隆过滤器(Bloom Filter)的核心实现是一个超大的位数组和几个哈希函数;

以下图为例,首先会将所有数组位的都置位0,当集合x,y,z进来时,会根据3个哈希函数进行映射,映射到的位置置位1;
当元素W进来时,假设映射的3个位置中存在一个为0的,就提示w一定不存在;
但是也存在误判,4,5,6的位置就是3个1,假设一个元素正好映射这里,其实这几个位置是其他元素组成的,这个就是误判;
误判率 fpp 的默认值是 0.03,可通过配置减少误判率;
在这里插入图片描述

seata框架实现分布式事务原理:

参考:https://www.jianshu.com/p/044e95223a17
seate框架三个重要角色:
1.Resource Manager(RM):控制分支事务,负责分支注册、状态汇报,并接收事务协调器的指令,驱动分支(本地)事务的提交和回滚。
2.Transaction Coordinator(TC): 事务协调器,维护全局事务的运行状态,负责协调并驱动全局事务的提交或回滚。
3.Transaction Manager (TM):控制全局事务的边界,负责开启一个全局事务,并最终发起全局提交或全局回滚的决议.

下面是一个分布式事务在Seata中的执行流程:

1.TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的 XID。
2.XID 在微服务调用链路的上下文中传播。
3.RM 向 TC 注册分支事务,接着执行这个分支事务并提交
(重点:RM在第一阶段就已经执行了本地事务的提交/回滚),最后将执行结果汇报给TC。
4.TM 根据 TC 中所有的分支事务的执行情况,发起全局提交或回滚决议。
5.TC 调度 XID 下管辖的全部分支事务完成提交或回滚请求。
TM是决议者,负责判断提交/回滚;然后将结果提交给TC;TC负责与RM通信,告诉他们是否提交/回滚;

下面详细说说分支事务是如何提交和回滚的:

第一阶段:
	本地事物直接提交,并在 undo log表生成日志记录,该表记录了全局事物的XID
和回滚的sql,然后上报给TC;

第二阶段:
	TM协议全局提交:TC协调各个RM删除undo log就行;因为第一阶段已经提交了事物;
	TM协议全局回滚:TC协调各个RM通过XID在undo log表找到对应记录,执行回滚记录;然后删除记录;然后上报TC;

分布式全局唯一主键怎么生成

UUID:
	优点:性能高,本地生成,没有调用消耗;
	缺点:字符串太长,不利于储存,很多场景不适用;
雪花算法:
	优点:性能高,并且是按照时间顺序有序递增;
	缺点:依赖时间,假如时间回拨,可能会出现重复ID
专门维护一张数据库表生成唯一自增ID:
	优点:有序
	缺点:性能低,依赖访问数据库,高并发可能压垮数据库;
通过redis自增获取唯一ID:
	优点:有序,性能比访问数据库高;
	缺点:依赖redis,需要保持redis高可用;

用到过哪些设计模式

策略模式:
抽象类:抽象动作
实现类:具体实现
配置类:将具体实现配置到map集合中
上下文切换类:引用抽象类,进行实现类切换;

工厂模式:

http网络协议1.0与1.1的区别:

http超文本传输协议,最初目的是提供发送和接收html页面的方法;
是用于www服务器传输超文本到本地的传输协议;
http/1.0:

为了提升服务器效率,只允许服务器与浏览器保持短暂的连接;
浏览器每一次请求都需要建立tcp连接,服务器处理完请求后立马断开连接;这会造成频繁的建立和断开tcp连接,消耗性能;

http/1.1:

为了克服http/1.0的缺陷,http/1.1支持持久连接,在一个tcp连接上可以连接多个http的请求和响应,减少了建立和断开的消耗和延迟;

http与websocket的区别:

http请求客户端是主动的,服务端是被动的;
websocket 是可以双向通信的,服务器也可以主动发消息给浏览器;

tcp的三次握手过程和每次的作用:

tcp四次握手过程:

tcp与udp的区别:

这里就引出了 TCP 与 UDP 的一个基本区别, TCP 是可靠通信协议, 而 UDP 是不可靠通信协议。

TCP 的可靠性含义: 接收方收到的数据是完整, 有序, 无差错的。
UDP 不可靠性含义: 接收方接收到的数据可能存在部分丢失, 顺序也不一定能保证。

redis主从模式下,主机挂了分布式锁怎么办?

spring 的生命周期

参考:https://www.jianshu.com/p/1dec08d290c1

spring 怎么管理bean

参考:https://blog.csdn.net/l18848956739/article/details/80917853

List泛型集合类以及一个Map<String,Object>集合
简单说明:
a.读取配置文件bean.xml,并根据文件中bean的id,class属性实例化一个BeanDefinition,装入泛型集合中。

b.通过循环+反射,将List中的bean加入到Map<String,Object>中,这里用反射将bean中的className属性转换为一个实例化的bean对象加入到了Map中。

c.提供一个对外的接口,通过传入参数获取该bean。

第一范式:

第一范式(1NF):强调的是列的原子性,即列不能够再分成其他几列;
比如:
联系人表(用户姓名,电话)
但是实际情况是,电话又分为家庭电话和公司电话;
那么这个就不符合原子性,因为电话这个字段还可以再进行分割;
那么应该拆分成联系人表(用户姓名,家庭电话,公司电话)

第二范式:

参考:https://blog.csdn.net/weixin_43971764/article/details/88677688
第二范式:所有的列必须完全依赖主键,不能出现有列依赖于非主键列;
不满足第二范式可能会出现冗余字段;
在这里插入图片描述
在这里插入图片描述

第三范式:

第三范式:满足第二范式的前提下;解决传递依赖;
在这里插入图片描述只要非主键内部存在传递依赖,就不满足第三范式

mybatis 一级缓存、二级缓存

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值