本人双非本科在秋招入职多益网络,然后本次春招,努努力了一把成功拿到了顺丰科技的offer。
以下为我在牛客上收集到的近年来的面试题目以及答案,希望对大家有用。
对于前后都有百分号的sql语句 这个语句是不走索引的
EXPLAIN SELECT * FROM `user` WHERE username LIKE '%ptd_%'; //这个语句是不走索引的
对于只有后面使用% 使用到了索引
EXPLAIN SELECT * FROM `user` WHERE username LIKE 'ptd_%';
对于%在前面的, 这个语句也是走全表扫描,没有走索引。
EXPLAIN SELECT * FROM `user` WHERE username LIKE '%ptd_';
综上所述: 当百分号出现在后面的时候 是走索引的。
https://www.jianshu.com/p/1dec08d290c1
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XEm8q75N-1618384899949)(F:\aboutIT\learningFromduoyi\images\image-20210314212016215.png)]
进程
进程是程序的一次执行过程,是一个动态的概念,是程序在执行过程中分配和管理资源的基本单位,每一个进程都有一个自己的地址空间,至少有五种状态:初始态,执行态,等待状态,就绪状态,终止状态。
线程
线程是CPU调度和分派的基本单位,它可以同属于一个进程的其他的线程共享进程所拥有的全部资源。
区别:
设置两个节点,第一个节点的步长为1。第二个步长为2
while,让两个节点不停的走。
只要两个节点相等了就保证了是有环的。
Node p1 = head;//先都指向头结点
Node p2 = head;
int times = 0;//相遇0次
while(p2!=null&&p2.next!=null)//如果快游标到结尾就退出循环
{
p1=p1.next;//一次走一步
p2=p2.next.next;//一次走两步
if(p1==p2)
{
return isCycle;//相遇即证明有环
}
}
return noCycle;
ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize, long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
可以使用ThreadPoolExecutor来创建,也可以使用Executors来创建
Executors来创建的方式
http://www.blogjava.net/kawaii/archive/2007/02/06/98395.html
public class BinarySearch {
// 二分查找,找到并返回下标
public static int binarySearch(int[] arr, int n) {
//定义一些变量
int left = 0;
int right = arr.length - 1;
int mid = 0;
while (left <= right) {
// 求中间值
mid = (left + right) / 2;
if (arr[mid] == n) {
return mid;
} else if (arr[mid] <= n) {
left = mid + 1;
} else {
right = mid - 1;
}
}
// 表示未找到
return -1;
}
}
一面
使用的是mybatis plus自带的分页插件pageHelper 实现的是物理分页。
对于搜索部分的分页,使用的是Elasticsearch的Scroll(游标)滚动API。
当Elasticsearch响应请求时,它必须确定docs的顺序,排列响应结果。如果请求的页数较少(假设每页20个docs), Elasticsearch不会有什么问题,但是如果页数较大时,比如请求第20页,Elasticsearch不得不取出第1页到第20页的所有docs,再去除第1页到第19页的docs,得到第20页的docs。
解决的方法就是使用Scroll。因为Elasticsearch要做一些操作(确定之前页数的docs)为每一次请求,所以,我们可以让Elasticsearch储存这些信息为之后的查询请求。这样做的缺点是,我们不能永远的储存这些信息,因为存储资源是有限的。所以Elasticsearch中可以设定我们需要存储这些信息的时长。
第一次请求筛选条件之后,直接返回一个scroll_id 。下次请求的时候带上这个id。会直接返回对应的筛选条件的下一页的数据。
使用explain
explain select id from user
项目中大部分用到的数据库是mysql数据库。 (索引创建)
mysql中的索引:
普通索引: 没有任何限制,允许在定义索引的列中插入重复的值和空值
create index index_name on table_name (column(length));
唯一索引: 索引列的值必须是唯一的,允许有空值,如果是组合索引,列值的组合必须唯一
create unique index index_name on table_name (column(length));
前缀索引: 截取字段的指定长度前半部分来作为索引。
组合索引: 多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。
alter table table_name add index index_name(column,column,column..);
主键索引:主键索引是一种特殊的唯一索引,一个表只能有一个主键,不允许有空值,一般是在创建表的时候指定主键,主键默认就是主键索引
聚集索引: 表数据按照索引的顺序来存储的,也就是说索引项的顺序与表中记录的物理顺序一致。叶子节点即存储了真实的数据行,不再有另外单独的数据页。一张表只能创建一个聚集索引,因为真实的物理顺序只有一种
非聚集索引: 表数据存储顺序与索引顺序无关。对于非聚集索引,叶结点包含索引字段值及指向数据页数据行的逻辑指针。,其行数量与表数据量一致。
总结
聚簇索引和非聚簇索引的区别是:
聚簇索引(innoDB)的叶子结点就是数据结点
非聚簇索引(myisam)的叶子结点任然是索引文件,知识这个索引文件包含指向对应数据块的指针
对于非聚簇索引来说,每次通过索引检索到所需行号后,还需要通过叶子上的磁盘地址去磁盘内取数据(回行)消耗时间。为了优化这部分回行取数据时间,InnoDB引擎采用了聚簇索引。
聚簇索引,即将数据存入索引叶子页面上。对于Innodb引擎来说,叶子页面不再存改行对应的地址,而是直接存储数据。
这样便避免了回行操作所带来的时间消耗。使得InnoDB在某些查询上比MyISAM还要快!
索引可以将无序内容转换为有序的一个集合,就如同新华字典,如果没有目录,那么查询一个汉字就需要很长时间了。
而且索引的数据结构是二叉树。B+树,相当于二分查找,加快查询速率。
B+树需要平衡,如果知识二叉树可能存在链式的结构,这样的话查询的速度也就不存在了。
正因为B+树是一棵平衡树,如果我们要对这棵树增删改的话,那肯定会破坏它的原有结构
要维持平衡树,就必须做额外的工作。导致额外的开销。
第一个
select name from T group by match DESC
第二个
select sum(id) from account group by id
如果from子句两个表用“,”隔开,解释下该子句的意思?
DESC:降序 ASC :升序
如果from子句,是两个表用逗号隔开,那就是对两个表做一个笛卡尔集。
线程间同步怎么实现?
static修饰的变量 全局共享的只有一份 所以阻塞
普通成员变量,每个线程特有的,加了synchronized只对当前线程加锁,其他线程操作自己的成员方法。
使用过线程池。
创建线程:
Executors :
ThreadPoolExecutor(int corePoolSize, //核心线程池大小
int maximumPoolSize,//最大线程池大小
long keepAliveTime,//超时没有人调用就会释放
TimeUnit unit,//超时单位
BlockingQueue workQueue,//阻塞队列
ThreadFactory threadFactory,//线程工厂:创建线程,一般不用动
RejectedExecutionHandler handler) {//拒绝策略
拒绝策略
设置过期时间: expire key time(以秒为单位)
setex(String key,int seconds,String value) 字符串独有的方式
ttl key : 查看剩余过期时间
3种过期策略
Redis的持久化
RDB
AOF
Elasticsearch
…
反问
二面
使用的是mybatis plus自带的分页插件pageHelper 实现的是物理分页。
对于搜索部分的分页,使用的是Elasticsearch的Scroll(游标)滚动API。
使用的是mysql。
使用explain
explain select id from user
索引可以将无序内容转换为有序的一个集合,就如同新华字典,如果没有目录,那么查询一个汉字就需要很长时间了。
而且索引的数据结构是二叉树。B+树,相当于二分查找,加快查询速率。
B+树需要平衡,如果知识二叉树可能存在链式的结构,这样的话查询的速度也就不存在了。
正因为B+树是一棵平衡树,如果我们要对这棵树增删改的话,那肯定会破坏它的原有结构
要维持平衡树,就必须做额外的工作。导致额外的开销。
大概了解吧。
讲一下程序计数器的作用
讲一下堆的划分 新生代 老年代 如何转换
讲一下可达性算法
String str = new String(“Hello World”); 可能是一个 也可能是两个
这个语句会创建两个或者一个对象,如果堆中未存在"Hello World"这个字符串的化,执行以上语句会在堆中产生一个"Hello World"的字符串,并且在线程的栈中新建一个str的指针,指向堆中的"Hello World" 以上情况会有两个对象
如果堆中已存在这个字符串的化,则不会新建一个"Hello World"对象,直接在栈中生成一个str指针指向对应的"Hello World"对象,因此新建了一个对象。
悲观锁:synchronized?
乐观锁: juc包下的类
Executors :
开启核心线程以外的线程: 当核心线程满了之后,如果还有额外的线程进入,会先往阻塞队列里面插入,当阻塞队列满了的时候,就会开启核心线程以外的线程。
当执行的线程数大于 最大线程数 + 阻塞队列的时候会用到拒绝策略。
AOP 代理模式
IOC 工厂模式
https://blog.csdn.net/qq_37126357/article/details/100689342
这个有谁懂吗?解释下。具体我不知道怎么讲。
一面
上面有讲到!!
进程
进程是程序的一次执行过程,是一个动态的概念,是程序在执行过程中分配和管理资源的基本单位,每一个进程都有一个自己的地址空间,至少有五种状态:初始态,执行态,等待状态,就绪状态,终止状态。
线程
线程是CPU调度和分派的基本单位,它可以同属于一个进程的其他的线程共享进程所拥有的全部资源。
区别:
基本介绍
Servlet:
Servlet 是一种服务器端的Java应用程序,具有独立于平台和协议的特性,可以生成动态的Web页面。它担当客户请求(Web浏览器或其他HTTP客户程序)与服务器响应(HTTP服务器上的数据库或应用程序)的中间层。 Servlet是位于Web服务器内部的服务器端的Java应用程序,与传统的从命令行启动的Java应用程序不同,Servlet由Web服务器进行加载,该Web服务器必须包含支持Servlet的Java虚拟机。
Jsp:
JSP 全名为Java Server Pages,中文名叫java服务器页面,其根本是一个简化的Servlet设计。JSP技术使用Java编程语言编写类XML的tags和scriptlets,来封装产生动态网页的处理逻辑。网页还能通过tags和scriptlets访问存在于服务端的资源的应用逻辑。JSP将网页逻辑与网页设计的显示分离,支持可重用的基于组件的设计,使基于Web的应用程序的开发变得迅速和容易。 JSP(JavaServer Pages)是一种动态页面技术,它的主要目的是将表示逻辑从Servlet中分离出来。
相同点
jsp经编译后就变成了servlet,jsp本质就是servlet,jvm只能识别java的类,不能识别jsp代码,web容器将jsp的代码编译成jvm能够识别的java类。
不同点
JSP侧重视图,Sevlet主要用于控制逻辑。
Servlet中没有内置对象 。
JSP中的内置对象都是必须通过HttpServletRequest对象,HttpServletResponse对象以及HttpServlet对象得到。
使用ThreadLocal维护变量时,其为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立的改变自己的副本,而不会影响其他线程对应的副本。
ThreadLocal内部实现机制:
https://blog.csdn.net/weixin_43691723/article/details/105810442
https://www.cnblogs.com/coderD/p/13828832.html
是否线程安全
效率
对于Null Key和Null Value的支持
初始容量和每次扩容的大小不同
HashMap创建的时候如果不指定容量大小,初始容量大小为16,之后每次扩充,容量变为原来的2倍;
HashTable创建的时候如果不指定容量大小,初始容量大小为11,之后每次扩充,容量会变为2n + 1;
HashMap创建的时候给定初始容量大小,HashMap 会将其扩充为2的幂次方大小(HashMap 中的tableSizeFor()
方法保证,下面给出了源代码)。也就是说 HashMap 总是使用2的幂作为哈希表的大小,后面会介绍到为什么是2的幂次方。
HashMap 中带有初始容量的构造函数:
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
/**
* Returns a power of two size for the given target capacity.
*/
static final int tableSizeFor(int cap) {
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;
}
底层数据结构
https://blog.csdn.net/ycg33/article/details/100069585
序列化是将对象转换为一系列字节,这样可以轻松的将对象保存到持久存储或跨通信链接流。然后,字节流可以反序列化-转换为原始对象副本。(可能是二进制也可能不是二进制,取决于实现。)
Elasticsearch是面向文档的,这意味着它可以存储整个对象或文档。然而它不仅仅是存储,还会索引(index) 每个文档的内容使之可以被搜索。在es中,你可以对文档进行索引,搜索,排序,过滤。
String[] arrags = {
"hello", "world", "java", "zhiyin"};
//数组转换为集合
List<String> list = new ArrayList<>(Arrays.asList(arrags));
String[] array = list.toArray(new String[list.size()]);
finally 关键字是对 Java 异常处理模型的最佳补充。finally 结构使代码总会执行,而不管有无异常发生。
使用 finally 可以维护对象的内部状态,并可以清理非内存资源。 如果没有 finally,您的代码就会很费解。
finally通常用于try catch语句块中,完整的异常处理语句一般都有finally语句。无论有无异常发生,finally语句一定会被执行。
使用过线程池。
创建线程:
Executors :
ThreadPoolExecutor(int corePoolSize, //核心线程池大小
int maximumPoolSize,//最大线程池大小
long keepAliveTime,//超时没有人调用就会释放
TimeUnit unit,//超时单位
BlockingQueue workQueue,//阻塞队列
ThreadFactory threadFactory,//线程工厂:创建线程,一般不用动
RejectedExecutionHandler handler) {//拒绝策略
Start方法可启动线程,而run方法只是thread的一个普通方法,调用run方法不能实现多线程。
start方法
start方法用来启动线程,实现了多线程运行,这时无需等待run方法体代码体执行,而是直接执行下面的代码。
Run() 方法
run() 方法只是Thread类的一个普通方法,如果直接调用Run方法,程序中依然只有主线程这一个线程。
其程序执行路径还是只有一条,还是要等待run方法体执行完毕后才可继续执行下面的代码。
这样就没有达到多线程的目的。
通过start能够异步的调用run() 方法,但直接调用run()方法是同步的
对很重要的事情 紧张。比如这次面试,我都很紧张,但是会提前准备很多东西来化解紧张,
hr就问到我这个问题了,我是这样说的。
二面
token的原理和作用
token是用于验证身份的,在web系统中验证前端是否能够访问后端系统
token在服务端产生,如果前端使用用户名/密码向服务端请求认证,服务端认证成功,那么在服务端会返回 Token 给前端。前端可以在每次请求的时候带上 token 证明自己的合法地位。token是服务端生成的一串字符串,以作前端进行请求的一个令牌,当第一次登录后,服务器生成一个token并返回给前端,之后前端只需带上这个token前来请求数据即可,无需再次带上用户名和密码。
token相比其他验证方式,主要是无需每次都去验证用户名密码,同时由于token保存在前端,服务端无需保存每个用户的登录状态,只需要验证访问时带着的token是不是自己签发的,可以减轻服务器的压力,减少频繁的查询数据库,使服务器更加健壮。
token可以解决的问题
Token和Cookie的区别
cookie 举例:服务员看你的身份证,给你一个编号,以后,进行任何操作,都出示编号后服务员去看查你是谁。
token 举例:直接给服务员看自己身份证
总结:
Token 完全由应用管理,所以它可以避开同源策略
Token 可以避免 CSRF 攻击
Token 可以是无状态的,可以在多个服务间共享
如果你的用户数据可能需要和第三方共享,或者允许第三方调用 API 接口,用 Token,如果之上自己的那就无所谓了。
redis分布式储存。
@Service:
注解在类上,表示这是一个业务层bean
@Controller:
注解在类上,表示这是一个控制层bean
@Repository:
注解在类上,表示这是一个数据访问层bean
@Component:
注解在类上,表示通用bean ,value不写默认就是类名首字母小写
@Autowired:
按类型注入.默认属性required= true;当不能确定 Spring 容器中一定拥有某个类的Bean 时, 可以在需要自动注入该类 Bean 的地方可以使用@Autowired(required = false), 这等于告诉Spring:在找不到匹配Bean时也不抛出BeanCreationException 异常。
@Configuration:
注解在类上,表示这是一个IOC容器,相当于spring的配置文件,java配置的方式。 IOC容器的配置类一般与 @Bean 注解配合使用,用@Configuration 注解类等价与 XML 中配置 beans,用@Bean 注解方法等价于 XML 中配置 bean。@Bean: 注解在方法上,声明当前方法返回一个Bean
@Value:
注解在变量上,从配置文件中读取
RestController:
@RestController 是一个结合了 @ResponseBody 和 @Controller 的注解(像:resetful接口调用时,返回的是json等就使用)
@PathVariable和@RequestParam:
都是将request里的参数的值绑定到contorl里的方法参数里的,区别在于,URL写法不同。
使用@RequestParam时,URL是这样的:http://host:port/path?参数名=参数值
使用@PathVariable时,URL是这样的:http://host:port/path/参数值
当请求参数username不存在时会有异常发生,可以通过设置属性required=false解决,例如: @RequestParam(value=“username”,required=false)
不写的时候也可以获取到参数值,但是必须名称对应。参数可以省略不写
@suppresswarnings:抑制警告
@Transactional:事务注解
@SpringBootApplication:
约定优于配置 @SpringBootApplication=@ComponentScan+@Configuration+@EnableAutoConfiguration。 放在主程序入口类上, 主程序入口类(启动类) 放在root 包下,这样程序启动时所有的相关配置,类都能扫描,查找到
keys * -----> 获取所有的键
第一步: 评估bug的影响范围
第二步:解决线上的问题
针对线上问题最重要的是要解决,在评估完影响范围后,就需要制定对应的措施来解决问 题并恢复系统的正常使用。
第三步:回溯线上问题
当线上问题解决后,我们还需要对问题进行总结回溯,避免同样的问题再次发生。线上
问题回溯主要从如下几个方面进行:
概念:
哨兵模式
如果主机此时回来了,只能归并到新的主机下,当作从机,这就是哨兵模式的规则!
优点:
缺点:
SpringBoot + MyBatis Plus + MySQL + js + bootStarp.
对于资源的具体操作类型,由HTTP动词表示。
常用的HTTP动词有下面五个(括号里是对应的SQL命令)。
下面是一些例子
指定过滤信息
如果记录数量很多,服务器不可能都将它们返回给用户。API应该提供参数,过滤返回结果。
下面是一些常见的参数。
状态码
服务器向用户返回的状态码和提示信息,常见的有以下一些(方括号中是该状态码对应的HTTP动词)。
http://www.ruanyifeng.com/blog/2014/05/restful_api.html
PATCH: 局部更新
PUT: 全量更新
假设我们有一个UserInfo
,里面有userId
, userName
, userGender
等10个字段。可你的编辑功能因为需求,在某个特别的页面里只能修改userName
,这时候的更新怎么做?
人们通常(为徒省事)把一个包含了修改后userName
的完整userInfo
对象传给后端,做完整更新。但仔细想想,这种做法感觉有点二,而且真心浪费带宽(纯技术上讲,你不关心带宽那是你土豪)。
于是patch
诞生,只传一个userName
到指定资源去,表示该请求是一个局部更新,后端仅更新接收到的字段。
而put
虽然也是更新资源,但要求前端提供的一定是一个完整的资源对象,理论上说,如果你用了put
,但却没有提供完整的UserInfo
,那么缺了的那些字段应该被清空
前后端分离,了解下这里不细讲
Elasticsearch分别为每个字段都建立了一个倒排索引。比如,在上面“张三”、“北京市”、22 这些都是Term,而[1,3]就是Posting List。Posting list就是一个数组,存储了所有符合某个Term的文档ID。
当然是建索引了,为Terms建立索引,最好的就是B-Tree索引
使用过线程池。
创建线程:
Executors :
ThreadPoolExecutor(int corePoolSize, //核心线程池大小
int maximumPoolSize,//最大线程池大小
long keepAliveTime,//超时没有人调用就会释放
TimeUnit unit,//超时单位
BlockingQueue workQueue,//阻塞队列
ThreadFactory threadFactory,//线程工厂:创建线程,一般不用动
RejectedExecutionHandler handler) {//拒绝策略
ThreadPoolExecutor(int corePoolSize, //核心线程池大小
int maximumPoolSize,//最大线程池大小
long keepAliveTime,//超时没有人调用就会释放
TimeUnit unit,//超时单位
BlockingQueue workQueue,//阻塞队列
ThreadFactory threadFactory,//线程工厂:创建线程,一般不用动
RejectedExecutionHandler handler) {//拒绝策略
队列
所有 BlockingQueue
都可用于传输和保持提交的任务。可以使用此队列与池大小进行交互:
三种队列
将i改成AutomicInteger类型。用到了CAS
CAS会造成ABA问题 ----> 携带版本号
锁有:
锁有:乐观锁,悲观锁,自旋锁,读写锁。
synchronized有三种状态:偏向锁、轻量级锁、重量级锁
锁有四种级别,按照量级从轻到重分为:无锁、偏向锁、轻量级锁、重量级锁。
每个对象一开始都是无锁的,随着线程间争夺锁,越激烈,锁的级别越高,并且锁只能升级不能降级。
一面
HashMap:
HashSet底层使用的是HashMap的key
concurrentHashMap: 分段锁机制
CountDownLatch
InnoDB聚集索引,数据文件和主键索引在同个文件。
类似二分搜索的形式 ,大大缩短了查询的时间。
数据库索引是B+数。
二叉树可能形成链形结构,查询复杂度还是O(n)。 不能自平衡
这个题面试官没让我说这么详细,大致说一下流程。
http是无状态的统个session+cookie去辅助
协议对于事务处理没有记忆能力【事物处理】【记忆能力】
对同一个url请求没有上下文关系【上下文关系】
每次的请求都是独立的,它的执行情况和结果与前面的请求和之后的请求是无直接关系的,它不会受前面的请求应答情况直接影响,也不会直接影响后面的请求应答情况【无直接联系】【受直接影响】
服务器中没有保存客户端的状态,客户端必须每次带上自己的状态去请求服务器【状态】
Web应用=http协议+session、cookies等状态机制+其他辅助的机制。
private static void quickSort(int[] arr, int leftIndex, int rightIndex) {
if (leftIndex >= rightIndex) {
return;
}
int left = leftIndex;
int right = rightIndex;
//待排序的第一个元素作为基准值
int key = arr[left];
//从左右两边交替扫描,直到left = right
while (left < right) {
while (right > left && arr[right] >= key) {
//从右往左扫描,找到第一个比基准值小的元素
right--;
}
//找到这种元素将arr[right]放入arr[left]中
arr[left] = arr[right];
while (left < right && arr[left] <= key) {
//从左往右扫描,找到第一个比基准值大的元素
left++;
}
//找到这种元素将arr[left]放入arr[right]中
arr[right] = arr[left];
}
//基准值归位
arr[left] = key;
//对基准值左边的元素进行递归排序
quickSort(arr, leftIndex, left - 1);
//对基准值右边的元素进行递归排序。
quickSort(arr, right + 1, rightIndex);
}
二面
大多数情况下可以直接定位到值,具有很快的访问速度。 非线程安全。(ConcurrentHashMap)
初始化的时候默认的容量是16 (必须是2的指数此幂)
Java7 —> 数组 + 链表
Java8 ----> 数组 + 链表 + 红黑树 (性能提升10%左右)
Student stu = new Student(“zhangsan”);
stu.add();
stu=null;
通过分析,如上url表示为:请求位于服务器localhost:9999上的SpringMVC站点的input-product控制器
直接CRUD那一套讲就行了吧
聚集索引 。B+数
InnoDB特性
Mybatis中有一级缓存和二级缓存,默认情况下一级缓存是开启的,而且不能关闭。
一级缓存是指SqlSession级别的缓存 即:同一个SqlSession中进行相同的SQL语句查询时,第二次后查询不会从数据库查询, 而是直接从缓存中获取,一级缓存最多缓存1024条SQL。
二级缓存是指可以跨SqlSession缓存 即: 是mapper级别的缓存,对于mapper级别的缓存不同的sqlSession是可以共享的。
它是一个服务于spring框架的框架,能够简化配置文件,快速构建web应用,内置tomcat,无需打包部署,直接运行。
默认提供 application.properties/yml 文件
默认通过 spring.profiles.active 属性来决定运行环境时读取的配置文件
SpringBootApplication 本质上是由 3 个注解组成,分别是
@Configuration
@EnableAutoConfiguration
@ComponentScan
@Configuration:
在启动类里面标注了@Configuration,意味着它其实也是一个 IoC
容器的配置类@ComponentScan:
ComponentScan 默认会扫描当前 package 下的的所有加
了@Component 、@Repository、@Service、@Controller的类到 IoC 容器中;
过滤器
依赖于servlet容器。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的是用来做一些过滤操作,获取我们想要获取的数据,比如:在过滤器中修改字符编码;在过滤器中修改HttpServletRequest的一些参数,包括:过滤低俗文字、危险字符等。
拦截器
依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架。在实现上基于java的反射机制,属于面向切面编程(AOP)的一种运用。由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。但是缺点是能对controller请求进行拦截,可以拦截静态资源,但是拦截不了jsp页面
拦截器
**思路:**即将请求的url地址进行解析,除了登录外的请求都要进行拦截或者过滤,这些请求在通过登录的判断,来决定最后的结果
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.invoicing.interceptor.LoginInterceptor">bean>
mvc:interceptor>
mvc:interceptors>
因为拦截器无法拦截jsp页面,所以,直接登录jsp页面无法拦截。
过滤器
@WebServlet(urlPatterns = {
"/*"})
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest httpServletRequest=(HttpServletRequest) servletRequest;
System.out.println("123");
//放行URL
if (httpServletRequest.getRequestURI().indexOf("/login") >= 0) {
filterChain.doFilter(servletRequest,servletResponse);
}
Object user = httpServletRequest.getSession().getAttribute("user");
if (user==null){
httpServletRequest.getRequestDispatcher("/login.jsp").forward(servletRequest,servletResponse);
}else {
//放行请求
filterChain.doFilter(servletRequest,servletResponse);
}
}
@Override
public void destroy() {
}
}
三次握手
第一次握手:Client什么都不能确认,Server确认了对方发送正常,自己接受正常。
第二次握手:Client确认了:自己发送,接受正常;对方发送,接受正常; Server确认了:自己接收正常;对方发送正常
第三次握手:Client全部确认 Server全部确认
四次挥手
确保客户端和服务端 都想结束通讯了。
第一范式的目标是确保每列的原子性,如果每列都是不可分割的最小数据单元(也称为最小的原子单元),则满足第一范式1NF 属性不可分割
每个表只描述一件事情,首先满足第一范式,并且表中非主键列不存在对主键的部分依赖。
第二范式要求每个表只描述一件事情
不存在对非主键列的传递依赖
满足第二范式,并且表中的列不存在对非主键列的传递依赖。除了主键订单编号外,顾客姓名依赖于非主键顾客标号。
Mname 是依赖于sdept的 不直接依赖于Sno主键
可分解为以下
数组+链表
哈希冲突使用链表展示
可达性分析算法
通过GC ROOT
的对象作为搜索起始点,通过引用向下搜索,所走过的路径称为引用链。通过对象是否有到达引用链的路径来判断对象是否可被回收(可作为GC ROOT
的对象:虚拟机栈中引用的对象,方法区中类静态属性引用的对象,方法区中常量引用的对象,本地方法栈中JNI
引用的对象)
通过可达性算法,成功解决了引用计数所无法解决的循环依赖问题,只要你无法与GC Root
建立直接或间接的连接,系统就会判定你为可回收对象。那这样就引申出了另一个问题,哪些属于GC Root。
UDP:在传输数据之前不需要先建立连接,远地的主机收到UDP报文后也不需要任何确认。虽然UDP不提供可靠的交付,但是却非常的高效(QQ语言,QQ视频,直播等等)
TCP:在传送数据时需要先建立连接,结束后要释放连接。TCP不提供广播,多播等服务。增加了许多开销。用于文件传输,发送和接收邮件,远程登录等场景
不是稳定的算法
在排序之前,有两个数相等.但是在排序结束之后,它们两个有可能改变顺序.
在一个待排序队列中,A和B相等,且A排在B的前面,而排序之后,A排在了B的后面.这个时候,我们说这种算法是不稳定的.
时间复杂度一样的有:
设置过期时间: expire key time(以秒为单位)
setex(String key,int seconds,String value) 字符串独有的方式
ttl key : 查看剩余过期时间
3种过期策略
Redis的持久化
RDB
AOF
Elasticsearch
倒排索引
功能
双亲委派
每一个类都有一个对应它的类加载器。系统中的 ClassLoder 在协同工作的时候会默认使用 双亲委派模型 。即在类加载的时候,系统会首先判断当前类是否被加 载过。已经被加载的类会直接返回,否则才会尝试加载。加载的时候,首先会把该请求委派该父类加载器的 loadClass() 处理,因此所有的请求最终都应该 传送到顶层的启动类加载器 BootstrapClassLoader 中。当父类加载器无法处理时,才由自己来处理。当父类加载器为null时,会使用启动类加载器 BootstrapClassLoader 作为父类加载器。
BootstrapClassLoader “jre/lib”
extensionClassLoader “jre/lib/ext”
AppClassLoader 主要负责加载应用程序的主函数类
双亲委派模型带来的好处
双亲委托模型保证了Javac程序的稳定运行,可以避免类的重复加载(JVM区分不同的类的方式不仅仅根据类名,相同的类文件被不同的类加载器加载产生的两个 不同的类),也保证了Java的核心API不被篡改。如果不使用双亲委托模型,而是每个类加载自己的话会出现一些问题:编写一个java.lang.object类的话,那么 程序运行的时候,系统就会出现多个不同object类
项目中大部分用到的数据库是mysql数据库。
mysql中的索引:
普通索引: 没有任何限制,允许在定义索引的列中插入重复的值和空值
create index index_name on table_name (column(length));
唯一索引: 索引列的值必须是唯一的,允许有空值,如果是组合索引,列值的组合必须唯一
create unique index index_name on table_name (column(length));
前缀索引: 截取字段的指定长度前半部分来作为索引。
组合索引: 多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。
alter table table_name add index index_name(column,column,column..);
主键索引:主键索引是一种特殊的唯一索引,一个表只能有一个主键,不允许有空值,一般是在创建表的时候指定主键,主键默认就是主键索引
聚集索引: 表数据按照索引的顺序来存储的,也就是说索引项的顺序与表中记录的物理顺序一致。叶子节点即存储了真实的数据行,不再有另外单独的数据页。一张表只能创建一个聚集索引,因为真实的物理顺序只有一种
非聚集索引: 表数据存储顺序与索引顺序无关。对于非聚集索引,叶结点包含索引字段值及指向数据页数据行的逻辑指针。,其行数量与表数据量一致。
总结
聚簇索引和非聚簇索引的区别是:
聚簇索引(innoDB)的叶子结点就是数据结点
非聚簇索引(myisam)的叶子结点任然是索引文件,知识这个索引文件包含指向对应数据块的指针
对于非聚簇索引来说,每次通过索引检索到所需行号后,还需要通过叶子上的磁盘地址去磁盘内取数据(回行)消耗时间。为了优化这部分回行取数据时间,InnoDB引擎采用了聚簇索引。
聚簇索引,即将数据存入索引叶子页面上。对于Innodb引擎来说,叶子页面不再存改行对应的地址,而是直接存储数据。
这样便避免了回行操作所带来的时间消耗。使得InnoDB在某些查询上比MyISAM还要快!
@Controller:作用于表现层(spring-mvc的注解),它注解的类进行前端请求的处理,转发,重定向。包括调用Service层的方法。
@Service:作用于业务逻辑层(service层)
@Repository:作用于持久层(dao层),它注解的类作为DAO对象(数据访问对象,Data Access Objects),这些类可以直接对数据库进行操作
@Component:是一个通用的Spring容器管理的单例bean组件,最普通的组件,可以被注入到spring容器进行管理。@Component是通用注解,其他三个注解是这个注解的拓展,并且具有了特定的功能。
@RequestMapping:是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。该注解有六个属性:
@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@Mapper和**@MapperScan**的区别
@Mapper:作用是为了把mapper这个DAO交给Spring容器管理,目前的我的理解是将该注解放在dao层的接口上,对应了它的.xml的映射文件。一个接口,就要在接口上方加一个mapper注解。
@MapperScan:如上面所讲,当存在多个mapper接口时,在每个接口上方都要加一个@Mapper注解,这样显得十分的繁琐,为此我们可以直接在SpringBoot的启动类的上方加一个@MapperScan注解,来解决这个问题。此注解可以添加对包的的扫描,我们可以直接将mapper接口所在的包写在此注解里,这样就会自动扫描所有的mapper接口。
Executors创建
ThreadPoolExecutor
redis为什么这么快? redis快
首先,采用了多路复用io阻塞机制
**然后,数据结构简单,操作节省时间 ** 跳跃表
最后,运行在内存中,自然速度快
这个记得看
UDP:在传输数据之前不需要先建立连接,远地的主机收到UDP报文后也不需要任何确认。虽然UDP不提供可靠的交付,但是却非常的高效(QQ语言,QQ视频,直播等等)
TCP:在传送数据时需要先建立连接,结束后要释放连接。TCP不提供广播,多播等服务。增加了许多开销。用于文件传输,发送和接收邮件,远程登录等场景
第一次握手:Client什么都不能确认,Server确认了对方发送正常,自己接受正常。
第二次握手:Client确认了:自己发送,接受正常;对方发送,接受正常; Server确认了:自己接收正常;对方发送正常
第三次握手:Client全部确认 Server全部确认
四次挥手
确保客户端和服务端 都想结束通讯了。
一面
ArrayList基于动态数组实现的。LinkedList基于双向链表。可以归结为链表和数组的区别
基于红黑树实现的。
是有序的
性能比其他两种Map差
使用 https://www.jianshu.com/p/e11fe1760a3d
HashMap
数组+链表+B+树
默认大小16 扩容因子0.75
ConcurrentHashMap segement锁机制。
ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize, long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
可以使用ThreadPoolExecutor来创建,也可以使用Executors来创建
Executors来创建的方式
jvm内存划分
新生代内存结构
复制算法好处
内存划分
复制算法
优点:
缺点:
简单的理解就是多个事务方法相互调用时,事务如何在这些方法间传播。
举个栗子,方法A是一个事务的方法,方法A执行过程中调用了方法B,那么方法B有无事务以及方法B对事务的要求不同都会对方法A的事务具体执行造成影响,同时方法A的事务对方法B的事务执行也有影响,这种影响具体是什么就由两个方法所定义的事务传播类型所决定。
事务类型 | 解释 |
---|---|
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED) | 支持当前事务,如果没有事务会创建一个新的事务 |
SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS) | 支持当前事务,如果没有事务的话以非事务方式执行 |
MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY) | 支持当前事务,如果没有事务抛出异常 |
REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW) | 创建一个新的事务并挂起当前事务 |
NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED) | 以非事务方式执行,如果当前存在事务则将当前事务挂起 |
NEVER(TransactionDefinition.PROPAGATION_NEVER) | 以非事务方式进行,如果存在事务则抛出异常 |
NESTED(TransactionDefinition.PROPAGATION_NESTED) | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。 |
主从复制
在 redis2.8 版本之前主从复制过程如下图:
复制过程说明:
注意:此时如果生成 RDB 文件过程中存在写数据操作会导致 RDB 文件和当前主 redis 数据不一致,所以此时 master 主进程会开始收集写命令并缓存起来。
注意:后续 master 收到的写命令都会通过开始建立的连接发送给 slave。
当 master 和 slave 的连接断开时 slave 可以自动重新建立连接。如果 master 同时收到多个 slave 发来的同步连接命令,只会启动一个进程来写数据库镜像,然后发送给所有 slave。
完整复制的问题:
在 redis2.8 之前从 redis 每次同步都会从主 redis 中复制全部的数据,如果从 redis 是新创建的从主 redis 中复制全部的数据这是没有问题的,但是,如果当从 redis 停止运行,再启动时可能只有少部分数据和主 redis 不同步,此时启动 redis 仍然会从主 redis 复制全部数据,这样的性能肯定没有只复制那一小部分不同步的数据高。
Redis主机挂了
方案:切换主库的身份
# 连接从库
[root@localhost redis-4.0.12]# redis-cli -p 6380
# 取消从库身份
127.0.0.1:6380> slaveof no one
# 连接从库
[root@localhost redis-4.0.12]# redis-cli -p 6381
# 重新设置从库
127.0.0.1:6381> slaveof 127.0.0.1 6380
使用explain
explain select id from user
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8D2xbdUE-1618387946730)(F:\aboutIT\learningFromduoyi\images\image-20210314221608646.png)]
二面
不可以,因为String类有final修饰符,而final修饰的类是不能被继承的,实现细节不允许改变。
扩展?
StringBuffer
StringBuilder
线程安全
缓冲区
StringBuffer代码
private transient char[] toStringCache;
@Override
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
StringBuild代码
@Override
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
可以看出,StringBuffer 每次获取 toString 都会直接使用缓存区的 toStringCache 值来构造一个字符串。
而 StringBuilder 则每次都需要复制一次字符数组,再构造一个字符串。
所以,缓存冲这也是对 StringBuffer 的一个优化吧,不过 StringBuffer 的这个toString 方法仍然是同步的。
性能
既然 StringBuffer 是线程安全的,它的所有公开方法都是同步的,StringBuilder 是没有对方法加锁同步的,所以毫无疑问,StringBuilder 的性能要远大于 StringBuffer。
sycnronized的作用
sycnronized的使用
https://blog.csdn.net/weixin_38481963/article/details/88384493
大多数情况下可以直接定位到值,具有很快的访问速度。 非线程安全。(ConcurrentHashMap)
初始化的时候默认的容量是16 (必须是2的指数此幂)
Java7 —> 数组 + 链表
Java8 ----> 数组 + 链表 + 红黑树 (性能提升10%左右)
ArrayList基于动态数组实现的。LinkedList基于双向链表。可以归结为链表和数组的区别
这个被问到了不太懂,我只是大概讲了下。有点复杂,看源码
redis数据类型
事务配置
@Transactional
注解的方式。隔离级别
delete from Table where id = 1 ?
create index (index_name) on table_name(column_name)
select 性别,count(*) 人数 from student group by 性别;
单点登录
至此,跨域单点登录就完成了。以后我们再访问app系统时,app就是登录的。接下来,我们再看看访问app2系统时的流程。
https://www.jianshu.com/p/75edcc05acfd
Spring
包含一些很好用的功能,如依赖注入和开箱即用的模块:SpringJDBC、SpringMVC、SpringSecurity、SpringAOP、SpringORM、SpringTest 。
SpringBoot
是Spring的扩展,消除了设置Spring应用程序所需的XML配置,为更快,更高效的开发生态系统铺平道路,以下是一些特征:
从配置分析
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webartifactId>
<version>5.1.0.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.1.0.RELEASEversion>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId> spring-boot-starter-webartifactId>
<version>2.0.6.RELEASEversion>
dependency>
Spring对于测试库: SpringTest, JUnit。需要将这些作为依赖项导入。但是在SpringBoot中,我们只需要添加Spring-boot-starter-test依赖来自动包含这些库
SpringBoot为不同的Spring模块提供了许多依赖项,一些最常用的是:
spring-boot-starter-data-jpa spring-boot-starter-security spring-boot-starter-test spring-boot-starter-web spring-boot-starter-thymeleaf。
MVC配置
如果使用servlet创建之后,还是需要使用 @EnableWebMvc注释添加到@Configuration类,并定义视图解析器从控制器返回。
这意味着SpringBoot将查看应用程序中存在的依赖项,属性和bean,并根据这些依赖项,对属性和bean进行配置。当然,如果我们想要添加自己的自定义配置,那么SpringBoot自动配置将会退回。
配置模板引擎
现在我们来看下如何在Spring和Spring Boot中配置Thymeleaf模板引擎
应用程序启动引导配置
配置 web.xml方法启动的步骤
使用 Servlet3+方法的 Spring启动步骤 (容器搜索实现)
SpringBoot引导配置
Spring Boot应用程序的入口点是使用@SpringBootApplication注释的类
默认情况下, SpringBoot使用嵌入式容器来运行应用程序。在这种情况下, SpringBoot使用 publicstaticvoidmain入口点来启动嵌入式 Web服务器。
此外,它还负责将 Servlet, Filter和 ServletContextInitializerbean从应用程序上下文绑定到嵌入式 servlet容器。SpringBoot的另一个特性是它会自动扫描同一个包中的所有类或 Main类的子包中的组件
打包和部署
这两个框架都支持 Maven和 Gradle等通用包管理技术。但是在部署方面,这些框架差异很大。例如,Spring Boot Maven插件在 Maven中提供 SpringBoot支持。它还允许打包可执行 jar或 war包并 就地运行应用程序。
在部署环境中 SpringBoot 对比 Spring的一些优点包括:
数据库乐观锁实现方式:
update table set columnA = 1,version=version+1 where id=#{id} and version = #{oldVersion}
update product set rest = rest– #{deduct} where name = ‘abc’ and rest >= #{deduct}
缺点
需要对表的设计增加额外的字段,增加了数据库的冗余,另外,当应用并发量高的时候,version值在频繁变化,则会导致大量请求失败,影响系统的可用性。
我们通过上述sql语句还可以看到,数据库锁都是作用于同一行数据记录上,这就导致一个明显的缺点,在一些特殊场景,如大促、秒杀等活动开展的时候,大量的请求同时请求同一条记录的行锁,会对数据库产生很大的写压力。
悲观锁实现
只需要添加for update语句就能执行行锁
select * from table where id = 1 for update;
缺点
为什么不建议使用数据库外键?
外键优点
性能问题
假设一张表名为user_tb。那么这张表里有两个外键字段,指向两张表。那么,每次往user_tb表里插入数据,就必须往两个外键对应的表里查询是否有对应数据。如果交由程序控制,这种查询过程就可以控制在我们手里,可以省略一些不必要的查询过程。但是如果由数据库控制,则是必须要去这两张表里判断。
并发问题
在使用外键的情况下,每次修改数据都需要去另外一个表检查数据,需要获取额外的锁。若是在高并发大流量事务场景,使用外键更容易造成死锁。
扩展性问题
做平台迁移方便,比如你从Mysql迁移到Oracle,像触发器、外键这种东西,都可以利用框架本身的特性来实现,而不用依赖于数据库本身的特性,做迁移更加方便。
分库分表方便,在水平拆分和分库的情况下,外键是无法生效的。将数据间关系的维护,放入应用程序中,为将来的分库分表省去很多的麻烦。
技术问题
使用外键,其实将应用程序应该执行的判断逻辑转移到了数据库上。那么这意味着一点,数据库的性能开销变大了,那么这就对DBA的要求就更高了。很多中小型公司由于资金问题,并没有聘用专业的DBA,因此他们会选择不用外键,降低数据库的消耗。
相反的,如果该约束逻辑在应用程序中,发现应用服务器性能不够,可以加机器,做水平扩展。如果是在数据库服务器上,数据库服务器会成为性能瓶颈,做水平扩展比较困难。
单个for的冒泡排序算法:
public static void bubbleSort(int[] arr) {
//定义比较次数,次数为数组长度-1
int times =arr.length-1;
for (int i = 0; i < times ; i++) {
//arr[i]是否大于arr[i+1]
if(arr[i]>arr[i+1]){
//arr[i]比arr[i+1]大,那么交换之
int temp=arr[i];
arr[i]=arr[i+1];
arr[i+1]=temp;
}
//关键点,判断是否是最后一次比较,若是,那么重置变量
if(i==times-1){
i=-1;//i重置为-1,随后for循环会++,因此下次比较时i值为0
times--;//比较次数递减1
}
}
}
抽象类
含有abstract修饰符的class即为抽象类,abstract 类不能创建实例对象。含有abstract方法的类必须定义为abstract class,abstract class类中的方法不必是抽象的。abstract class类中定义抽象方法必须在具体(Concrete)子类中实现,所以,不能有抽象构造方法或抽象静态方法。如果子类没有实现抽象父类中的所有抽象方法,那么子类也必须定义为abstract类型。
接口
接口(interface)可以说成是抽象类的一种特例,接口中的所有方法都必须是抽象的。接口中的方法定义默认为public abstract类型,接口中的成员变量类型默认为public static final。
语法上的区别
1.抽象类可以有构造方法,接口中不能有构造方法。
2.抽象类中可以有普通成员变量,接口中没有普通成员变量
3.抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。
eclipse下不报错,但应该也不行),但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型。
抽象类中可以包含静态方法,接口中不能包含静态方法
抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。
一个类可以实现多个接口,但只能继承一个抽象类。
它们都处于java.util包中,Set、List和Map都是接口,它们有各自的实现类。Set的实现类主要有HashSet和TreeSet,List的实现类主要有ArrayList和LinkedList。
List和Set的区别
1、List,Set都是继承自Collection接口
2、List特点:元素有放入顺序,元素可重复 ,Set特点:元素无放入顺序,元素不可重复,重复元素会覆盖掉,(元素虽然无放入顺序,但是元素在set中的位置是有该元素的HashCode决定的,其位置其实是固定的,加入Set 的Object必须定义equals()方法 ,另外list支持for循环,也就是通过下标来遍历,也可以用迭代器,但是set只能用迭代,因为他无序,无法用下标来取得想要的值。)
3.Set和List对比:
Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。
List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。
概念: 分区是分割数据到多个Redis实例的处理过程,因此每个实例只保存key的一个子集
分区的优势:
分区的不足:
分区的实现:
范围分区:
用户根据数据对应的ID从0到10000的用户映射R0 10000-20000用户映射到R1… 依次分区。
存在以下问题:
哈希分区:
哈希可以适应任何形式的key,而不像范围分区一样需要key的形式为int,比较简单。一个公式就可以表达:
id = hash(key)%N
hash可以使用(crc32函数)计算出一个数值型的值。然后按照分区的个数取余就行了N就是分区的个数
ES数据并发冲突控制是基于乐观锁和版本号机制
当一个document第一次创建的时候,他的_version 内部版本号就是1;以后每次对这个document执行修改或者删除操作,都会对这个__version 版本号自动加1;哪怕是删除,也会对这条数据的版本号加1(假删除)
客户端对es数据做更新的时候,如果带上了版本号那带的版本号与es中文档的版本号一致才能修改成功,否则抛出异常。如果客户端没有带上版本号,首先会读取最新版本号才做更新尝试,这个尝试类似于CAS操作,可能需要尝试很多次才能成功。乐观锁的好处是不需要互斥锁的参与。
es更新之后会向副本节点同步更新数据(同步写入),直到所有副本都更新了才返回成功
Elasticsearch Master节点的职责
HTTP
基于TCP/IP通信协议来传递数据的协议,传输的数据类型为HTML文件,图片文件,查询结果等。
HTTP协议一般用于B/S架构()。浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。
HTTP请求报文构成
响应报文构成
Http存在如下问题:
HTTPS
HTTPS协议:一般理解为HTTP+SSL/TLS,通过SSL证书来验证服务器的身份,并为浏览器和服务器之间的通信进行加密。
SSL
SSL是位于TCP/IP协议与各种应用层协议之间,为数据通讯提供安全支持。
浏览器在使用HTTPS传输数据的流程是什么?
HTTPS的缺点
强引用
把一个对象赋值给一个引用变量,这样引用变量就是一个强引用。当一个对象被引用变量引用时,它处于可达状态,不会被JVM回收。因此是造成Java内存泄漏的主要原因之一。
Object obj = new Object();
软引用
软引用需要用 SoftReference 类来实现,对于只有软引用的对象来说,当系统内存足够时它不会被回收,当系统内存空间不足时它会被回收。软引用通常用在对 内存敏感的程序中。
Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj);
obj = null; // 使对象只被软引用关联
弱引用
弱引用需要用 WeakReference 类来实现,它比软引用的生存期更短,对于只有弱引用的对象来说,只要垃圾回收机制一运行,不管 JVM 的内存空间是否足够, 总会回收该对象占用的内存。
Object obj = new Object();
WeakReference<Widget> weakWidget = new WeakReference<Widget>(obj);
obj = null;
虚引用
虚引用需要 PhantomReference 类来实现,它不能单独使用,必须和引用队列联合使用。 虚引用的主要作用是跟踪对象被垃圾回收的状态。(唯一目的是能在这个对象被回收时收到一个通知)
Object obj = new Object();
PhantomReference<Object> pf = new PhantomReference<Object>(obj, null);
obj = null;
都可以使用ArrayList 和 LinkedList来实现。
Java语言按照错误严重性,从throwable根类衍生出Error和Exception两大派类
Error
程序在执行过程中所遇到的硬件或操作系统的错误。错误对程序而言是致命的,将导致程序无法运行。常见的错误有内存溢出,jvm虚拟机自身的非正常运行,calss文件没有主方法。程序本生是不能处理错误的,只能依靠外界干预。Error是系统内部的错误,由jvm抛出,交给系统来处理。
Exception(异常)
是程序正常运行中,可以预料的意外情况。比如数据库连接中断,空指针,数组下标越界。异常出现可以导致程序非正常终止,也可以预先检测,被捕获处理掉,使程序继续运行。
EXCEPTION(异常)按照性质,又分为编译异常(可检测)和运行时异常(不可检测)。
编译时异常
又叫可检查异常,通常时由语法错和环境因素(外部资源)造成的异常。比如输入输出异常IOException,数据库操作SQLException。其特点是,Java语言强制要求捕获和处理所有非运行时异常。通过行为规范,强化程序的健壮性和安全性。
运行时异常
又叫不检查异常RuntimeException,这些异常一般是由程序逻辑错误引起的,即语义错。比如算术异常,空指针异常NullPointerException,下标越界IndexOutOfBoundsException。运行时异常应该在程序测试期间被暴露出来,由程序员去调试,而避免捕获。
所以,java语言处理运行时错误有三种方式
阻塞队列是一种可以再多线程环境下使用,并且支持阻塞等待的队列。支持在队列为空时,获取元素的线程会等待队列变为非空,当队列满时,存储元素的线程会等待队列可用。
JDK提供的阻塞队列
基于数组实现的一个阻塞队列,在创建ArrayBlockingQueue对象时必须指定容量大小。并且可用指定公平性与非公平性,默认情况下为非公平性,即不保证等待时间最长的队列最优先能够访问队列
基于链表实现的一个阻塞队列,在创建LinkedBlockingQueue对象时如果不指定容量大小,则默认大小为Integer.MAX_VALUE
以上2种队列都是先进先出队列,而PriorityBlockingQueue却不是,它会按照元素的优先级对元素进行排序,按照优先级顺序出队,每次出队的元素都是优先级最高的元素。注意,此阻塞队列为无界阻塞队列,即容量没有上限,前面2中都是有界队列
基于PriorityQueue,一种延时阻塞队列,DelayQueue中的元素只有当其指定的延时时间到了吗,才能够从队列中获取到该元素。DelayQueue也是一个无界队列,因此往队列中插入数据的操作(生产者)永远不会被阻塞,而只有获取数据的操作(消费者)才会阻塞
SynchronousQueue是一个不存储元素的阻塞队列。每一个put操作必须等待一个take操作,否则不能继续添加元素
三种类型的BlockingQueue
无界队列
队列大小无限制,常用的为无界的LinkedBlockingQueue,使用该队列作为阻塞队列时要尤其当心,当任务耗时较长时可能会导致大量新任务在队列中堆积最终导致OOM。阅读代码发现,Executors.newFixedThreadPool 使用的就是LinkedBlockingQueue,导致cpu和内存飙升导致服务器挂掉。
有界队列
一类是遵循FIFO原则的队列例如ArrayBlockingQueue,另一类是优先级队列PriorityBlockingQueue
PriorityBlockingQueue中的优先级由任务的Comparator决定。
使用有界队列时队列大小需要和线程池大小互相配合,线程池较小有界队列较大时可减少内存消耗,降低CPU使用率和上下文切换,但是可能会限制系统吞吐量。
同步移交队列
如果不希望任务在队列中等待而是希望将任务直接交给工作线程,可使用SynchronousQueue作为等待队列。SynchronousQueue不是一个真正的队列,而是一种线程之间移交的机制。要将一个元素放入SynchronousQueue中,必须有另一个线程正在等待接收这个元素。只有在使用无界线程池或者有饱和策略时才建议使用该队列。
https://blog.csdn.net/qq_35909080/article/details/87002367
固定窗口
所以我们引入了滑动窗口机制,窗口的大小并不是固定的而是根据我们之间的链路的带宽的大小,链路是否拥塞,接收方是否能处理这么多数据,三个元素共同决定的
滑动窗口
https://blog.csdn.net/h2604396739/article/details/85239439?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&dist_request_id=&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.control
- -e 创建的是临时节点
- -s 创建的是顺序节点
https://www.jianshu.com/p/40b0bb3c21e7
上面部分为我在牛客上收集到的近年来部分面经,感觉对我这次面试帮助挺大的。下面是我自己面试被问到的问题!有点久了,有些忘记了。