面试八股加油背
2023-11-19 22:19:21 8 举报
AI智能生成
面试八股加油背,是求职者在面试前为了应对各种常见问题而进行的一种背诵练习。这种练习通常包括对自我介绍、工作经历、职业规划等问题的回答。面试官通常会问到这些问题,因此求职者需要提前准备好答案,以便在面试时能够流利地回答。此外,面试八股加油背还包括对一些常见面试问题的分析和解答技巧的学习。这些技巧可以帮助求职者更好地展示自己的优势,提高面试成功率。总之,面试八股加油背是一种有效的面试准备方法,能够帮助求职者在面试中脱颖而出。
作者其他创作
大纲/内容
通过双重循环来维护数据,内循环保证每次找到未排序的数组中最大的数,他是稳定的排序,时间复杂度是n^2
冒泡排序
对于冒泡排序的一种改进,每次找到一个基准数,设置两个指针分别在数组的左右两边,先从右向左遍历,找到一个比基准数小的位置,再从左向右找到一个比基准数大的位置,然后交换两个数,继续遍历直到两个指针相遇,然后交换指针和基准数的位置,此时基准数左侧都比基准数小,基准数右侧都比基准数大(也包含等于)是不稳定的时间复杂度 nlogn
快速排序
将数据两两分组,每次将相邻的分组进行排序合并,层层向上合并最后完成,他的排序是稳定的,时间复杂度nlogn
归并排序
排序算法
树形
图形
集合
线性
逻辑结构
链式
顺序
物理结构
数据的物理结构和逻辑结构有哪些?
管道
信号量
消息队列
共享内存
socket套接字
进程间通信的方式
做一件事情能不能立即得到返回应答,如果不能立即获得返回,需要等待,那么就阻塞了,否则可以理解为非阻塞
阻塞是指一个服务调用另一个服务以后他只能够等待调用的服务完成再进行操作
非阻塞就是当一个服务调用了另一个服务以后它可以进行其他服务
阻塞和非阻塞
做完一件事情以后再去做另一件,不管是否需要时间等待
异步就是可以同时做很多件事,并不一定需要一个做完以后再做另一个。在调用结束之后,通过消息回调来通知调用者是否调用成功。
同步是指一个服务调用另一个服务以后只能等待这个服务的结果完成后再进行下一步
异步就是通过异步的结果回调
同步和异步
1、同步阻塞:小明啥都不干等奶茶。2、同步非阻塞:小明一边玩手机一边等奶茶。3、异步阻塞:小明拿着小票啥都不干等着奶茶妹告诉他奶茶好了4、异步非阻塞:小明拿着小票玩着手机等着奶茶妹告诉他奶茶好了
阻塞非阻塞和同步异步
杂项
构造方法私有,由类来保证唯一的实例
时间换空间,在第一次访问实例的时候创建
双重检查机制,锁住部分方法体
synchronized关键字来异步锁住方法
有线程安全的问题
创建一个内部类Holder,在内部类中定义实例,由于内部类只有在被调用时才被加载,用JVM实现了懒汉式,并且没有线程安全的问题
使用静态内部类实现懒汉式
懒汉式
用空间换时间,在类加载时就创建
没有线程安全问题
枚举类实现饿汉式
饿汉式
单例模式
定义一种一对多的关系,当一个对象的状态发生改变时,会通知所有依赖于它的对象并自动更新
又称为发布订阅模式
观察者模式
有一个代理对象和目标对象,目标对象实现具体的业务,两个对象实现同一个接口,业务在调用时调用代理对象的方法,代理对象内部有一个目标对象的属性,在执行方法时执行目标对象的方法,同时加上业务
静态代理
要求传入的对象实现了接口
创建一个ProxyFactory来根据目标对象去生成一个代理对象,ProxyFactory通过java.lang.reflect.Proxy类的newProxyInstance方法传入目标对象的类加载器,对象实现的接口,以及一个InvocationHandler的匿名内部类实现他的invoke方法,来调用他的方法并且在中间增加事务
JDK动态代理
基于子类来实现,传入的目标对象类不能是final修饰
当目标对象没有实现接口时使用CGlib动态代理
CGlib动态代理
动态代理
代理模式
通过静态方法传递不同的参数实现创建不同的对象
1、一个目标对象被多个 观察者观察 List<Observer> observers;2、目标提供对观察者注册和退订的维护 attach(Observer observer)、detach(Observer observer)3、当目标的状态发生变化时,目标通知所有的注册的观察者 notifyObservers()方法通知所有观察者
简单工厂模式
通过设置抽象工厂,让工厂类符合开闭原则,工厂类都实现工厂接口,在创建工厂时创建抽象的引用,new出具体的实现类,来实现开闭原则,在扩展时不需要修改业务代码,只需要更改new出的具体的工厂对象即可
缺点是对于每一种产品都需要创建对应的工厂,使项目中的类的数目增多
工厂模式
抽象工厂就是在工厂模式的基础上增加了产品簇的概念,就是对于一个工厂,他不但能生产一种类型的产品,它有多个create方法去创建不同类型的产品去形成一个产品簇
抽象工厂模式
对于修改关闭,对于扩展开发,意思就是在扩展程序的功能时应该尽量少的去修改原来的代码,而是在设计时提供灵活的扩展方式
比如对于工厂方法模式,就是扩展产品只需要编写对应的产品工厂以及产品类,在创建时修改具体的实现类就可以扩展能添加的产品
当软件发生变化的时候,通过拓展来实现变化而不是通过修改实现变化,因为原有的功能可能被其他类所引用,如果修改的话会影响其他类的使用
开闭原则
最少知道原则
类与类之间的调用应该保持最少的了解只是用public的方法进行调用
迪米特法则
核心就是面向接口编程,业务代码应该依赖于抽象而不是依赖于具体的实现类也就是让实现依赖于抽象,而不是让抽象依赖于具体
依赖倒转原则
尽量少的去修改父类的方法,而是在继承时去扩展方法
子类应该可以替换掉父类
里氏代换原则
设计模式的原则
设计模式
提交当前的代码
git commit
切换分支
git checkout
创建分支并修改
git checkout -b
将当前分支与另一个分支合并,将两个分支直接进行合并
merge 会将生成一个commit,这个commit是来自于两个分支的,也就是基于公共分支生成的而rebase是基于当前分支生成的,在当前checkout分支的基础上生成commit
git merge
也是合并,但是区别是,是基于当前分支合并
git rebase
撤销上一次commit
git reset HEAD^
去撤销某一次的提交,生成一次新的提交,新的提交不包含撤销的提交的内容
git revert
将云服务器的分支获取下来,但是不合并
git fetch
将云服务器的分支获取下来并且merge合并
git pull
两种方式,一种是fetch将云服务器上别人的代码获得下来,然后git rebase进行合并
另一种方式是直接git pull 直接进行合并
git push的流程
为版本添加标签方便管理
git tag
修改上一次的提交但是不产生新的提交
git amend
创建分支
git branch
git相关
首先服务端转化为监听状态,客户端发送SYN报文,并且状态切换为SYN_SEND状态,服务端接收报文,状态由listen转化为SYN_RECEIVE状态,服务端发送SYN+ACK报文,客户端接收后状态转化为establish,并且发送ACK报文,服务端收到报文后也转化为establish状态
三次挥手四次握手
TCP是面向连接的,UDP是无连接的TCP的连接是可靠的,UDP的连接是不可靠的UDP传输效率高,适合传输视频直播这些对可靠性要求不是那么高的数据TCP的效率低可靠性高,适合用于网络通信
TCP连接的每一方都有固定大小的缓冲空间,TCP的接收端只允许发送端发送接收端缓冲区能接收的数据,如果接受端来不及接收数据,就提示发送方降低发送的速率,防止包丢失
流量控制
当网络拥塞时减少数据的发送
拥塞控制
校验和
超时重传
如何保证传输的可靠性
TCP和UDP
HTTPS是在http的基础上加入了TLS认证,首先客户端生成随机数R1和客户端支持的加密方式发送给服务端服务端生成随机数R2并选择加密方式,同时发送给客户端证书信息客户端验证证书信息,生成随机数R3,并且将R1 R2 R3生成报文摘要,再用服务端的公钥对R3进行加密,发送给服务端服务端再用私钥进行解密 ,然后双方用R1R2R3作为密钥进行对称式加密,进行后续的加密
HTTP是无状态的,通过cookie保存用户的身份HTTP是持续性的,一次连接以后可以一直通信
HTTP和HTTPS
计算机网络
==如果修饰基本数据类型,那么比较的就是值是否相等,如果比较的是引用类型,就会比较堆的地址是否相同而equals是Object类中的方法,只能比较引用类型,如果不进行重写,那么底层也是==,比较的还是引用类型的地址,而子类可以对equals方法进行重写,比如String类,重写后比较的就是字符串是否相等
==和equals的区别
抽象类要被继承,接口要被实现
抽象类可以定义成员变量和成员方法,接口只能定义常量
抽象类可以有构造器,接口不能有构造器
抽象类和接口的区别
JVM无法对于反射的代码进行优化,所以反射的效率会比非反射的效率慢很多
从性能角度来说
反射允许代码一些普通代码无法执行的操作,比如访问对象的私有变量,这样就破坏了代码的抽象性
内部暴露
反射的缺点
首先将Key和value封装成一个node对象,将key的值使用哈希算法进行哈希运算,得到数组下标,然后根据数组下标到hashmap对应的数组中查找,如果数组为空,那么将node对象存放在该数组的位置如果数组不为空,那么一次从头开始使用equals方法对key的值进行比较,如果返回是true,那么就将链表中对应的value值覆盖,如果返回都是false,就将节点放置在链表的尾部
首先将Key进行哈希运算获得数组下标,然后通过下标查找数组,如果对应数组中没有值,就返回null,如果有值就依次与节点的key执行equals方法,如果返回true,就返回对应的value值,如果都是false,就返回null
get(K)方法
为什么一定要重写equals方法和hashcode方法
通过hashcode方法和equals方法保证,对于传入的key首先通过两次hashcode()运算再去取余他的容量来获得对应的数组下标,如果对应下标没有数据,就将对应的node存储在位置,如果有数据就会执行equals方法如果返回为true就覆盖对应的value值,如果为false,就在链表的尾端添加对应的node
hashmap如何保证唯一性
使用大小数组+链表的形式,每次锁只锁住整个小数组,不对大数组加锁,大数组初始容量大小为16,大数组不会扩容,只有小数组会扩容,小数组使用扩容因子进行扩容,每次扩容两倍,因为大数组不会扩容,所以他的并发度是固定的,整体比较臃肿
jdk1.7
采用和hashmap一样的数据结构,数组+链表/红黑树,在hashmap中加入了线程安全的操作,使用node+cas+synchronized方式来保证安全
初始化数组时使用cas锁
添加元素时,如果数组对应位置没有元素,就用cas自旋锁添加,如果有元素,就对一个数组下标加锁,来实现线程安全
jdk1.8
ConcurrentHashMap
hashmap是线程不安全的,hashtable是线程安全的,因为hashtable底层使用了synchronized锁来保证线程安全性,
hashmap使用了数组+链表/红黑树,hashtable使用了数组+链表
hashmap效率高,hashtable效率低
hashmap和hashtable的区别
HashMap
对于函数式接口,可以使用Lambda表达式来 简化实现
对于只使用一次的接口实现类,最开始使用外部类实现接口,然后使用静态内部类然后使用局部内部类,然后使用匿名内部类,最后我们使用Lambda表达式来简化函数式接口的实现
必须是对函数式接口!
简化参数类型
必须方法只有一个参数
简化括号
必须是实现类只有一行
简化花括号
lambda表达式的简化
对于集合对象使用对象.stream()对于数组对象使用 Arrays.stream(数组)来创建
获取流操作
map讲流中的元素进行计算或转换
distinct对流中的元素进行去重
sorted对流中的元素进行排序
limit限制流的长度
flatMap将一个对象转换为另一个对象来作为流中的元素
中间操作
foreach遍历流中的元素
max min获取流中的最大值最小值
anyMatch
allMatch
noneMatch
findAny
findFirst
查找与匹配
最终操作
流操作
Stream流
lambda表达式
使用反射可以动态的创建类和对象,比如可以通过协议xml或者properity文件来读取信息来动态的创建
为什么框架要用反射
javaSe相关
调用start方法启动
继承Thread类
传入目标对象+Thread().start
实现Runnable接口
实现Callable接口
通过线程池创建
创建线程的方法
使用标志位flag来停止
利用次数让线程自动停止
不要使用stop方法来停止线程
要让线程在内部自动停止,而不是调用外部方法停止线程
线程停止
让线程进入阻塞态指定的时间,每一个对象都有一把锁,sleep不会释放锁
sleep方法
合并线程,当当前线程执行完毕后其他线程才能执行,其他线程阻塞
join方法
线程的礼让,让当前的线程进入到阻塞态礼让不一定会成功,会让cpu重新调度
yield方法
守护线程,通过setDaemo方法设置线程为守护线程守护线程是否结束不会影响虚拟机,虚拟机也不会等待守护线程结束后再结束,当前台线程结束后,虚拟机就会结束,虚拟机结束后守护线程也会跟着结束
wait方法时线程停止并且释放锁,进入到等待池中,直到其他线程调用notify/notifyall方法将他唤醒,它才会去重新申请锁资源,进入到就绪态和运行态
wait方法
sleep是Thread类下的方法,它可以在任何线程中使用,而sleep方法只能在线程同步有锁的场景下使用
sleep方法不释放锁执行后依然会对线程进行监控,而wait方法执行后会释放锁
wait方法和sleep方法的区别
线程的方法
创建态
就绪态
运行态
阻塞态
死亡状态
当线程执行wait方法时,需要用到锁(synchronized,否则会报错,wait后会进入到等待队列中,同时释放锁,当其他线程notify唤醒这个线程时,会进入到锁池,去请求锁,在获得锁后进入就绪态,对于synchronized锁住的代码块或方法,调用时也会去锁池去申请锁,在申请到后继续
wait是Object的方法,sleep是Thread类的方法
wait会释放锁,sleep不会释放锁,还是对线程进行监控,在达到要求的是时间以后会进入到就绪态,wait会释放自己的锁资源,进入到等待队列中去,等待其他的线程notify/notifyall唤醒它,再去获取锁,进入到就绪态再到运行态执行
wait和sleep方法的区别
线程的状态
对于java中创建线程和销毁线程的开销特别大,如果一个jvm中创建过多的线程那么可能会使系统资源不足,为了防止资源不足,采用一些方法来限制给定时刻处理的目标请求数目,尽可能的减少创建和销毁线程的次数,特别是一些资源耗费比较大的线程的创建和销毁,尽量利用已有对象来进行服务
为什么要用线程池?
首先会查看核心线程数量满没满,如果没满直接创建线程来执行任务
如果核心线程满了,那就将任务存放到阻塞队列中去,等待核心线程执行完毕后再创建线程执行
如果阻塞队列满了,那么就继续创建线程来执行任务,直到达到线程池规定的最大线程数
如果到达线程池规定的最大线程数,那么就直接执行拒绝策略(四种
线程池的执行流程?
也叫常驻线程数,一般情况下,不管有没有任务都会一直在线程池中存活
核心线程数
线程池所能容纳的最大线程数当活动线程达到这个值后,后面的新任务就会被阻塞
最大线程数
线程闲置时的超时时长,一般对于非核心线程来说,如果线程闲置的时间超过这个时间,就会终止该线程
等待时间
单位,TimeUnit枚举类,
等待时间的单位
当核心线程数达到最大时,新任务会放在任务队列也就是阻塞队列中等待执行
阻塞队列
一个接口,为线程池创建新线程
线程工厂
直接抛出异常
AbortPolicy(默认
让调用者线程进行调用
CallerRunsPolicy
抛弃等待队列中等待最久的任务,把当前任务尝试再次提交当前任务
DiscardOldestPolicy
默默丢弃无法处理的任务,不抛出异常也不做任何处理
DiscardPolicy
当提交的任务数大于阻塞队列的长度+线程池的最大线程数,时,出发拒绝策略
拒绝的策略
参数
根据需求创建线程池,可以扩容
newCachedThreadPool
一个任务一个任务的执行
newSingleThreadExecuto
一池N线程
newFixedThreadPool (int)
newScheduledThreadPool
Executors类创建
指定参数创建线程池
通过ThreadPoolExecutor创建
创建的方法
对于CPU密集型的任务,设置成CPU的数量 + 1
对于IO密集型的任务,设置成CPU数量 * 2
核心线程数设置
线程池的创建
线程池
Java多线程相关
给数据表加上一个版本号字段,当数据被修改时,版本号会+1,那么在提交数据时会检查数据表上的版本号和自己读取的版本号是否一致,如果一致就提交,如果不一致就重新读取版本号进行更新操作
版本号机制
CAS算法
乐观锁的实现方式
乐观锁
悲观锁和乐观锁
公平锁:就是对于线程执行必须先来先执行,不允许插队
非公平锁:允许插队,比如对于一个3s的线程和一个3h的线程,可以让3s的先执行不需要等待3h才执行
synchronized和lock默认都是非公平锁,lock锁也可以设置成非公平锁
公平锁和非公平锁
表示一个线程如果之前获得了锁,还可以再增加锁而不会产生死锁,加多少次锁就需要解多少次锁
ReentrantLock 和synchronized 都是 可重入锁
可重入锁
通过lock方法加锁通过unlock方法解锁,对于加锁和解锁必须严格使用try catch finally代码块进行保护,对于加锁必须有对应的解锁,防止产生死锁
ReentrantLock
ReentrantReadWriteLock
读锁与读锁兼容与写锁互斥
ReentrantReadWriteLock.ReadLock
实现Lock接口
写锁与读锁和写锁都互斥
ReentrantReadWriteLock.WriteLock
Lock锁
synchronized是java自带的关键字,lock锁是一个java类
synchronized会自动解锁,而lock锁需要手动解锁
synchronized不可以判断是否加锁,而lock有isLock方法判断是否加锁
synchronized锁的线程会一直等待,而lock锁有trylock()方法判断能否加锁,设定等待一定时间后结束
synchronized适合少量同步的代码,lock锁适合大量同步的代码
synchronized和lock都是悲观锁,synchronized是非公平锁,lock默认是非公平锁,可以设置成公平锁
synchronized和lock(ReentrantLock)锁的区别
锁相关
有一个生产者线程和消费者线程和一个资源类
为什么要用while循环判断,而不是用if判断?防止虚假唤醒
循环判断
修改资源
通知其他线程
执行过程
生产者和消费者问题
当一个线程修改一个共享变量时,另一个线程能够读到修改的这个值
保证可见性
比如对于i++,就是不保证原子性的,i++分为三步,1.读取i的值2.将读取的值+13.将结果赋值给i对于这种情况,当读取完i的值之后另一个线程完成了对i的值的修改,就会导致结果赋给i时出现错误,不保证原子性
AtomicIntege保证运算的原子性
不保证原子性
对于单例模式的双重检查机制,如果发生指令重排可能会导致检查发生错误,因此需要加volatile关键字
禁止指令重排
volatile关键字
需要写入的内存地址
存入前他的预期值
需要存入的值
包括的三个位置
逻辑是,如果需要写入的内存地址是我要预期的值,那么就存入需要存入的值,如果不是就一直自旋尝试,直到成功位置
是一个乐观锁
他要求必须是多核cpu
通过CPU来保证它的原子性
优点是不需要进行线程状态切换,进行线程切换会消耗性能
缺点是他是自旋锁,也就是一直会循环判断浪费时间,并且由于是基于cpu的操作,它只能保证一个共享变量的原子性
优缺点
而对于有些应用场景,ABA问题是不被允许的,就比如对于开保险箱的场景下,一个保险箱如果已经被开启过又关闭又开启,那这种情况是不被允许的,就需要处理ABA问题
为什么要解决ABA问题?
最常用的方式就是使用版本号机制+CAS自旋锁,当版本号与修改前不一致就废弃这次操作,重新执行
如何解决ABA问题
AtomicStampedReference就可以解决ABA问题,AtomicInteger类就存在ABA问题
CAS锁的ABA问题
CAS,比较并交换
死锁是一个互相抢占的过程,线程互相拥有其他线程所需要的资源一直在阻塞请求,并且不释放
互斥条件
请求和保持条件
不剥夺条件
环路等待条件
只要打破四个必要条件之一就能有效预防死锁的发生
四个必要条件
在代码中避免死锁
死锁
实现多个线程之间资源对象的线程隔离,对于不同的线程创建不同的资源对象,对于线程内部,调用同一个资源对象
因此它还有第二个作用,就是实现线程内的资源共享
作用
比如对于jdbc操作,当你需要在service层中执行两条SQL语句时,如果每条SQL语句都创建新的Connection连接对象,那么就会导致无法实现事务,事务需要在同一个连接内进行开启和回滚,因此就可以使用ThreadLocal来创建sql连接对象,方便一起进行事务的回滚和提交
具体的实现就是,当调用getConnection对象时,会先检查ThreadLocal对象中有没有Connection对象如果有就代表该线程的之前的方法已经创建过连接,就直接返回连接,如果没有就创建新的连接存在ThreadLocal中
如果这样的话如何关闭Connection呢,对于jdbc来说,以前每执行完sql就用finally关闭connection而对于ThreadLocal创建的对象如果这样就没有意义了,因此要在service层中创建生成连接对象并用finally在线程层面上去关闭连接
应用场景
ThreadLocal底层做了一个ThreadLocalMap,类似于hashMap实现了线程的隔离
set方法,ThreadLocal以自己做key,用set的对象做value
get,以自己做key,获取对应的value
remove,以自己做key,删除当前线程对应的相关信息
方法
原理
ThreadLocal
JUC并发编程相关
首先存在一个代理对象和目标对象,目标对象执行具体的方法,他们同时实现同一个接口,创建代理对象代理对象中有目标对象的属性,代理对象实现方法时会先实现目标对象的方法,然后在实现方法前后去加入增强代码比如权限控制,日志处理
为什么要用动态代理而不用静态代理,对于静态代理需要创建大量的代理类,并且如果要维护代理的方法就需要该的代码很多,因此有了动态代理
JDK自带的代理方式,首先创建一个代理工厂类,然后在工厂类的静态方法中传入目标对象,就能获取一个接口的类型的代理对象,通过调用代理对象来实现动态代理
工厂内部调用了java.lang.reflect.Proxy类的newProxyInstance方法,他会传入目标对象的类加载器,实现的接口以及一个InnovationHandler的事件处理器,去实现他的invoke方法,通过调用method.invoke方法来实现目标对象的方法,在前后增加增强的代码
JDK动态代理有一个限制就是只能为实现了接口的对象生成代理对象,CGlib动态代理通过生成子类代理对象实现如果一个类是final修饰,那么就不能使用CGlib实现动态代理
通过@Aspect注解来定义切面
@Before前置通知
有时候需要知道方法的返回值和执行结果,因此需要后置使用通知
@AfterReturning后置通知
@Round环绕通知
切面表达式,表示对于哪些返回值的那些类的哪些方法进行aop
最终通知
异常通知
设置通知的类型
表示需要切入的位置,某些类或者某些方法
PointCut切入点
Spring中使用AOP
AOP(面向切面编程
在程序中创建具体的实现类会使代码的耦合性太高,我们希望能够只面对接口编程,而不出现具体的实现类
将具体的实现类交给Spring容器去管理和创建
控制反转
将Spring容器中的bean的中的属性进行赋值
构造器注入
setter方法注入
注入的方法
根据接口的实现类进行注入
如果有多个实现类,就需要再加上@Qualifier注解来指定具体的实现类名称
@Autowired
通过实现类的名称进行注入
@Resource
通过标签注入
DI依赖注入
IOC
默认情况下只开启一级缓存,对于一级缓存只针对一个SqlSession而言,对于相同参数的相同SQL语句MyBatis会存储他的结果,只针对同一个SqlSession,当一次会话结束后就会销毁
一级缓存
针对namespace会创建一个缓存区,被多个SqlSession共享,当一个sqlsession结束后会将一级缓存的内容存储到二级缓存中
默认不开启
二级缓存
MyBatis缓存
if
where
foreach
choose
when
otherwise
set
常用的标签
动态SQL
mybatis它是对jdbc的封装
他可以很轻松的实现数据库单表和多表之间的操作
他有动态标签可以简化sql操作
有一级缓存和二级缓存来提高查询的效率
为什么要用mybatis
MyBatis
然后接收到请求后,他会根据请求的路径去向HandlerMapping请求路径对应的方法
获得对应的Handler方法以后,他会去请求HandlerAdapter去执行对应的Handler方法,Adapter找到对应的Handler方法执行完毕后会向DispatherServlet返回一个ModelAndView对象
收到以后,回去视图解析器将视图的路径解析到真实的路径,然后再通过View去渲染视图
最后返回接收到的数据通过response返回
SpringMVC的执行流程
Filter是servlet中的概念,他只能在web项目上使用Interceptor拦截器是Spring的组件,除了web项目在其他的Spring项目中也能使用
过滤器和拦截器的出发时间不同,过滤器先触发,在进入Servlet再进入Interceptor,再进入Controller
Filter 和 Spring中的Interceptor的区别
在controller执行之前拦截
preHandler
在执行完controller方法之后执行
postHandler
在渲染完视图以后执行
afterCompletion
拦截器的三个方法的执行时机
controller是单例的
阿里巴巴编码规范要求不要在controller里定义成员变量
如果一定要定义非静态成员变量,就通过通过注解@Scope(“prototype”),将其设置为多例模式
controller中可以使用ThreadLocal来实现controller中的成员变量
spring的controller是单例的还是多例的?怎么保证并发安全
SpringMVC相关
首先他会从单例池中getBean去获取bean对象
如果bean对象中没有对应的bean对象就回去实例化bean
再对bean进行属性填充
再对bean进行初始化
检查是否已经创建了aop的代理对象,如果没有就调用后置处理去创建aop的代理方法放在单例池中有的话就将半成品池中的代理对象放到单例池中,再创建
bean的获取/创建流程
循环依赖的场景,为什么会有循环依赖问题
首先解决当A包含B,B包含A时,如何不循环创建
再解决aop代理对象所产生的问题,单例池中应该是代理对象而不是目标对象
循环依赖要解决哪些问题
循环依赖的解决流程
循环依赖问题
在controller层上添加类上加上@restcontrolleradvice在里面的方法上加上@ExecptionHandler
使用注解处理异常
spring异常处理
可以将sessionid追加到浏览器请求的url中
在浏览器发送的参数中携带sessionId
在http请求头中携带sessionId
如果浏览器禁用了cookie,那么还能够使用session吗
spring相关
底层的实现原理?
不但是存储字符串,也能存储二进制数据,数字等等,可以使用方法将数字+1(文章点赞记录),可以接收任何格式的数据,图片也可以,最多512M
普通字符串
用于实现个人信息的展示和购物车信息的展示
哈希类型map
用于实现一些集合操作,并集交集差集,用来实现小程序抽奖(随机获取set集合中的指定的值,有两种方法spop和SRANDMEMBER分别代表会将抽中的数据删除和不删除抽中的数据)以及共同关注的人功能
集合类型set
它是一个双端的队列,可以作为消息队列
列表类型list
可以用作各种排行榜,权重就代表热度
有序集合类型sortedSet
redis支持的数据结构
攻击者故意搜索id特别大或为-1的不存在的数据增加数据库的压力
解决方案,对于没有的数据也在redis生成一个过期时间为1分钟的缓存数据
缓存穿透
对于数据库中有但是redis中没有的数据,用户并发量过大,导致数据库压力增大
热门数据永不过期
加互斥锁
解决方案
缓存击穿
在某个时间点,大量的缓存失效,导致数据库瞬间需要承载大量请求,导致数据库宕机
设置热门数据永不过期
设置随机的过期时间
针对redis热点内存失效的情况
采用redis集群
限流
针对redis服务不可用的情况
缓存雪崩
redis的一些问题
redis事务分为两个阶段,一个是组队阶段,一个是执行阶段
multi开启事务
exec执行事务
discard放弃事务
单独的隔离操作
没有隔离级别的概念
redis事务的特性
redis事务的相关操作
如果在排队阶段的命令有语法上的错误(就类似于编译时错误),那么就不会进入执行阶段,直接报错
但是如果是运行时错误,比如说对于一个字符串进行+1操作,在运行时才能发现错误,这种操作即使报错了也不会进行回滚
redis事务的注意事项
悲观锁解决问题
采用版本号机制来解决问题实现事务
使用wathc key命令来监视一个数据,如果在执行前该数据有改动,就会将当前事务打断
乐观锁解决问题
redis事务冲突的问题
redis中两个字段分别是库存和购买的用户id的集合,当购买以后将库存-1,将用户id加入到set集合中去,并且在每次购买前检查用户是否已经购买过
redis秒杀场景
对于不采用任何措施的场景下,如果大量线程并发涌入,会导致多卖的情况产生,在别的线程修改库存之前,其他线程已经通过if判断,导致商品多买
悲观锁解决:使用synchronized关键字可以解决这个问题,加锁让所有线程串行执行,问题就是速度会大大降低
1.并不是先来先抢到,使用版本号机制实现,那么100个线程同时到达,那么哪个线程先完成先修改了版本号,哪个线程先抢到,并不是谁先来谁抢到
2.产生库存遗留问题,对于5000个商品,100个人去抢,只有一个人能抢到,其他人因为版本号修改而作废,导致产生库存遗留问题
使用redis事务+乐观锁解决超卖问题,会产生问题:
通过lua脚本解决争抢问题,实际上是redis利用其单线程的特性,用任务队列的方式解决多任务并发问题
lua脚本又一定的原子性,不会被其他命令插队,可以完成一些redis事务性的操作
使用lua脚本解决库存遗留问题
redis秒杀产生的问题
redis事务秒杀超卖问题
redis事务相关
redis的数据备份文件,也就是将redis中的数据备份到磁盘中去,当redis出现宕机,就可以根据RDB文件去恢复redis中的数据
每次redis服务端关闭时都会自动的执行一次RDB,来实现数据的持久化操作
save方法实现RDB,这种方法是不被推荐的,他是使用redis的主进程去备份redis中的数据,那么都知道redis的单线程的如果主进程去进行RDB操作,就会影响到用户的后续操作
bgsave方法,启动后台线程去执行rdb操作,不影响主进程
RDB的操作
配置文件中可以设置RDB的文件名,RDB的频率
RDB持久化
AOF追加文件,将redis的所有写命令写入到aof文件中去
默认是关闭的
everysec每执行一次命令,就将命令写入到aof缓冲区,在每秒将缓冲区的数据写入到aof文件中去
aof的三种频率
优化aof日志中的命令,比如对于同一个key的多次写只记录最后一次,用最少的命令达到相同的效果
为了解决aof文件过大的问题,引入了bgrewriteaof命令
AOF持久化
redis的持久化
将B设置为A的从服务器,方法就是在B的客户端上执行slaveof A的ip地址 A的端口
主从集群的搭建
master执行bgsave命令,生成后台进程生成RDB文件,发送RDB文件,slave清空本地数据,加载RDB文件,再将RDB期间的所有命令写到缓冲区中去,然后将缓冲区中的内容发送到slave中
比较消耗性能的,比较慢,因为RDB文件他是需要将所有的数据拷贝到RDB文件中去,所以只有第一次会进行全量同步
用来标记数据集,如果id一致说明是同一数据集,每一个master都有唯一的replid
Replication Id
偏移量,slave完成同步以后会记录当前同步的offset,如果slave的offset小于master的offset,说明slave数据落后于master,需要更新
offset
判断一个slave是不是第一次来,基于id来判断,如果id不一样,就是第一次同步
master如何判断slave是第一次同步数据
全量同步
增量同步的同步的内容是去缓冲区中offset后的数据,只要offset不超过repl_baklog的上限
slave断开的时间过久,当尚未同步的数据太多大于缓冲区中的数据量,那么就只能去做全量同步,而不是增量同步
如果slave重启后同步,则执行增量同步
增量同步
提高repl_baklog缓冲区的大小
发现slave宕机时尽快恢复,尽可能避免全量同步
减少全量同步的次数
在master中配置无磁盘复制,当要写RDB文件时不写到磁盘中,直接发到网络的IO流中磁盘比较慢,网络比较快的时候用
提高全量同步的效率
采用主从从链式结构的redis节点,减少master的压力
主从同步的优化
主从同步的原理
用来实现主从集群的故障恢复
监控:不断的检查您的master和slave是否正常工作
自动故障恢复:如果master故障,sentinel会将一个slave提升为master,当故障实例恢复后也以新的master为主
通知redis客户端的服务发现的来源,当故障转移时将最新信息推送给客户端
哨兵的作用
每隔1秒向集群的每个实例发送ping命令
主观下线:如果某个实例规定时间内未响应,则认为该实例主观下线
客观下线:若超过一定数量的sentinel认为该实例下线,则认为该实例客观下线
哨兵的服务状态监控
排除于master断开时间超过指定值的slave节点
判断slave节点和slave-priority值,在配置文件里配置的,优先级值,默认大家一样
判断offset值,值越大越新,优先级越高
最后判断slave节点的运行id大小,越小优先级越高
选举新的master
向其他的节点更新master节点的ip和端口号 slaveof 新masterip 端口号
对于旧的master也发送将新的master认为是主节点的命令
故障转移
redis哨兵机制
redis分布式缓存
过期删除,可以给键设置过期时间,让它过期自动删除
定时删除,当redis内存满的时候会将过期时间最近的键删除
惰性删除,当访问key的时候再检查对应的key有没有过期,如果过期了就进行删除
redis的过期策略
先删redis 再改mysql 再删redis
先改mysql,再删redis
redis和mysql保证最终一致性的方案
redis
JVM是在一个操作系统的进程虚拟机,java程序通过JRE来进行编译,转换成字节码对象在通过操作系统去操作硬件体系
JVM的位置
作用:加载Class文件
包括虚拟机自带的加载器
启动类加载器
扩展类加载器
应用程序加载器
类加载器
作用:保证安全
当jvm加载一个类时,他会先去根据根加载器去加载这个类,如果根加载器中没有这个类,再去扩展加载器,再去应用程序加载器
为什么要这么做?为了保护java定义的类,比如我们自己定义了一个java.lang.String的类,然后去执行,那么java不会去优先去找我们自己定义的,而是先去跟加载器里找,也就是找java自己定义的类,这样就保证了代码的安全性,防止有人去写一个String类去破坏java代码
java无法查看根加载器,因为它是一个本地方法用native修饰,也就是c语言写的方法,所以java无法读取到
双亲委派机制
加载
确保Classs文件的字节流中包含的信息是否符合当前虚拟机的需求保证被加载类的正确性,不会危害虚拟机自身的安全
验证
创建类的static变量创建内存地址并且设置成默认值
准备
将常量池内的符号引用转换为直接引用的过程
当java文件编译成class文件时,虚拟机并不知道引用类的地址,所以使用符号来代替引用,当进入解析阶段,就将符号引用替换成真正的地址
String str = \"abc\" 编译时没有分配内存,先用符号代替,解析阶段把符号引用替换成直接引用
什么是符号引用,什么是直接引用
解析
链接
对于静态资源在<clinit>类加载器方法中初始化,执行静态代码块里的操作和静态变量的赋值操作
初始化
将加载的类信息存放在方法区中的内存空间
JVM类加载的过程
方法区
堆是所有线程共享的,需要考虑线程安全的问题
有垃圾回收机制
设置堆空间最大值的虚拟机参数 -Xmx
堆内存溢出 OOM
查看当前系统有哪些Java进程
jps工具
查看当前时刻堆内存占用情况
使用方法 jmap -heap 进程id
jmap工具
图形界面,多功能监测工具,可以连续监测
jconsole工具
可以使用jvitualvm工具
将内存转储,再查看占用内存最大的前几个对象
如果垃圾回收后内存占用仍然很高怎么办
堆内存诊断
所有类的对象都是从伊甸园区创建
当伊甸园区满后,会执行轻GC,对伊甸园区和幸存者区的from区进行gc,将gc后仍存活的对象存放在幸存者区的to区
伊甸园区
幸存者1区
幸存者0区
当经过15次轻gc后仍然存活的对象会放置在老年区
新生代
用来存放经过15次请GC后仍存在的对象
老年代
他是JVM的一个规范,并不一定要在堆中
元空间是在方法区中,逻辑上属于堆
JDK8是元空间
永久代是存放在堆内存中的
JDK7之前是永久代
对于每个线程都生成一个私有的缓冲区,用来存放每个线程私有的变量,提高并发性
线程私有的缓冲区
堆的区域
堆相关
堆
栈是线程运行需要的内存空间
一个栈帧对应了一次方法的调用
包含返回地址,参数,局部变量等方法调用信息
栈由多个栈帧组成
每个线程只能有一个活动栈帧,对应着当前正在执行的方法
栈帧
垃圾回收不回收栈内存
栈内存不是越大越大,过大会导致进程能创建的线程数减少,能增加递归调用的层数
通过虚拟机参数-Xss设置栈内存大小
栈帧过多导致栈内存溢出(方法递归调用)
栈帧过大
栈内存溢出
可能有死循环等问题
cpu占用过多
可能发生了死锁
程序运行很长时间没有结果
通过top命令动态查看进程cpu占用率
通过ps命令查看线程进程的cpu占用率
根据线程id找到有问题的线程,并且进一步定位到问题代码的源代码行数
java自带的jstack命令
线程诊断
栈相关
Java栈
用来记录JVM要运行的下一条执行
程序计数器是线程私有的,属于当前线程
不会存在内存溢出
程序计数器
本地方法栈
运行时数据区
不一定,如果局部变量没有逃离方法的作用访问,也就是只在方法内部使用,那么他是线程安全的,但是如果他逃离了,比如引用变量引用了局部变量的对象(他必须是引用类型,基本数据类型没有问题)或者作为返回值返回了,那么就可能产生线程安全的问题
局部变量一定线程安全吗?
什么时候进行GC?
对于每个对象添加一个计数器,如果他增加一个引用就+1,减少一个引用就-1,如果一个对象的引用是0,那就清除这个对象
对于环形引用的多个对象,循环引用,当引用的入口关闭以后,仍然会有引用,导致内存泄露也就是GC不会去清理他,所有java不使用这种方法
需要为每个对象保存一个整形计数器,占用空间,需要时刻维护计数器,费时间
缺点
实现简单,效率高
优点
引用计数法
可达性算法会从GCroot出发,形成一个引用链,将引用链的所有对象标记为可达,不需要清理,对于不可达的对象进行清理
同样简单高效
解决了循环依赖导致内存泄露的问题
对于虚拟机栈中栈帧存储的对象的引用地址指向的对象,也就是方法的参数,方法的局部变量
静态变量的引用,对于静态变量,除非类销毁,否则不会消除这个对象
synchronized锁持有的对象,如果锁持有的对象被回收,锁就会失效
哪些对象是GCroot对象?
当要进行可达性算法分析的时候,需要保持在一个快照中进行,因此进行可行性算法需要STW,也就是stop the world 暂停所有的用户线程,这也是GC需要STW的一个重要原因
注意事项
可达性算法
如何确定哪些对象需要GC?标记算法
清除标记阶段没有标记的内存对象
会产生内存碎片,需要去维护空闲列表
需要STW,用户体验比较查
标记-清除算法
第一阶段和标记清除阶段一样,从根节点开始标记所有被引用对象
第二阶段将所有存活的对象压缩到内存的一段按顺序排放,之后清理边界以外的所欲空间
消除了标记清除法中会产生内存碎片的问题
相比于标记复制法,不需要内存减半
效率上比标记清除法低,因为有碎片整理的过程
移动过程中,需要暂停所有的用户线程,时间比较长
标记-清除-压缩算法
没有标记的过程,将可达的对象,直接复制到内存大小相同的另一个区域中区,并且连续存放,复制完成后,A区全部清理,下一次再从B区复制到A区,A区全部清理
运行效率高
不会产生内存碎片,复制过去保证了空间的连续性
需要两倍的内存空间
对于新生区这种频繁创建对象的区域,垃圾对象很多,存活对象很少的场景比如幸存者1区和幸存者0区就是用于GC进行标记复制法清理内存的
引用场景
如果活动对象太多,每层都需要复制很多,效率很低对于老年代的GC,存在很多的活动对象,效率就比较低
标记复制算法
对于不同的对象的声明周期是不同的,因此对于不同的声明周期的对象采用不同的收集方式,来提高回收效率
java将堆分为新生代和老年代,根据不同代的特点使用不同的回收算法,提高效率
对象生命周期短,存活率低,频繁回收
使用标记复制算法最合适,因为存活的对象少,复制的对象的数量少,效率就高,速度快
区域大,对象生命周期长,存活率高,回收没有年轻代频繁
由1.标记清除 2.标记清楚和标记清除压缩算法混合实现
分代
清除阶段
GC垃圾收集
JVM相关
最常用的索引结构
B树每个节点都存储数据,而B+树非叶子节点只存放索引指针和索引的字段,对于B+树的查询会更加稳定对于B树可能需要查询1层也可能查询10层,而B+树必须查询到叶子节点
B+树叶子节点实现了单向链表,而在MySql中B+树索引对原B+树增加了一个链表指针,形成了单向链表,提高了区间访问的性能,利于数据库的排序工作
Innodb默认的数据页大小是16K,B+树的非叶子节点只存放索引字段和索引指针不存放数据,这样他就能够存储更多的索引字段和索引指针,树的深度就会更低,查询效率就会更高
B树和B+树的区别
B+树索引
不支持范围查询,只支持单个查询
hash索引
MyIsam引擎特有的索引,用于地理空间数据类型
R-tree索引
用于快速匹配文档
全文索引
二叉树
红黑树
B树
为什么不用其他数据结构?
索引的数据类型
多个字段组成的索引
复合索引
单个字段的索引
单一索引
根据主键生成的索引
主键索引
唯一索引的字段不能重复
唯一索引
索引的类型
根据索引的字段建立B+树,叶子节点存放主键ID,再根据sql语句查询的字段看有没有覆盖索引,要不要进行回表查询(再根据主键对聚集索引进行查询
二级索引(辅助索引
根据主键建立的B+树,数据存放在叶子节点
聚集索引
innoDB下的索引类型
对于索引,存在最左前缀法则,也就是索引最优先根据最左边的字段进行查询,如果中间断开也会差生索引失效比如你有ABC,三个字段的联合索引,那么如果你只根据A和C进行查询,那么索引到B就会失效,导致BC的索引都无法使用
最左前缀法则
模糊查询首部不要进行%匹配,不然会导致索引失效
对于or关键字如果一边字段没有索引就会索引失效
不要在索引列上增加函数运算否则索引失效
字符串不加单引号会形成隐式类型转换导致索引失效
如果mysql认为全表扫描比用索引快,索引失效
is null 和 is not null也会引起索引失效
索引失效的情况
80%还是通过索引
分表
读写分离
redis缓存
当数据达到百万级时,再考虑
sql优化的方式
对于数据量比较大的查询使用索引
添加索引的字段应该重复性不高
主键和外键必须有索引
对于过长的字段不应该加索引,如果加可以对于字段的前几位加索引
如果可以尽量使用复合索引
索引的使用原则
数据库索引,sql调优
DML(数据库操作语言,也就是增删改)支持ACID特性的事务
行级锁,提高并发性能
支持外键
表空间
段
区
页
行
逻辑存储结构
内存结构
磁盘结构
架构
innoDB存储引擎
不支持事务,不支持外键
支持表锁,不支持行锁
访问速度快
表结构和表数据是分开存储的
MyISAM
数据库存储引擎
锁定数据库中的所有表,让整个数据库处于只读状态,在进行数据库备份时使用在备份期间不能更行,如果从从库上备份会造成主从库延迟也可以使用快照读来进行备份
全局锁
对于当前客户端,只能读,不能写,对于其他客户端,也是只能读不能写。
与写锁互斥,与读锁不互斥
读锁(读共享锁
对于当前客户端,能读也能写,对于其他客户端,不能读也不能写,与其他写锁和读锁都互斥
写锁(写独占锁或者叫排他锁
表锁
用于避免当添加表锁时,需要一次检查每一行的行锁是否与表锁互斥,因此增加意向锁,当添加行锁时,会向表添加意向锁,这样后面其他表锁要添加时只需要判断与意向锁是否互斥就可以了,不需要扫描所有行锁
select ... lock in share mode 语句添加与读锁兼容,与排他锁互斥
意向共享锁
由insert update delete select ... for update语句添加与表锁读锁以及表锁写锁锁都互斥 意向锁之间不会互斥
意向排他锁
意向锁
DQL和DML都会自动加上共享读锁和共享写锁,相互之间不是互斥的
对于alter 语句该表表结构,那么会和共享读锁和共享写锁互斥
为了解决执行DML语句和DDL语句之间的冲突问题的,所以这个锁它无论是读锁和写锁他都是兼容的,但是对于alter语句也就是DDL语句,他是互斥的
元数据锁
表级锁
每次锁住对应行的数据,锁定的粒度最小,发生冲突概率最低,并发度高,Innodb引擎支持行锁通过对于索引上的索引项来加锁实现的而不是对记录加锁
select ... lock in share mode
共享锁 和共享锁兼容 和排他锁冲突
insert update delete select ... for update
排他锁 和共享锁和排他锁都冲突
行锁
保证索引记录间隙,确保索引记录间隙不变,防止其他事务在这个间隙insert,产生幻读在RR隔离级别下支持
间隙锁
行锁和临键锁 ,在RR隔离级别下支持
临键锁
间隙锁和临键锁是为了锁住一定范围的数据,防止产生幻读的问题
行级锁
指对于事务,要么全部成功,要么全部失败,通过undo log日志实现事务的回滚操作当事务中的语句发生错误时,通过undo log 日志回滚之前的操作实现原子性
原子性
通过其他的三个属性来保证,意思是事务的执行结束后,如果事务回滚,那么数据库中的数据和事务执行前保持一致,如果事务提交,那么提交后的数据库的数据是完整的
一致性
读未提交
读已提交
可重复读
串行读
隔离性的三个级别
事务的传播特性
事务的隔离级别
spring相关事务隔离性使用@Transactional注解添加事务
隔离性是底层是通过MVCC+锁来实现的,快照读也就是不同的select语句是通过MVCC来实现的,而当前读也就是DML语句和select...for update 和select ...lock in share mode 是通过加锁来实现的,对于不同的语句加的锁的粒度也是不同的,
隔离性
持久性是底层是通过redo log 日志来实现的,对于已经提交的事务,redo log会记录数据的物理信息,并且提交到磁盘上,当磁盘刷新脏页时出现错误,就可以通过redo log 日志来实现数据的恢复,保证数据的持久性
持久性
指的是一个事务读取到其他事务还没有提交的数据
事务A去执行update语句,但是还没有提交,此时事务B去执行select语句,读取到事务A没有提交的修改
读脏数据
一个事务先后读取同一条记录,但是读取的结果不同
事务A两次读取了一条数据,在这个过程中事务B修改了这个数据导致事务A两次读取的内容不同
不可重复读
一个事务按照条件查询数据时,没有对应的数据行,但是在插入数据时,又发现这行数据已经存在
幻读
产生的三个问题
事务的ACID特性
保障事务的持久性
当执行update delete insert语句时,会首先从内存缓冲池中找有没有对应的数据,如果没有就会去磁盘将对应的数据更新到缓冲区,然后再在buffer pool缓冲区中更新数据 更新完成后提交事务,然后缓冲区中被修改的数据页被称为脏页,会按照一定的频率将脏页刷新到磁盘空间中去,而这时如果磁盘刷新发生错误,就无法保证数据的一致性,这时就要引入redo log 日志,redo log 是一个物理日志,会存储数据页的数据变化,他为两个部分,一部分在缓冲区,一部分在磁盘中,当事务提交时,会同步将缓冲区的redo log 日志刷新到磁盘中去,如果刷新磁盘产生错误,就会借助redo log 日志来恢复,保证数据的持久性
redo log日志
保障事务的原子性
undo log日志是一个逻辑日志,它会记录事务执行的sql语句修改前的数据,在事务回滚时,通过undo log日志实现数据的回滚,恢复到事务开始之前的状态,保证原子性,同时undo log并不都会在事务提交后删除,Insert语句的undo log会立即删除,而update delete语句会保留形成版本链,用于MVCC,并且配合两个隐式的字段 最近修改的事务ID,上一个版本的地址引用保证事务的隔离性
undo log 日志
保障事务的隔离性
当前读
一种非阻塞读,它读取的不一定是数据的最新版本
快照读
不同事务或相同事务对同一行数据的修改会产生一条undo log版本链
undo log版本链
读视图,它是快照读sql读取版本的依据,包含四个字段当前活跃的事务id集合活跃的最小事务id预备事务的ID集合(活跃的最大事务的ID+1ReadView创建者的事务ID
通过版本链数据访问规则来确定快照读到底访问undo Log版本链中的哪段数据
版本链数据访问规则1. trx_id == creator_trx_id 可以访问该版本2. trx_id == min_trx_id 可以访问,说明事务已经提交了3. trx_id > max_trx_id 不可以访问该版本 不可以访问 说明该事务是在ReadView 生成后才开启的4. min_trx_id <= trx_id <= max_trx_id 如果trx_id 不在m_ids 中是可以访问该版本的 说明数据已经提交
readView
在RC隔离级别下,每次快照读都会生成新的ReadView,实现每次读取undo log版本链中已经提交事务的版本
在RR隔离级别下,只有第一次快照读会生成ReadView,后面的快照读沿用第一次的ReadView,因此后面每一次读取的结果都是一致的,这也就是为什么叫可重复读,事务每次重复读的ReadView都相同,那么读取的undo log版本链中的数据也相同
在RR的隔离级别下又增加了间隙锁和临键锁来方式幻读问题
RC锁不会升级?
其他区别
RC 和RR隔离级别下的MVCC区别
MVCC(多版本并发控制
事务的原理
包含一系列sql语句的集合,完成一个复杂的功能,存储过程可以被反复使用,执行效率高
存储过程
他不是一个真正的表,用来查询数据和展示数据,但是不能修改数据
视图
一个由事件触发的存储过程
触发器
union和unionAll都是将两个sql查询结果合并,区别是union它会将查询的相同查询结果合并,而unionAll不会合并
union和unionAll有什么区别
varchar是不定长存储char是定长存储
从速度上定长的速度快
varchar因为是不定长的所有需要有一个数据表示存储了多少长度
如果对于长度有时候很长有时候很短的,这种如果用char存储空间利用率就很低,就最好用varchar
对于性别这种一个1长度的就最好用char,利用率高,查询速度快
varchar和char的区别
内连接就是匹配两个表中符合on连接条件的所有行
获取左表中的全部内容,并且显示右表中匹配的内容,如果右表中没有匹配的内容,对应字段就显示为null
左外连接left join
获取右表中所有的内容,并显示左表中匹配的内容,如果左表中没有匹配的内容的字段,对应位置为null
右外连接 right join
full join结合了左外连接和右外连接,左右表的内容都会展示,左表没有匹配的右表字段的和右表没有匹配左表字段的也会展示,并且外表字段为null
全外连接 full join
外连接
内连接与外连接
from语句
group by having
聚合函数
select
order by
sql执行的顺序
尽量避免使用子查询
读取尽量用limit,不要全部查询
用in来代替or
禁止没必要的orderby
避免全表扫描
sql语句优化
alter table table_name add index ('coloum1');
创建索引的语句
round()四舍五入
floor 向下取整
ceil()向上取整
mysql的取整函数
sql语句编写相关
数据库相关
集群就是指多台服务器做相同的功能而分布式是多台服务器每台都做不一样的功能,一起组成一个项目
什么是集群?什么是分布式?
用户传过来的技术会经过springCloudGateway网关,去注册中心去进行注册与发现,再去具体的微服务进行访问
SpringCloudGateway
网关
服务集群就是多台服务器开启同样的服务,负责相同的服务
服务集群
对于微服务的调用,不只调用一台服务器的服务,而是根据调用的策略去进行调用,比如随机调用、轮询调用策略
负载均衡
所有的微服务都会去注册中心去进行注册,让注册中心去进行分配调用
注册中心
服务限流就是说对于服务接收大量的请求,会使用服务限流策略,限制一部分流量的进入
对于有些依赖其他服务的服务,可能服务会出现异常,导致整个服务的资源被占用(多个线程都在等待这个服务),此时会有一个异常比例的概念,如果异常比例达到会直接切断这个服务,以免局部的异常导致整个服务的资源崩溃
服务限流、熔断
使用seata实现分布式事务,对于单体应用来说,事务可以使用trational注解来实现,但是对于分布式系统,服务都是分离的,比如对于支付订单,可能需要调用支付,仓库,订单相关的微服务来一起完成,这个时候就无法使用trational注解来实现,因此使用seata实现分布式的事务,保证操作的原子性
使用seata完成
分布式事务
微服务的相关概念
SpringCloud分布式微服务
面试要问的
0 条评论
回复 删除
下一页