Advertisement

JAVA知识重点

阅读量:

【Git】

一、 Git常用命令

git init 初始化
全局设置
git config --global user.name “smallming”
git config --global user.email
“293882181@qq.com”
设置成功后会在C:\Users\smallming(此目录是Windows系统账号名)中出现.gitconfig
git add filename 添加文件到缓存区
git commit -m “备注” 提交到本地库
git remote add origin 仓库地址 设置远程仓库地址
git push -u origin master 推送到远程仓库
git clone 克隆远程仓库内容
git pull origin master 拉取内容
git rm 删除文件
git checkout -b 分支名 master 创建分支
git branch 查看本地分支
git push origin 本地分支名:远程分支名 推送分支
**git branch --set-upstream-to=origin/**分支名 关联本地和远程分支
git merge --no-ff 分支名 把分支合并到当前分支(注意先切换到当前分支)
git branch -d 分支名 删除本地分支
git push origin --delete 分支名 删除远程分支

二、 Git中常用分支及作用

主干线-master:主要存储正式版本软件。
开发干线-Develop:早晚提交至该干线,请确保最终整合至主干线上方。
功能干线-feature:专为开发特定功能而设立的。
预发干线-release:在发布前进行测试。
修复漏洞-fixbug:专门用于修复发布后出现的问题。

【Linux】

三、 Linux常用命令(笔试或面试)

cd 进入目录
cd..返回上一级
pwd 打印当前目录
clear 清屏
ls/ll 显示当前目录内容
mkdir 创建新文件夹
mkdir -p 路径 : 如果路径中包含了不存在的路径 则自定创建一个
rm -r 文件夹路径 : 删除目录所有文件夹及文件夹中的子内容都需要通过输入y进行确认删除
rm -rf 文件夹路径 :删除文件夹不需要确认
esc 退出编辑状态在退出编辑状态下 按两次d 表示删除当前行D+数字+回车便是删除数字行
q 退出 只有没有任何编辑操作的时候能使用
wq 保存并退出
**q!**强制退出Cat 文件路径
touch 创建新文件
cp 复制
mv
rm 删除
vi/vim 编辑
cat 查看内容内容
tail 查看文件后几行
tar 解压gzip文件
yum install 在线安装
ps aux|grep 查看进程
kill 杀掉进程
reboot 重启

【JVM】

四、 请介绍一下HotSpot

在Java的官方文档中能够找到每一版提供的两个版本文档地址。

请添加图片描述

官方对JVM所规定的规范是什么?规范指的是那些指导我们开发JVM的行为和原则。
但在实际开发过程中可能存在一定的偏差或差异。
我们现在主要应用的是官方所制定的JVM规范的具体实施方式。

请添加图片描述

在该工具的实现中配置若干参数以支持开发者的调试需求(其中JVM调优即通过调节这些配置项来完成)。HotSpot:https://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html 然而,在市场上还有其他厂商各自独立地实施了具体的JVM规范。

JVM名称 说明
HotSpot Oracle官方对JVM规范的实现
jRocket 曾经JVM的扛把子,号称最快的虚拟机,后被Oracle收购,和HotSpot整合
J9 IBM对JVM规范的实现
TaobaoVM HotSpot深度定制版
azul zing 商业收费版

五、 JVM内存结构

JVM主要负责执行Java程序,在物理计算机中作为一个进程运行。
在JVM运行过程中,为了便于管理和GC回收的需求,在所占用的内存空间内进行合理划分。

1 Java HotSpot VM 内存结构图

请添加图片描述
1.1 源文件
复制代码
    源文件就是我们编写Java代码的文件。文件扩展名为.java
1.2 字节码文件
复制代码
    字节码文件是源文件编译后的文件。字节码文件是二进制文件,需要通过特定的工具才能查看。里面存放了源文件编译后的字节码指令。
1.3 类加载器 Class Loader
复制代码
    Java 程序运行时会由类加载器负责把.class的字节码文件装在到内存中,供虚拟机执行。
       加载 Loading
    1. 启动类加载器 BootStrap Class Loader   :  负责从启动类中加载类。具有最高执行优先级。即:rt.jar等。
    
    2. 扩展类加载器 Extension Class Loader  :  负责加载扩展相关类。即:jre/lib/ext 目录
    
    3. 应用程序加载器 Application Class Loader  :  加载应用程序类路径(classpath)中相关类。
    4. 链接 Linking
    5. 校验 Verify  : 校验器会校验字节码文件是否正确。
    6. 准备 Prepare  : 所有静态变量初始化并赋予默认值
    7. 解析 Resolve  :符号引用被换成直接引用。
    8. 初始化 Initialization  :  所有静态变量赋予初值,静态代码块执行。
1.4 执行引擎

运行时数据区的字节码会交给执行引擎执行

复制代码
 解释器 Interpreter   : 解释器负责解释字节码文件。每次方法调用都会被重新解释。
    2. JIT编译器  : JIT 即时编译器。对于多次被使用的方法会放入到本地内存中,下次就可以直接调用了。
    3. 探测器  ; 负责探测多次被调用的代码。
    4. 垃圾回收器 GC  : 负责回收不在被使用的对象。GC是JVM中非常重要的一块,在后面我们会单独讲解一下GC。
1.5 本地库接口
复制代码
    	在Java代码中使用native修饰的方法表示方法具体实现使用其他编程语言实现的。例如:C语言。通过本地库接口为Java程序提供调用其他语言的实现方案。
1.6 本地方法库
复制代码
    	所有的本地方法,通过本地库接口调用。
1.7 程序计数器
复制代码
    程序计数器简称:PC Register
    程序计数器是一块较小的内存空间。记录了当前线程执行到的字节码行号。每个线程都有自己的程序计数器,相互不影响。如果是native方法,计数器为空。
1.8 虚拟机栈
复制代码
    虚拟机栈跟随线程创建而创建,所以每个线程都有一个虚拟机栈。
    虚拟机栈中存储的是栈帧(frames),每个栈帧对应一个方法,每个栈帧都有自己的局部变量表、操作数栈、动态链接和返回地址等。当前正在执行的方法称为当前方法,当前方法所在的帧称为当前帧。方法执行时帧就是一个入栈操作,方法执行完成之后栈帧就是一个出站操作。
1.8.1 局部变量表
复制代码
    局部变量表存储的8大基本数据类型和返回值以及方法参数及对象的引用。	其中long和double占用2倍长度。
    局部变量表就是一个数组,数组的长度在编译器确定。通过从0开始的索引调用局部变量表的内容。
    对于类方法,从索引0开始连续N个作为方法传递。对于实例方法索引0存储的都是实例化方法的实例对象的引用。
1.8.2 操作数栈
复制代码
    操作数栈存在于栈帧中,其大小在编译期确定。
    操作数栈中存储了class文件中虚拟机指令以及准备要传递的参数和接收对方的返回结果。
    运行时常量池中数据以及局部变量表中得值都可以由操作数栈进行获取。
1.8.3 动态链接
复制代码
    方法·把符号转换为直接引用分为两种情况。在JVM加载或第一次使用转换时称为静态链接或静态解析。而在运行期间把符号转换为直接引用时就称为动态链接。
1.8.4 方法返回地址

方法返回地址分为两种情况:

复制代码
 正常结束执行。例如碰见return关键字。调用程序计数器的值后当前栈帧直接出栈就可以了。
    2. 异常结束。可能需要恢复上层方法的局部变量表和操作数栈,然后把返回值压如到栈帧的操作数栈中,之后调用程序计数器的值后获取到下条指令。
1.9 堆
复制代码
    堆是所有线程共享的,存储类的实例和数组。
    堆是在虚拟机启动时创建的,由GC负责回收。
    堆可以是一块不连续的内存空间。
    在Java 8 中,String是存在于堆中的。

堆被分为二大部分:

复制代码
    在Java 7时分为:新生代(Young Generation)、老年代(Old Generation)。在HotSpot中使用永久代来实现方法区的规范。且新生代、老年代和永久代是连续的。
    新生代又被分为Eden区、From Survivor区、To Survivor区。官方说明默认分配比例为8:1:1。但是使用jmap工具进行测试时发现比例为6:1:1。
复制代码
    在Java 8时把永久代替换为元空间(MetaSpace),也就是说在Java8中使用元空间来实现方法区。且在Java8中把元空间移植到本地内存上(Native Memory),其实在Java 7 时,部分数据已经移植到本地内存上了。例如:符号引用(Symbols)
1.10 方法区
复制代码
 方法区是线程共享的。
    2. 在虚拟机启动时自动创建方法区,方法区可以是一块不连续的内存空间。
    3. 方法区可以理解为编译代码存储区。在方法区中存储每个类的结构、运行时常量池、字段、方法、构造方法。
    4. 在JVM规范上方法区是一个独立的区域,但是在Java SE7 的HotSpot 上方法区使用永久代作为实现,永久代和堆是一块连续空间。在Java SE8的JVM规范实现上,HotSpot使用元空间实现方法区。
1.11 运行时常量池
复制代码
    运行时常量池存在于方法区。存储每个类或每个接口从编译时已知的数字文字到必须在运行时解析的方法和字段引用。
1.12 本地方法栈

本地方法栈主要目的旨在支持 native 方法;每一个线程都拥有自己的本地方法栈。

六、 说一下JDK6、7、8中JVM内存结构的区别

请添加图片描述

七、 请说一下你知道的JVM启动参数

初次接触Java课程时, 我们都会编写第一个HelloWorld程序, 然后使用javac命令行工具对其进行编译, 运行一个名为java.exe的工具, 该工具提供了多种参数选项用于配置.
通常所说的JVM启动参数实际上是java.exe中的options选项.
Java 8 中HotSpot官方文档地址
https://docs.oracle.com/javase/8/docs/technotes/tools/windows/java.html

1 总体语法

复制代码
    java [options] classname [args]
    
    java [options] -jar filename [args]
    
    javaw [options] classname [args]
    
    javaw [options] -jar filename [args]

语法解释:
1. java和javaw为JDK里面带有的工具

请添加图片描述
复制代码
 **options**:选项。工支持6种选项。
    3**. classname**:类名
    4. **-jar** 参数
    5. **filename** 文件名称
    6. **args** 传递给主方法的参数。用空格分隔。

options分类

options分为6类,分别是:

复制代码
 Standard Options 标准选项。
    2. Non-Standard Options 非标准选项
    3. Advanced Runtime Options 高级运行时选项。控制Java HotSpot VM运行时行为。
    4. Advanced JIT Compiler Options 高级JIT(即时编译器)编译器选项。使用JIT时优化参数,属于HotSpot专有选项,非JVM通用选项。
    5. Advanced Serviceability Options 高级可维护性选项。提供了收集系统信息和执行大量调试的能力。
    6. Advanced Garbage Collection Options 高级垃圾收集选项。控制HotSpot执行GC的方式。
    其中3、4、5、6的参数都是以-XX:开头的

Standard Options(标准选项)

复制代码
    	标准选项是所有JVM实现都支持的选项。
1. -help
复制代码
    	启用帮助,显示所有支持的选项。
    	也可以写成:java -?
2. -version
复制代码
    	打印版本号。
3. -ea
复制代码
    	启用断言。默认情况下断言是不启用的。
    	assert expression;如果表达式为真继续运行,如果表达式为假报异常。
请添加图片描述
复制代码
    	assert expression1:expression2;如果expression1为真继续运行。如果表达式为假expression2作为异常信息。
4. -javaagent:jarpath[=options]
复制代码
    	加载指定的jar包。
5. -Dproperty=value
复制代码
    	设置系统属性值。在项目部署或Spring Boot项目中会使用到。
6. -jar filename
复制代码
    	运行指定的jar文件。
7. -verbose
复制代码
    	-verbose:gc 打印GC信息。用于诊断GC时使用。
    	-verbose:class 打印载入类的相关信息。可用于诊断找不到类或类冲突时使用。
    	-verbose:jni 输出native方法调用情况。可用于诊断native调用相关问题。
    	Non-Standard Options(非标准选项)
    	非标准选项是HotSpot专有选项。所有内容都是以-X开头(必须是大写)。
8 . -X
复制代码
    显示所有选项。
请添加图片描述

里面常用的就红色区域:

复制代码
    -Xms<size> 设置初始化Java堆大小
    
    -Xmx<size> 设置最大堆大小。
    
    -Xss<size>  设置Java线程堆栈大小。等效于-
    XX:ThreadStackSize
    
    -Xmn<size> 年轻代初始化和最大大小。建议年轻代占用整个堆的1/2~1/4之间。年轻代太小GC频率太高,年轻代太大会触发Full GC。也可以使用还可以使用-XX:NewSize设置初始大小和-XX:MaxNewSize最大大小
    
    -Xloggc:filename 设置将gc信息写入到某个日志文件中。
高级运行时选项 Advanced Runtime Options
复制代码
    控制Java HotSpot VM运行时行为。所有的参数都是-XX: 开头。注意大小写。
    
    -XX:+PrintCommandLineFlags
    打印命令行JVM参数。默认不开启不打印。通过此内容可以查看默认配置,例如当前锁使用的虚拟机类型。等效于jinfo -flags pid
    
    -XX:ThreadStackSize
    设置线程堆栈大小。等效于-Xss
    
    -XX:+TraceClassLoading
    启用类加载跟踪。默认不开启打印。
    
    -XX:+TraceClassLoadingPreorder
    按照引用顺序跟踪所有加载的类。默认禁用
    
    -XX:-UseBiasedLocking
    禁用偏向锁。设置后对于一些大量无竞争的同步操作会明显提升性能。但对于某些具有锁的应用性能会降低。
高级JIT编译器选项 Advanced JIT Compiler Options

使用JIT时优化参数。

复制代码
    -XX:+AggressiveOpts
    允许积极的使用性能优化功能。预计在后续版本设置为默认开启。目前是默认关闭的。
    
    -XX:+BackgroundCompilation
    启用后台编译,默认开启。如想取消使用-XX:-
    BackgroundCompilation(等效于-Xbatch).
    
    -XX:CICompilerCount=threads
    设置用于编译的编译器线程数。默认情况下,服务器 JVM 的线程数设置为 2,客户端 JVM 的线程数设置为 1
    
    -XX:CodeCacheMinimumFreeSpace=size
    设置编译器最小空间。默认500KB。当剩余空间少于最小空间时,编译器停止工作。
    
    -XX:InitialCodeCacheSize=size
    设置初始代码缓存大小
    
    -XX:+OptimizeStringConcat
    启动String连接操作优化。默认启用。可使用-XX:-OptimizeStringConcat禁用
高级可维护性选项 Advanced Serviceability Options

**具备获取系统相关信息以及支持复杂调试的能力。官方文档仅列出了六个参数设置。

**具备获取系统相关信息以及支持复杂调试的能力。官方文档仅列出了六个参数设置。

复制代码
    -XX:+HeapDumpOnOutOfMemoryError
    当出现java.lang.OutOfMemoryError把堆中内容转存到当前目录文件中。默认禁止的。
    
    -XX:HeapDumpPath=path
    当设置了-XX:+HeapDumpOnOutOfMemoryError以后,转存文件路径。例如:-XX:HeapDumpPath=C:/log/java/java_heapdump.log
    
    -XX:LogFile=path
    设置写入日志数据的路径和文件名,例如-XX:LogFile=C:/log/java/hotspot.log
    
    -XX:+PrintClassHistogram
    默认禁止。当按Ctrl+Break时打印类的直方图。等效于:jmap -histo pid 命令
    
    -XX:+PrintConcurrentLocks
    默认禁止。打印Concurrent相关的锁。等效于:jstack -l
    
    -XX:+UnlockDiagnosticVMOptions
    默认禁止。解锁诊断VM选项内容。因为在JVM中某些参数必须诊断后才能赋值。
高级垃圾回收选项 Advanced Garbage Collection Options

配置HotSpot进行垃圾回收操作的方式有多种选择。这些可选的参数数量较多,并且具有显著意义。其中特定的参数属于JVM优化常用的设置项。

复制代码
    -XX:ActiveProcessorCount= x
    设置VM操作各种线程池(例如:ForkJoinPool)的CPU数量
    
    -XX:+AggressiveHeap
    默认禁止。启用堆优化。设置后将会根据计算机配置(RAM和CPU)将各种参数设置为最合适长时间运行的值。
    -XX:+CMSClassUnloadingEnabled
    默认开启。在使用标记清除垃圾收集齐(CMS)时开启类卸载。可使用减号禁用。
    
    -XX:CMSInitiatingOccupancyFraction=num
    设置老年代占用比例。取值(0-100),如果为负数,表示-XX:CMSTriggerRatio进行定义比例。
    
    -XX:ConcGCThreads=threads
    设置并发GC时可用线程数。默认情况下该值取决于JVM可用的CPU数量。
    
    -XX:+DisableExplicitGC
    默认禁用的。开启后将不会处理System.gc();但JVM仍会在必要时候进行GC。
    
    -XX:+ExplicitGCInvokesConcurrent
    默认禁用。通过System.gc()启用并发GC。必须与-XX:+UseConcMarkSweepGC一起启用。
    
    -XX:G1HeapRegionSize=size
    设置使用垃圾优先 (G1,garbage-first ) 收集器时 Java 堆细分到区域大小。
    
    -XX:+G1PrintHeapRegions
    默认禁止。打印哪些堆区域进行了回收,以及G1的使用情况。
    
    -XX:InitialHeapSize=size
    初始化堆大小。必须是0或1024的倍数且大于1MB。默认是根据系统配置自动选择。
    
    -XX:InitialSurvivorRatio=ratio
    初始化Suvivor区域的比例。
    
    -XX:InitiatingHeapOccupancyPercent=percent
    初始化堆所占空间触发GC的比例。默认45%。设置0表示不间断回收
    
    -XX:MaxGCPauseMillis=time
    单位毫秒。设置堆最大暂停时间。JVM有可能无法满足这个要求。默认没有设置。
    
    -XX:MaxHeapSize=size
    堆最大大小。等效于-Xmx
    
    -XX:MaxHeapFreeRatio=percent
    设置GC后最大堆空闲比例。如果超过这个比例将会缩小。默认值75%
    
    -XX:MaxMetaspaceSize=大小
    最大元空间大小。默认是根据系统所剩内容自动判断。
    
    -XX:MaxNewSize=size
    新生代最大大小
    
    -XX:MaxTenuringThreshold=threshold
    设置GC最大大小,即GC进行回收时年龄的大小。处于Survivor区域的对象每次GC后年龄都会增加1。最大值15.parallel collection默认值15.CMS默认值6.
    
    -XX:MetaspaceSize=size
    元空间大小。超过后进行GC。默认需要看系统可以内存大小。
    
    -XX:NewRatio=ratio
    年轻代和老年代比例。默认值为2.
    
    -XX:NewSize=size
    新生代大小。等效于-Xmn
    
    -XX:ParallelGCThreads=threads
    并行GC线程数。默认值取决于CPU。
    
    -XX:+PrintGC
    打印GC消息。默认禁用。
    
    -XX:+PrintGCApplicationStoppedTime
    打印GC时应用暂停时间
    
    -XX:+PrintGCDetails
    打印GC详情。默认禁用。
    
    -XX:+PrintGCTimeStamps
    打印GC时间戳
    
    -XX:+ScavengeBeforeFullGC
    在每次FullGC之前先对年轻代进行GC。默认开启的。且Oracle不建议禁用。
    
    -XX:SurvivorRatio=ratio
    设置年轻代中Eden区和Survivor区的比例。默认值是8.但是经过测试发现是打印的是8,实际比例为6.
    
    -XX:TargetSurvivorRatio=percent
    设置年轻代GC后Survivor区域的比例。
    
    -XX:+UseAdaptiveSizePolicy
    默认启用。使用自适应大小策略。如果想关闭,可以使用减号。关闭后必须显示设置 -XX:SurvivorRatio
    
    -XX:+UseG1GC
    默认禁用,根据计算机配置和JVM类型自动选择。配置后表示启动G1垃圾收集器。适用于服务端的垃圾收集器。主要是针对内存比较大的情况。G1可以尽可能的满足GC暂停时间要求,同时保证良好的吞吐量。在使用G1收集器的时候建议堆大小至少6GB以上。
    
    -XX:+UseGCOverheadLimit
    默认启用的。JVM限制在抛出OutofMemoryError之前GC所花费的比例。如果GC花费时间超过98%,而堆恢复低于2%则抛出OutOfMemoryError
    
    -XX:+UseParallelGC
    默认禁用。配置表示启用并行GC。如果开启会自动-XX:+UseParallelOldGC。除非显示禁用了。对于64位平台,默认GC类型。
    
    -XX:+UseParallelOldGC
    默认禁用。配置表示启用并行老年代GC。如果开启会自动启用-XX:+UseParallelGC
    
    -XX:+UseParNewGC
    默认禁止。启动年轻代并行GC。在JDK8 中如果希望启用年轻代GC时推荐使用这个选项,而不是XX:+UseConcMarkSweepGC
    
    -XX:+UseConcMarkSweepGC
    默认禁止,JVM根据计算机配置自动选择合适的GC。在老年代启用CMS垃圾回收器Oracle建议在吞吐量(-XX:+UseParallelGC)垃圾收集器无法满足应用程序延迟要求时使用CMS垃圾收集器。当开启此选项后自动开启-XX:+UseParNewGC。并且在JDK8中不允许-XX:+UseConcMarkSweepGC和-XX:-UseParallelGC组合
    
    -XX:+UseSerialGC
    启用串行GC。默认禁止。但是对于小型简单应用,这种GC是较佳选择。客户端默认GC类型。
    
    -XX:+UseStringDeduplication
    G1回收器专有选项。默认禁止。配置后表示启用字符串重复删除。也就是说允许多个字符串对象指向同一个堆中字符串对象。

八、 GC是如何判断对象已经死亡(是垃圾)

复制代码
    引用计数(已淘汰)
    引用计数算法就是看对象是否被引用。如果引用则对象计数器加一。如果释放引用计数器减一。但是引用计数算法最大的问题就是循环引用问题。当出现循环引用时对象计数器至少为1.这时候对象可能已经是垃圾了,但是无法被回收。
请添加图片描述

根可达算法(Root Searching)

达可根算法通过避免循环引用无法被回收的问题。其核心思路是运用一系列被称为GC Roots的对象作为根节点,在这些节点基础上进行深度遍历搜索。遍历过程中经过的所有路径都被视为引用链(Reference Chain)。一旦某个对象成为GC Root,则表示该对象当前仍在使用资源;若发现无引用来或与其他非GC Roots之间存在循环引用的情况,则应将其归类为垃圾。例如,在这种系统架构下:静态变量通常会被分配为GC Root;线程变量则会在每个线程启动时独立分配各自的GC Root;常量池中的常量对象也会被配置为相应的GC Root;而JNI(指针)类型则会根据具体的内存管理策略来确定其对应的GC Root

请添加图片描述

九、 请说一下你知道的GC算法

标记清除算法(Mark-sweep)

首先识别并标定需回收的所有对象。在完成标记后将所有已标记的对象统一回收。缺点主要体现在效率低下和碎片化严重方面。

请添加图片描述

复制算法(coping)

主要目的是解决标记清除算法产生的碎片问题。在实现过程中,将内存划分为容量相等的两部分,每次仅占用其中一部分内存空间。当内存中的一块被用完后,在另一部分内存中复制存活的对象,并一次性清空该区域的空间。该方法的一个明显缺点是可用内存总量有所减少。

标记压缩算法(Mark-Compact)

又称为标记整理算法。与标记清除算法存在相似之处。其主要区别在于,在完成标记后,并不会立即进行清除操作;相反地,在一端方向上会首先移动所有未被回收的对象,并随后清除边界外部的对象。从而避免了内存碎片的产生。

请添加图片描述

分代收集算法

将内存堆划分为新生代和老年代两个部分。其中新生代部分采用特定算法进行处理,而老年代部分则采用另一种算法。具体的算法选择则取决于所使用的垃圾回收机制。

十、 请说一下你知道GC的种类
1 总表说明

请添加图片描述

这些内容可以组合关系

请添加图片描述

十一、 请详细介绍下GC中各种垃圾回收器

1、 Serial、Serial Old 串行收集器

1.1 Serial 收集器
复制代码
    	起源于JDK 1.3,单线程执行,每次回收必须STW。
    	应用场景:虚拟机在client模式下默认的GC。
    	优点:简单高效。
1.2 Serial Old收集器
复制代码
    	老年代收集器。标记整理算法。单线程。主要应用在client模式下老年代收集。在JDK1.5之前可以与Parallel Scavenge配合使用。可作为CMS的备选。

2 ParNew收集器

复制代码
    	Serial 收集器多线程版本,用于收集新生代。可与CMS配合使用。
    	ParNew可以并行执行,主要为了减少STW的时间,加快程序响应,给用户提供良好的体验。

3 Parallel Scavenge收集器

复制代码
    	新生代收集器。采用复制算法。可以并行执行。
    	优点:
    	具备自适应调节能力。-XX:+UseAdaptiveSizePolicy
    	主要解决吞吐量问题。也被称为“吞吐量优先”收集器。即吞吐量 = 运行用户代码时间 /(运行用户代码时间 + 垃圾收集时间),虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%。高吞吐量可以高效的利用CPU时间,尽快完成运算任务。

4 Parallel Old收集器

复制代码
    	老年代收集器。标记整理算法。多线程。JDK 1.6中出现。

5 CMS收集器(Concurrent Mark-Sweep Collector)

5.1 介绍
复制代码
    		主要为了减少STW时间。
5.2 步骤
复制代码
    	采用标记清除算法:
    	初始标记:初始标记只是标记下GC Root能够关联的对象。速度很快。需要STW
    	并发标记:进行GC Roots Tracing的过程
    	重新标记:修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录
    	并发清除:并发的清除对象。
5.3 优缺点

优点:

复制代码
    	并发收集。低停顿。

缺点:

复制代码
    	对CPU非常敏感,对于CPU大于4个时要求并发收集时使用的线程数不等于1/4。但随着CPU增加而下降。
    可能产生浮动垃圾。因为CMS清理阶段程序还在运行,所以就可能产生新的垃圾,这部分垃圾只能等到下次才能被清理。所以称为浮动垃圾。
    可能产生大量空间碎片。

6 G1收集器(garbage-first)

6.1 介绍
复制代码
    JDK8中主推的收集器。属于CMS的替代品。
    G1收集器时堆中的年轻代和老年代只是逻辑上的概念,实际上把堆(一块连续内存)分为很多Region(分区)每个分区里面又被分为多个卡片(Card)。所以里面可能有很多年轻代和老年代。G1收集器里面多了一个新的概念:humongous(巨型对象)。当对象达到或超过Region一半时称为巨型对象。举行对象独占一个或多个连续的Region。
6.2 步骤:
复制代码
 初始标记:初始标记阶段仅仅只是标记一下GC Roots能直接关联到的对象,并且修改TAMS(Next Top at Mark Start)的值。此过程需要STW,但是耗时很短。
    	2. 并发标记:并发标记阶段是从GC Root开始对堆中对象进行可达性分析,找出存活的对象。此过程耗时可能较长于用户操作并发执行,不需要STW。
    	3. 最终标记。标记因为并发标记时用户执行产生的垃圾。需要STW(也可以并行)
    	4. 筛选回收。对各个Region收回价值和时间进行计算,筛选出符合用户设定的预期回收时间。

十二、 G1收集器和CMS收集器的区别

复制代码
 G1是用在新生代和老年代同时使用。CMS是老年代收集器。
    
    	2. G1是Java8主推的收集器。CMS是java5出现的收集器。
    	
    	3. G1的STW时间可由用户设定,在筛选回收过程“可预测”的想办法满足设定要求。CMS是尽可能的减少STW时间
    	
    	4. G1使用的是标记整理算法,CMS使用的是标记清除算法(所以可能有内存碎片)。
    
    	5. G1回收的流程是初始标记、并发标记、最终标记、筛选回收。CMS的流程是:初始标记、并发标记、重新标记、并发清除。

十三、 请说一下如何进行JVM调优

只有当程序运行过程中的性能指标无法达到预期的生产效率时 ,才有必要对JVM参数进行优化;如果不考虑性能优化的需求 ,则认为当前的JVM配置已经能够确保系统运行的顺畅性和稳定性。因此,在大多数情况下,默认的JVM配置参数能够充分满足系统的需求。**

JVM调优总体思路是:

复制代码
 服务器硬件配置能高则高。
    	
    	2. GC类型要选择好合适的类型。
    	
    	3. 减少GC频率和Full GC的次数。实现较高的吞吐量或较低延迟。
服务器硬件配置能高则高

理论上讲服务器配置越高,服务器的性能越好。JVM性能自然也会更高。但是一般情况下服务器的配置都是固定的。即使不是固定的单机硬件也有瓶颈。
所以一般都是在硬件配置固定的情况下尽量实现使用更少的内存来获取高吞吐量或较低延迟。

GC类型要选择好合适的类型
复制代码
    GC的类型选择也是很重要的事情。
    
    单核或客户端模式选择serial+serial Old 组合即可。
    
    多核64位系统选择Parallel Scavenge+Parallel Old 模式。吞吐量表现非常优秀。
    
    如果对于较低延迟较高的应用可选择G1回收器。
减少GC频率和Full GC次数
减少GC频率
复制代码
    新生代的大小(-Xmn或-XX:NewSize+-XX:MaxNewSize)直接影响GC频率。GC频率太高会导致JVM性能下降。
    
    但是要注意:在堆大小固定的情况下,新生代大小越大,老年代大小越小。
减少Full GC 次数
复制代码
    当老年代满了以后会进行Full GC,所以增加老年代大小可以减少Full GC 的次数。
    
    但是要注意:在堆大小固定的情况下,老年代越大,新生代越小。
    
    所以默认情况下新生代和老年代的比例1:2(-XX:NewRatio=)是Java官方通过不断尝试,不断测试出来的最优比例,在没有特殊要求的情况下可以使用这个比例。
JVM性能明显下降了,如何进行分析
复制代码
    		可以借助各种技术或工具进行观察。
日志
复制代码
    可以通过 -XX:+PrintGCDetails 和 -Xloggc:/data/jvm/gc.log 把GC日志打印出来。
堆栈错误信息
复制代码
    当发生OutOfMemoryError后可以使用-XX:+HeapDumpOnOutOfMemoryError和-XX:HeapDumpPath=path把堆信息转存下来。
线程快照
复制代码
    	jstack pid 打印某个时间点的线程快照。
JDK里面带的其他工具
复制代码
    		jconsole 可视化控制台工具,查看内存等。
    		
    		jmap 堆内存查看工具。
    		
    		jstat 虚拟机状态查看。
    		
    		jvisualvm 和jconsole类似,功能稍微强大一点。
分析完成后如何尝试调优

JVM调优是一项经验性的工作,并没有统一固定的解决方案。不同的项目在同一台服务器上运行时可能采用的调优方案会有显著差异。经过分析发现GC频率过高或Full GC次数过多可以从两个方面进行优化调整:一方面可以通过优化内存分配策略来减轻内存压力;另一方面可以通过适当减少垃圾回收频率来减少资源消耗。

复制代码
 尝试修改新生代,老年代,元空间大小。及新生代中Eden和survivor的比例。但是最好不能全动,需要一点一点增加或减少根据配置后的效果再次修改。
    		个人建议:JVM默认配置都官方经过多少次尝试给出的方案,能不动就不动。
    
    2. 检查代码。很多时候是因为代码问题导致JVM性能下降。
    		(1)	避免创建大对象或大数组。因为大对象和大数组可能直接进入到老年代。增加Full GC 的次数。
    		(2)	避免一次做大量数据操作。可以考虑分页或分批完成。
最后的建议
复制代码
    JVM 并不是我们写的,我们只是使用它。
    
    HotSpot之所以能够调优也是因为它给我们提供了一系列参数选项和工具,只要我们能够清楚JVM内存结构图、GC类型、GC算法、JVM参数就可以根据自己应用实际情况进行尝试调优。
    JVM调优是无法给出固定答案的,因为一点点小变化可能导致调优方案不一样。所以只要具备调优的思想就可以应付所有的情况了。
    
    另外优秀的代码也是调优的有段。好的代码可以大大减少调优的成本。

十四、 全盘负责委托机制

复制代码
    这是类加载的一种机制。即:当一个ClassLoader加载一个类时,除非明确指定,否则这个类所依赖和引用的类也由这个ClassLoader进行加载。

十五、 双亲委派机制

复制代码
    双亲委派机制在代码中体现可以通过java.lang.ClassLoader中的loadClass()方法进行查看。
请添加图片描述

断点查看可以总结出来下面流程,需要注意的是:

复制代码
 委派的过程就是一层一层向上找的过程。只要当前加载器加载过,就不会重新加载。如果没有加载过,会向上寻找是否加载过。
    2. 当加载到Bootstrap ClassLoader后会一层一层的向下判断是否可以进行加载,如果能加载则加载。如果不能加载向下一层寻找下个加载器是否能加载。如果到最后一层都无法加载则报ClassNotFoundException
    .
请添加图片描述

好处:防止多次加载(不再进行额外的加载操作)和防止核心类的串行修改(优先使用Bootstrap classloader**)

十六、 类加载的生命周期/类加载机制

在这里插入图片描述

类加载的过程(类的生命周期)就是

复制代码
 加载:加载.class文件到内存。包含自定义类的字节码文件和依赖及引用的字节码文件。
    		
 链接:包含校验文件、静态变量申请内存赋初值、符号引用变成直接引用。
    		
 初始化:执行静态代码块和静态变量赋值。
    		
 运行。
    		
 卸载。

【MySQL】

十七、 MySQL行列转置/MySQL行列转换(笔试题)

要求:
把下面数据转换

在这里插入图片描述

转换成下面效果

在这里插入图片描述

创建表及测试数据

复制代码
    create table student(
    	id int(11) primary key auto_increment,
    	name varchar(20),
    	subject varchar(20),
    	score double
    );
    
    insert into student values(1,'张三','语文',20);
    insert into student values(2,'张三','数学',30);
    insert into student values(3,'张三','英语',40);
    insert into student values(4,'李四','语文',50);
    insert into student values(5,'李四','数学',60);
    insert into student values(6,'李四','英语',70);

该函数可用于if(条件表达式)语法结构中。当条件满足时,则根据条件计算结果返回相应的结果。其中这些值可能来自数据中的某一列。

复制代码
    select 
    	max(if(subject='语文',score,0)) 语文,
    	max(if(subject='数学',score,0)) 数学,
    	max(if(subject='英语',score,0)) 英语,
    	name 
    from student group by name;

也可以使用case 条件 when 条件取值 then 结果 … else 结果 end

复制代码
    select 
    	max(case subject when '语文' then score else 0 end) 语文,
    	max(case subject when '数学' then score else 0 end)  数学,
    	max(case subject when '英语' then score else 0 end)  英语,
    	name 
    from student group by name;

十八、 请说一下MySQL支持的存储引擎?

MySQL 提供了命令 show engines 以查看其所有支持的存储引擎类型。 在MySQL 中,默认情况下共有 8 种存储引擎被启用。 其中 federated 设置为关闭状态,默认不启用该功能。 此外该系统设计主要用于实现对远程 MySQL 服务上数据资源的有效访问接口。

InnoDB
在所有数据库管理系统中,默认采用的是InnoDB技术。它也是唯一支持事务处理、XA协议以及保存点回滚的技术。

该存储引擎采用MyISAM技术(其已弃用),其主要特点在于显著提高了查询效率;然而,在事务处理和容错性方面存在一定缺陷。

MEMORY

  1. MRG_MYISAM
    旧版本被称为MERGE。简单理解即是对MyISAM表进行特殊封装处理,并对外提供了一个统一的入口点。该接口实现了对外的一体化管理,并降低了应用程序处理MyISAM表时的复杂度和开销。

  2. ARCHIVE 存储引擎主要依靠较小的空间来存储过期且低频访问的历史数据。该表未建立索引,并通过三个特定类型的文件实现数据管理:一个是.frm结构定义文件(.frm),另一个是ARZ格式的数据压缩文件(.ARZ),还有一个是_ARM元信息文件(.ARM)。由于其存储的数据具有特殊属性,在设计上特别限制了对其功能的操作能力——即无法执行删除或修改操作;尽管如此,在实际应用中仍允许插入新数据以及进行查询操作以满足基本需求。

BLACKHOLE

CSV
存储引擎实际上处理的是一个标准格式的数据文件(如常见的 CSV 文件),它不具备索引功能。其主要用途在于满足用户在某些情况下需要从数据库导出报告的需求。由于 CSV 文件是一种广泛使用的、标准化的数据格式(如 Excel、Python 等程序均可读取),我们可以采用以下步骤生成 CSV 报表:首先,在数据库中创建一张专门用于存储报表信息的 CSV 表格后……然后将生成的所有报表数据依次添加到该表格中……最终就能获得所需的形式化 CSV 报表文档。

  1. PERFORMANCE_SCHEMA
    从MySQL 5.6新增的存储引擎。主要用于收集一些系统参数。

官方文档参考地址:https://dev.mysql.com/doc/refman/8.0/en/storage-engines.html

十九、 MySQL支持的日志类型?

MySQL 官方文档中说明MySQL共有六种日志类型

在这里插入图片描述
  1. 错误日志(Error log):包含MySQL运行过程中出现的重要警告信息以及MySQL启动和关闭的关键数据。通过执行命令show variables like ‘%log_error%’;可以获取错误日志的相关信息。
  2. 通用查询日志(General query log):记录客户端连接建立过程及执行的具体语句。通过调用show variables like ‘%general%’可以查看通用查询日志的相关设置,默认情况下该功能未开启。
  3. 二进制日志(Binary log):记录所有更新数据及表结构变动的SQL指令。可以通过执行命令show variables like ‘%log_bin%’;获取相关设置信息。由于该文件采用二进制格式存储,在无法直接用记事本打开的情况下建议使用专用工具进行查看。
  4. 中继日志(Relay log):在MySQL主从复制操作中用于记录数据库数据的变化情况。
  5. 慢查询日志(Slow query log):收集所有超时时间超过指定秒数的查询指令以及不依赖索引的查询操作。通过执行命令show variables like ‘%quer%’;可以获取相关信息设置,并可通过命令log_queries_not_using_indexes进一步确认是否有未使用索引的SQL被记录。
  6. DDL日志(DDL log):记录影响表分区的数据定义操作生成的元数据指令。例如,在执行 ALTER TABLE t3 DROP PARTITION p2 这样的操作时,必须确保完全删除该分区并将其从表分区列表中移除 t3。MySQL 会根据这些元数据操作中间出现的问题来恢复系统状态。

官方文档参考地址: https://dev.mysql.com/doc/refman/8.0/en/server-logs.html

二十、 MySQL 的SQL语句执行流程

在这里插入图片描述
  1. 连接管理与安全验证:MySQL通过连接池机制实现客户端与数据库的安全通信。在客户端建立连接时需完成身份认证、权限验证等安全检查。
  2. 缓存(Cache &Buffer):该系统采用哈希编码技术实现数据持久化存储,在默认模式下关闭查询结果缓存功能以避免性能瓶颈问题。
  3. **解析器(Parser)**主要负责将SQL语句转换为数据结构表示形式,在语法分析过程中会检测关键字是否正确、顺序是否合理以及引号是否匹配等问题。
  4. 预处理器根据语法分析结果进一步核查数据库表结构完整性及字段一致性情况。
  5. **优化器(Optimizer)**采用启发式策略选择最优查询执行方案,在实际运行中会综合考虑各候选方案的成本评估指标。
  6. 执行器接收并处理已优化后的 SQL 执行计划,并将计算结果返回给前端应用使用。
  7. 存储引擎负责数据在物理介质上的读写操作以确保事务处理的原子性完整性及一致性。

MySQL中SQL执行流程文字说明

复制代码
 客户端向服务器端发送SQL命令和连接参数
    
    2. 服务器端连接模块连接并验证
    
    3. 缓存模块解析SQL为Hash并与缓存中Hash表对应。如果有结果直接返回结果,如果没有对应继续向下执行。如果是MySQL 8 是没有查询缓存的。
    
    4. 解析器解析SQL为解析树,检查关键字相关问题,如果出现错误,报SQL解析错误。如果正确,继续执行
    
    5. 预处理器对解析树继续处理检查表、列别名等,处理成功后生成新的解析树。
    
    6. 优化器根据开销自动选择最优执行计划,生成执行计划
    
    7. 执行器执行执行计划,访问存储引擎接口
    
    8. 存储引擎访问物理文件并返回结果
    
    9. 如果开启查询缓存,缓存管理器把结果放入到查询缓存中。
    
    10. 返回结果给客户端

二十一、 什么是执行计划?MySQL中执行计划类型有哪些?

执行计划表:MySQL中可利用EXPLAIN关键字模拟优化器运行SQL语句以生成执行计划表;从高到低排序的等级划分中,默认最低要求为范围级(Range),最高目标为引用级(Reference)。

复制代码
    system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

给定表结构如下:

在这里插入图片描述

建表SQL语句

复制代码
    create table teacher(
    	tid int(11) primary key auto_increment,
    	tname varchar(20)
    );
    insert into teacher values(1,'老师1');
    insert into teacher values(2,'老师2');
    
    create table student(
    	sid int(11) primary key auto_increment,
    	sname varchar(20),
    	stid int(11),
    	constraint  fk_t_s foreign key (stid) references teacher(tid)
    );
    
    insert into student values(1,'学生1',1);

系统:在逻辑表格中仅存在一条记录数据的情况被视为const类型的特殊情形;若物理表格中的单条记录被明确标记为'ALL'状态

复制代码
    explain select * from (select * from student  limit 0,1) t;

该常量确保查询结果仅包含一行数据。由于只有一个记录符合条件,因此它被视作一个常量值。这种查询方式具有极高的效率,在执行时仅访问一次索引。通常将主键字段或唯一索引字段用作唯一条件时的查询操作视为常量行为。

explain select * from student where sid = 1

在查询过程中,前一端每次匹配到后一端的数据量为单一记录(当外键字段存在null值时会转换为引用类型),这相当于在后一端引入了eq_ref类型的字段。需要注意的是,在这种情况下另一端的所有记录都会被包含进去。

复制代码
    explain select * from student s,teacher t where s.stid=t.tid;

ref:查询时,不是使用唯一索引或主键时的其他索引作为查询条件

复制代码
    explain select * from student where stid=1;
    explain select * from student where stid=1 and sname='学生1';

1.4. fulltext: 属于全文索引使用的都被归类为fulltext类型。
1.5: 在外检表查询中使用外键列作为筛选依据,并包括null值的情况。特别注意的是,在这种情况下(即只考虑外键列为null的情况),应将其标记为ref。

复制代码
    explain select * from student where stid=3 or  stid is null;

通过索引合并优化技术实现数据类型的整合,在该方法中仅支持对单一表中的索引查询结果进行整合

复制代码
    alter table student add column (age int(11));
    create unique index bbb on student(age);
    insert into student values (2,'学生2',2,12);
    insert into student values (3,'学生3',2,13);
    explain select * from student where stid=1 or age=1;

1.7. 在实现过程中 eq_ref 时的部分场景涉及包含子查询的情况会被替换成 single-subquery 的处理方式

复制代码
    explain select t.tid in (select s.sid from student s) from teacher t

1.8. index_subquery 子查询中使用了索引,但是没有使用唯一索引。

复制代码
    explain select t.tid in (select s.stid from student s) from teacher t

区间:将此列为条件仅检索相关的一个区间。其中常见的关键字包括between、小于号(<)和in。通常适用于具有索引支持的列。

复制代码
    explain select * from student where sid between 1 and 5

1.10. 该字段采用全索引扫描模式(Index Full Scan)。从技术角度来看 index字段与 ALL字段的主要区别在于 index字段仅遍历其所属的索引树结构 而 NOT EXISTS字段则对应于 NOT EXISTS块的原因是 index文件通常规模较小。尽管两者在功能上均为全表扫描方式 但 index字段是从索引表中进行数据读取 whereas NOT EXISTS字段则直接从磁盘上进行数据读取。

复制代码
    explain select count(*) from student;

1.11. ALL:Full Table Scan,遍历全表以找到匹配的行

复制代码
    explain select * from student where sname='学生';

官方文档参考地址:https://dev.mysql.com/doc/refman/8.0/en/explain-output.html

二十二、 什么是索引,索引的优缺点有哪些?

1. 索引是什么

类似于图书目录的概念,在数据库领域中被定义为一种数据存储结构。该数据结构通过建立索引来实现快速定位所需信息的能力。参考官方文档说明,在处理大量数据(如500万至800万条记录)时 MySQL 的查询性能可能出现显著下降现象;因此,在这种情况下构建索引以提升数据库查询效率具有重要意义。

复制代码
    索引和数据都是存储在.idb文件(InnoDB引擎),默认MySQL使用B+Tree(InnoDB引擎)结构进行存储索引数据,也可以修改为Hash索引。
    B+Tree就是基于B-Tree而来的,主要是非叶子节点只存储键的信息;数据都记录在叶子节点上;所有叶子节点都有都有一个链式指针。
在这里插入图片描述

2. 索引的优点

为什么要创建索引?这是因为,创建索引可以大大提高系统的查询性能。

复制代码
    第一、通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。 
    
    第二、可以大大加快数据的检索速度,这也是创建索引的最主要的原因。 
    
    第三、可以加速表和表之间的连接,特别是在实现数据的完整性方面特别有意义。 
    
    第四、在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。 
    
    第五、通过使用索引,可以在查询的过程中,使用查询优化器,提高系统的性能。

3. 索引的缺点

或许会有疑问:为何在表中为每个列都创建索引并非最佳选择?这种观点虽有一定道理但并不全面。尽管索引具有诸多优势但过度使用反而带来诸多弊端。因为, 增加索引用带来的负面效果不容忽视:

复制代码
    第一、创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。 
    
    第二、索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间。如果要建立聚簇索引,那么需要的空间就会更大。 
    
    第三、当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。

4. 什么样的字段适合创建索引

索引通常建立在数据库表中的特定列之上。 因此,在构建索引时需特别关注哪些列适合构建索_index_、哪些则不适合.index。 通常情况下, 建议为具有以下特征的列构建_index_:

复制代码
    第一、在经常需要搜索的列上,可以加快搜索的速度; 
    
    第二、在作为主键的列上,强制该列的唯一性和组织表中数据的排列结构; 
    
    第三、在经常用在连接的列上,这些列主要是一些外键,可以加快连接的速度; 
    
    第四、在经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的; 
    
    第五、在经常需要排序的列上创建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间; 
    
    第六、在经常使用在WHERE子句中的列上面创建索引,加快条件的判断速度。

创建索引通常基于select语句中的where子句来构建。例如,在select语句中使用where子句指定为f1f2时,则需要考虑以下情况:如果我们单独为f1f2字段创建索引,则没有实际意义;只有当我们在f1f2两个字段同时创建索引时才具有实际意义。

5. 什么样的字段不适合创建索引:

同样地,在某些情况下不建议为字段创建索引。通常认为,在数据库设计中不应该为那些不具备特定需求的字段创建索引的有以下特征:

复制代码
    第一,对于那些在查询中很少使用或者参考的列不应该创建索引。这是因为,既然这些列很少使用到,因此有索引或者无索引,并不能提高查询速度。相反,由于增加了索引,反而降低了系统的维护速度和增大了空间需求。 
    
    第二,对于那些只有很少数据值的列也不应该增加索引。这是因为,由于这些列的取值很少,例如人事表的性别列,在查询的结果中,结果集的数据行占了表中数据行的很大比例,即需要在表中搜索的数据行的比例很大。增加索引,并不能明显加快检索速度。 
    
    第三,对于那些定义为text, image和bit数据类型的列不应该增加索引。这是因为,这些列的数据量要么相当大,要么取值很少。 
    
    第四,当修改性能远远大于检索性能时,不应该创建索引。这是因为,修改性能和检索性能是互相矛盾的。当增加索引时,会提高检索性能,但是会降低修改性能。当减少索引时,会提高修改性能,降低检索性能。因此,当修改性能远远大于检索性能时,不应该创建索引。

二十三、 索引分类

可通过show index from 表名 检索其包含的所有索引。
根据给定的表结构 测试其索引情况。

复制代码
    create table demo(
    	id int(11) primary key,
    	col1 int,
    	col2 int,
    	col3 varchar(20)
    );

1. 单列索引。就是给一个列添加索引。

复制代码
1 普通索引:不考虑过多情况,主要是为了让查询更快一些。
    	语法:create index 索引名 on 表名(列)
    		例如:create index index1 on demo(col1);
    
    1.2 唯一索引:列中值不可以重复,可以是null
    	语法:create unique index 索引名 on 表名(列)
    		例如:create unique index index2 on demo(col2);
    
    1.3 主键索引:列中值不可以重复,又不可以为null。简单点理解不允许为null的列添加上唯一索引就是主键索引。

2. 组合索引。

为表中的至少两个或更多列添加索引。然而,在操作过程中需要确保以最左边的方式进行前缀操作(即满足最左前缀的要求)。其效果类似于同时创建多个独立的索引,并且通常建议将使用频率最高的字段放置在左侧位置以便于快速访问

复制代码
    		语法:create index 索引名 on 表名(列1,列2...)
    
    	例如:create index index3 on demo(col1,col2,col3)

此时等价于生成一系列涉及列组合的索引,包括col1、col1-col2、col1-col2-col3以及col1-col3。通过执行计划查询来获取相关信息。若查询结果类型为ref,则表示系统已启用相应索引;若查询结果类型为index,则说明未找到合适的索引。

3. 全文索引(full-text)

在Char、VarChar和Text等字段中仅能实现全文索引功能。这种索引方法通过提取列中的关键词来构建索引结构。例如,在尚学堂学习的场景中,当执行搜索操作时可以定位到对应的学习内容。因此,在涉及包含LIKE运算符的查询时该方法特别适用。然而需要注意的是,该方案仅能有效应对带有模糊性(如xxxx%)的查询效率问题。

复制代码
    	语法:create fulltext index 索引名 on 表名(列)

二十四、 请介绍下什么是全文索引

MySQL自3.23.23版起推出全文检索功能,并于直至MySQL 5.6版本之前该功能仅限于MyISAM表实现。随着 MySQL 5.7及之后版本的引入,InnoDB表结构同样获得了这一功能的支持;此外,在MySQL 8版本中这一特性进一步得到了InnoDB表结构的支持;该功能可以从基于CHAR、VARCHAR或TEXT列属性设计的数据库表中作为CREATE TABLE语句的一部分实现,并且可在后续操作中通过ALTER TABLE或CREATE INDEX进一步实现。

对于大量数据集而言,在不带FULLTEXT索引的情况下将资料插入表中并随后建立索引相比直接在已有FULLTEXT索引中插入资料其效率更高;不过需要注意的是对于大数据量级的数据表一次性生成全文索引不仅会耗费大量时间和内存资源而且这种操作并不是十分推荐的做法。

全文索引对于中文的支持效果不佳,在搜索过程中仅支持基于最左侧对照的中文匹配方式。而英文则可以在整个文本中进行更灵活的匹配。

给定表结构,演示全文索引的使用

复制代码
    create table tb_fulltext(
    	id int(11) primary key auto_increment,
    	name varchar(100),
    address varchar(200)
    );

1. 创建索引

有三种方式创建全文索引。

复制代码
    CREATE FULLTEXT INDEX index_name ON table_name(column(length))
    
    TABLE table_name ADD FULLTEXT index_name( column)
    
    CREATE TABLE table_name (id int not null auto_increment,title varchar(30) ,PRIMARY KEY(id) ,  FULLTEXT  index_name (title))

例如:create fulltext index ft on tb_fulltext(name,address);

1.2 创建后的全文索引需要配合match(列,列) against(‘内容’)使用。

1.2.1 在match操作中涉及的字段应与创建全文索引时选定的一致。举例而言,在构建包含id和name两个字段的全文索引时, 若试图对name字段进行match操作, 则需分别为name字段单独建立一个专门的全文索引

复制代码
    insert into tb_fulltext values(1,'tony','beijing yizhuang');
    
    insert into tb_fulltext values(2,'kevin','beijing yizhuang jingkaiqu');
    
    insert into tb_fulltext values(3,'jordan','beijing daxing');
    
    explain select * from tb_fulltext where match(name,address) against('yizhuang');
    
    select * from tb_fulltext where match(name) against('yizhuang');// 执行报错

1.2.2 against中内容有三种模式

复制代码
    	自然语言模式:IN NATURAL LANGUAGE MODE
    	布尔模式:IN BOOLEAN MODE
    	查询扩展模式:WITH QUERY EXPANSION

1.3 自然语言模式:在拆分过程中提取出的关键信息必须与原始关键词精确对应。如北京这一关键词,则需严格按照"北京"来进行搜索而非使用"bei"等变体进行检索。

复制代码
    drop index ft on tb_fulltext;
    create fulltext index ft on tb_fulltext(address);
    select * from tb_fulltext where match(address) against('daxing');

1.4 Boolean模式:包含特殊符号。即使对未建立全文索引的字段也能执行搜索操作,但其搜索速度较慢。在执行查询操作时只能从左侧位置开始进行匹配操作,请注意以下示例:北京昌平,则无法按照昌平这一区域进行搜索

复制代码
     	+   一定要有(不含有该关键词的数据条均被忽略)。 
    	
    	-   不可以有(排除指定关键词,含有该关键词的均被忽略)。 
    	
    	>   提高该条匹配数据的权重值。 
    	
    	<   降低该条匹配数据的权重值。
    	
    	~   将其相关性由正转负,表示拥有该字会降低相关性(但不像 - 将之排除),只是排在较后面权重值降低。 
    	
    	*   万用字,不像其他语法放在前面,这个要接在字符串后面。 因为搜索时只能满足最左前缀搜索like ‘内容%’,不能实现类似like ‘%内容%’这种。
    	
    	" " 用双引号将一段句子包起来表示要完全相符,不可拆字。
复制代码
    # daxin* 可以搜索到内容。 daxin 不能搜索到内容
    
    explain select * from tb_fulltext where match(address) against('daxin*' in boolean mode);

1.5 查询扩展。查询时进行扩展查询,发现和条件有关系的内容都查询出来

复制代码
    insert into tb_fulltext values(4,'kelvin','hebei baoding');
    
    # 结果查询到三行,id为4的没有被查询出来。因为daxing相关的词条有beijing、所有包含beijing的都被查询出来了。
    
    explain select * from tb_fulltext where match(address) against('daxing' with query expansion);

中文拆词器ngram

因为没有空格 MySQL 自版本5.7.6起内置了ngram中英文分词插件 建议配置为将整个输入按照指定长度进行分割

在my.ini路径下的mysqld项中,请您配置一个参数用于设定拆分长度,请按照个人需求进行设置。

复制代码
    	ngram_token_size=2
在这里插入图片描述
  1. 给address创建全文索引。注意后面的with parser ngram
复制代码
    create fulltext index index3 on ft(address)  with parser ngram;

二十五、 MySQL优化?

MySQL的优化主要可以从两个维度着手:分别从硬件层面(Hardware Level)和数据库层面(Database Level)进行优化

硬件级别瓶颈包含如下的几个方面

1 磁盘寻找(Disk seeks):
复制代码
    磁盘需要一段时间才能找到一段数据。对于现代磁盘来说,这种平均时间通常低于10ms,因此理论每秒100次寻找。这一次用新的磁盘缓慢地改进,并且对于单个表是很难优化的。优化查找时间的方法是将数据分发到一个以上的磁盘上。
2 磁盘读写(Disk reading and writing):
复制代码
    当磁盘处于正确位置时,我们需要读取或写入数据。使用现代磁盘,一个磁盘可以提供至少10到20Mb/s的吞吐量。这比磁盘寻找更容易优化,因为您可以从多个磁盘并行读取。
3 CPU(CPU cycles):
复制代码
    当数据在主存储器中时,我们必须处理它以得到结果。与内存量相比,具有大数据库量的表是最常见的限制因素。但是对于小表来说,速度通常不是问题。
4 存储带宽(Memory bandwidth):
复制代码
    当CPU需要比CPU缓存中更多的数据时,主存储器带宽成为瓶颈。对于大多数系统来说,这是一个不常见的瓶颈,但也是一个需要注意的点

数据库级别:导致数据库应用最高效的因素是它的基本设计

复制代码
    1 表结构是否正确(Are the tables structured properly?):
    	特别的是列是否具有正确的类型,和表中列的个数是否正确。
    	例如:对于执行频繁更新的应用通常设计更多表,每个表的列并不多。而对于需要进行大量分析的应用通常设计更少的表,而每个表的列更多一些。
    
    2 正确的设置索引达到查询高效(Are the right indexes in place to make queries efficient?):
    	需要考虑的是什么SQL会导致索引无效,什么情况会让查询效率更高
    
    3 对于不同情况选择不同的存储引擎(Are you using the appropriate storage engine for each table, and taking advantage of the strengths and features of each storage engine you use? )
    	选择不同的存储引擎对性能和可伸缩性具有较大的影响
    
    4 每张表是否具有适当的行格式(Does each table use an appropriate row format?):
    	主要取决于适当的存储引擎。压缩表可以占用更低的磁盘空间和更少的I/O操作。压缩表适用于InnoDB和MyISM存储引擎。
    
    5 应用程序是否使用适当的锁策略(Does the application use an appropriate locking strategy? ):
    	在具有高并发、分布式应用程序中,选择适当的锁策略以保证数据的共享性和特定的情况下独占数据。
    	InnoDB存储引擎在不需要我们参与下能处理大部分锁问题,允许数据库实现更好的并发性,减少代码调优量。
    
    6 所有缓存区域使用的大小是否都正确(Are all memory areas used for caching sized correctly?):
    	配置的原则是缓存区域大到足以容纳所有频繁访问的数据,但又不能太大,否则导致过量占用物理内存而导致分页。
    	一般情况下需要配置InnoDB的缓冲池、MyISAM密钥缓存和MySQL的查询缓存

优化的哲学

复制代码
    	注意:优化有风险,涉足需谨慎!

优化可能带来的问题

复制代码
    	1 优化不总是对一个单纯的环境进行,还很可能是一个复杂的已投产的系统。
    	
    	2 优化手段本来就有很大的风险,只不过你没能力意识到和预见到!
    	
    	3 任何的技术可以解决一个问题,但必然存在带来一个问题的风险!
    	
    	4 对于优化来说解决问题而带来的问题,控制在可接受的范围内才是有成果

结论:保持现状或出现更差的情况都是失败!

优化的需求

复制代码
    	1 稳定性和业务可持续性,通常比性能更重要!
    	
    	2 优化不可避免涉及到变更,变更就有风险!
    	
    	3 优化使性能变好,维持和变差是等概率事件!
    	
    	4 切记优化,应该是各部门协同,共同参与的工作,任何单一部门都不能对数据库进行优化!

结论:所以优化工作,是由业务需要驱使的!!

优化由谁参与

在实施数据库优化方案时, 应包括以下相关人员: 数据库管理员, 业务部门代表, 应用程序架构师, 应用程序设计人员, 应用程序开发人员, 硬件及系统管理员, 存储管理员等其他相关领域人员

优化的顺序

优化选择:

复制代码
    	优化成本:硬件>系统配置>数据库表结构>SQL及索引
    	
    	优化效果:硬件<系统配置<数据库表结构<SQL及索引

访问 official documentation 的位置: https://dev.mysql.com/doc/refman/8.0/en/optimize-overview.html

二十六、 SQL优化

表设计方面:

1、 NULL值

在旧版本中存在NULL值的字段无法自动生成唯一索引,在MySQL 5.7版开始支持自动生成唯一索引功能。然而这可能会导致不可预见的问题。
建议在创建表格时为各字段添加NOT NULL约束或设置默认值以避免潜在问题。

2、 表格字段类型选择

优先考虑采用数值类型字段;对于仅包含数值数据的字段,则应避免将其定义为字符型;这样做不仅能够提升查询效率还能节省存储空间。具体原因在于数据库引擎在执行查询操作时会对每个记录进行逐项比对;而数值类型只需进行一次对比即可完成匹配。

推荐在数据库设计中优先考虑使用varchar替代char类型。由于varchar具有可变长度的特点,在多个方面提供了优势:其一,能够显著减少占用的空间从而实现资源的有效优化利用;其二,在执行查询操作时,在较短的数据字段中进行搜索能显著提高数据检索的速度和效率。

3、 组合索引使用

当设置索引字段作为查询条件时,在处理复合型数据结构的情况下(即为复合索引),只有当第一个指定的字段被包含在查询中时才能确保系统正确识别并利用这些多层数据关联。如果不满足这一前提,则可能导致数据引用错误;此外,在构建数据库表结构时,请尽量按照数据层次关系来排列各字段的位置。

4、避免全表扫描

提升查询效率时应尽量避免不必要的全表扫描操作,并应在涉及 WHERE 和 ORDER BY 运算的列上优先建立索引以提高查询性能

select 从句部分

5、 查询语法中的字段

避免使用带有星号的通用查询,并直接指定所需的具体字段。具体来说,在所有的查询语句中应当明确列出所需的所有字段名称(例如:user_id, password, name),而不是使用带有星号通配符(*)的形式。这种做法有助于提高查询效率并减少不必要的数据读取。避免返回那些无需的数据项

where 从句部分

6、避免负向条件

建议尽量避免在 WHERE 子句中采用 != 或 <> 操作符;如果不这样做的话,引擎将不再利用索引而执行全表扫描。

建议尽量避免在 WHERE 子句中采用 != 或 <> 操作符;如果不这样做的话,引擎将不再利用索引而执行全表扫描。

负向条件有:

复制代码
    !=、<>、not in、not exists、not like 等
    explain select * from teacher where address !='aa';
复制代码
1.	避免使用or逻辑
    应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:
    select id from t where num=10 or num=20
    
    可以这样查询:
    	select id from t where num=10
    	union all	
    	select id from t where num=20	
    在MySQL5.7 开始or已经可以触发索引了,老版本不可以。
    
    1.2.	慎用in和not in逻辑
    	in可以代替union all操作,虽然对CPU性能稍微增加一些,但是也可以忽略不计了。
    	
    	in 和 not in 也要慎用,否则会导致全表扫描,如:
    	select id from t1 where num in(select id from t2 where id > 10)
    	
    	此时外层查询会全表扫描,不使用索引。可以修改为:
    	
    	select id from t1,(select id from t1 where id > 10)t2 where t1.id = t2.id
    	
    	此时索引被使用,可以明显提升查询效率。
7、 避免查询条件中字段计算

最好避免在 where 子句中对字段进行复杂的表达式操作, 因为这可能导致数据库引擎无法利用索引信息而执行全表扫描. 例如, 在查询中使用复杂的计算或函数会导致这样的问题.

复制代码
    			select id from t where num/2=100

应改为:

复制代码
    		select id from t where num=100*2
8、 避免查询条件中对字段进行函数操作

必须避免在WHERE子句中的字段执行函数运算, 这将导致数据库引擎不再利用索引信息而转而执行全表范围内的查询. 如:

复制代码
    select id from t where substring(name,1,3)='abc'--name以abc开头的id

应改为:

复制代码
    select id from t where name like 'abc%'
9 、WHERE子句“=”左边注意点
复制代码
    不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
10、 注意模糊查询

下面的查询也将导致全表扫描:

复制代码
    select id from t where name like '%abc%'
复制代码
    模糊查询如果是必要条件时,可以使用select id from t where name like 'abc%'来实现模糊查询,此时索引将被使用。
    如果头匹配是必要逻辑,建议使用全文搜索引擎(Elastic search、Lucene、Solr等)。

子查询方面:

11、 exists

很多时候用 exists 代替 in 是一个好的选择:

复制代码
    select num from a where num in(select num from b)

用下面的语句替换:

复制代码
    select num from a where exists(select 1 from b where num=a.num)

其他查询方面

12、 索引也可能失效
复制代码
    并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。
13、 命令选择
复制代码
    不使用*、尽量不使用union,union all等关键字、尽量不使用or关键字、尽量使用等值判断。

连接方法

表连接建议不超过5个。如果超过5个,则考虑表格的设计。(互联网应用中)

表连接方式采用外联连接更优于内联连接。
外链接存在基础数据情况可用此方法实现。例如,在A与B进行左链接操作时(即A left join B),其中A表中的键字段与B表具有对应关系的数据行被称为基础数据(即基础数据为A)。
对于无对应关系的数据进行内链接操作时(即A inner join B),系统会先通过笛卡尔积生成所有组合后再通过指定条件筛选出符合条件的结果集。

在处理大规模的数据表格时,在执行分页查询操作可能会遇到性能问题;因此,在页面数量超过一定阈值的情况下,建议采用子查询来辅助实现 pagination功能。通过使用 LIMIT 子句限制结果集大小以提高性能,并确保与 IN 子句结合使用以精确控制 pagination 的起始位置和范围

官方文档: https://dev.mysql.com/doc/refman/8.0/en/statement-optimization.html

二十七、 索引优化

由于大量使用索引可能会导致潜在风险和可能导致的问题,在某些情况下需要权衡利弊。尽管索引显著提升了查询效率的同时也可能会导致更新操作的速度减慢。当插入、更新和删除操作的频率超过查询频率时建议避免过度依赖索引以防止可能出现的问题。此外创建索引将占用专门用于存储索引信息的空间这可能对磁盘空间造成一定压力尤其是当处理大量复杂字段时需谨慎考虑最优配置方案以确保系统性能得到充分保障

1. 使用短索引(前缀索引)

复制代码
    对串列进行索引,如果可能应该指定一个前缀长度。例如,如果有一个CHAR(255)的列,如果在前10个或20个字符内,多数值是惟一的,那么就不要对整个列进行索引。短索引不仅可以提高查询速度而且可以节省磁盘空间和I/O操作。
    CREATE  INDEX  index_name  ON  table_name (column(length))

2. 索引列排序

复制代码
    MySQL查询只使用一个索引,因此如果where子句中已经使用了索引的话,那么order by中的列是不会使用索引的。因此数据库默认排序可以符合要求的情况下不要使用排序操作;尽量不要包含多个列的排序,如果需要最好给这些列创建复合索引。

3. like语句操作

复制代码
    一般情况下不鼓励使用like操作,如果非使用不可,如何使用也是一个问题。like “%aaa%” 不会使用索引,而like “aaa%”(非前导模糊查询)可以使用索引。
    使用后,优化到range级别
    explain select * from teacher where address like '%oracle%';

4. 不要在列上进行运算

在传统数据库系统环境中进行查询时,在某些情况下(例如)使用YEAR(adddate) < 2007作为过滤条件会导致每年重复运算(即每年都会重新建立索引),从而使得索引失效并引发全表扫描问题)。为了避免这些问题,在新的数据库架构中我们建议将类似的操作直接嵌入到业务逻辑层(例如应用层面),以确保计算过程更加高效和稳定

5. 范围列可以使用索引

常见的范围条件包括:**小于号(<)、小于等于号(<=)、大于号(>)、大于等于号(>=)以及介于两者之间的关系运算符(between)。其中,在涉及索引的范围内最多只能应用一个索引字段。当处理多个查询条件时,在涉及索引的范围内最多只能应用一个索引字段。因此,在WHERE子句中应优先将最重要的查询条件放置在首位。

复制代码
    alter table teacher add column (age int(3));
    alter table teacher add column (weight int(3));
    select * from teacher;
    update teacher set age=10,weight=90 where id = 1;
    update teacher set age=20,weight=100 where id = 2;
    update teacher set age=30,weight=110 where id = 3;
    
    create index age_index on teacher(age);
    create index weight_index on teacher(weight);
    
    explain select * from teacher where age between 10 and 20 and weight between 90 and 100;

5.类型转换会导致索引无效

复制代码
    当列是文本类型时,把数值类型当作列的条件会弃用索引
    explain select * from teacher where name = 20;

6. 索引总结

总结一下,MySQL仅针对以下操作符建立索引:< ,<=,=,>,>=,between,in,以及某些特定情况下使用的like(仅在不以%或_开头的情形下)。理论上每张表最多可创建16个索引。建议避免将一个表的索引数量超过6个,这样做的好处是可以提高查询效率和优化数据库性能。

官方文档参考地址:https://dev.mysql.com/doc/refman/8.0/en/optimization-indexes.html

二十八、 说一下MySQL中锁的分类及SQL语句

复制代码
    **MySQL中锁按照不同分类标准有多种分类。**

按照锁的级别分为 :共享锁、排它锁。这也是最根本的分类标准。

根据目标将按照 lock 的目标将内容进行分类:分为 row-level lock 和 table-level lock。其中整个 table 所受到的约束即为 table-level lock;单个或多个 row 所受到的约束即为 row-level lock。

根据是否支持自增加锁进行分类**:自增加lock和显式lock两类。MySQL InnoDB引擎会对插入、删除和更新操作自动生成排他锁定,在这种情况下无需额外配置;然而,在Select操作中,默认是不会生成锁定机制的;但可通过相应的命令来生成锁定机制以实现所需的控制功能。

由于对锁进行划分时会存在多种可能性,因此同一个锁操作可能会既是共享 lock 又是行级 lock,也可能是显示 lock。

共享锁又称读锁 ,表示在读取数据时不允许其他事务对数据进行修改。

复制代码
    		SQL语句:
    				select .... lock in share mode;

排它锁又称独占锁 。在一个事务操作时,其他事务不允许操作数据。

复制代码
    		SQL:
    			select .... for update;

二十九、 MySQL支持的表连接类型有几种?

MySQL支持的表连接类型主要包括三种:内联结、左外联结和右外联结。与之相比,在Oracle数据库中提供了全外联结功能,而MySQL数据库则不具备这一功能。

复制代码
    create table teacher(
    	tid int(11) primary key,
    	tname varchar(20)
    );
    
    insert into teacher values(1,'老师1');
    insert into teacher values(2,'老师2');
    insert into teacher values(3,'老师3');
    
    create table student(
    	sid int(11) primary key,
    	sname varchar(20),
    	stid int(11)
    );
    
    insert into student values(1,'学生1',1);
    insert into student values(2,'学生2',1);
    insert into student values(3,'学生3',2);
    insert into student values(4,'学生4',null);

笛卡尔积:多个表连接查询产生的结果不会剔除任何未关联的数据。例如,在表A中有N条数据,在表B中有M条数据,则其连接结果共有N \times M条。

复制代码
    		select * from student s,teacher t;

内连接通常采用inner join操作来实现。该系统会检索出两个相关联的数据集合。无需担心无关数据的出现。另外一种方法是直接进行多表连接操作。

复制代码
    select * from student s inner join teacher t on s.stid=t.tid;
    
    select * from student s,teacher t where s.stid=t.tid;

左外连接使用left outer join的方式实现,在这种情况下意味着即使左表存在未关联的数据也会被查询出来。在left outer join中左侧的表被称为左表

复制代码
    select * from student s left outer join teacher t on s.stid=t.tid;
    
    select * from teacher t left outer join student s on s.stid=t.tid;

右侧外连接被定义为right outer join操作。该操作表示即使右表中存在未被左表完整关联的数据项也会被查询出来并包含在结果集中。在left outer join操作中,默认情况下会返回左侧表格的所有记录并将它们与右侧表格中可能存在但并未被左侧完整匹配的数据项进行联合查询

复制代码
    select * from student s right outer join teacher t on s.stid=t.tid;
    
    select * from teacher t right outer join student s on s.stid=t.tid;

三十、 请说一下count(*),count(列),count(1)的区别

从InnoDB引擎的角度来看,count(*)与count(1)在性能表现上并无显著差异。它们均会自动搜索主键列或唯一约束索引,在统计过程中,两者都会计算包含null值的行。因此最终结果都会返回表中数据行的数量。

在这里插入图片描述

而count(列) 统计列中包含多少行数据,不包含NULL值。

统计说明:

复制代码
    在InnoDB引擎中count(*)和count(1)性能没有什么差别
    count(列)需要看列和count(*)优化后的列情况,如果count(列)使用了非索引列,而表中包含索引列则count(*)更快。
    如果count(列)和count(*)优化后的是同一个列则性能没有什么差别。如果表中没有索引则count(列)和count(*)性能也没有什么差别。

三十一、 聚集索引和非聚集索引的区别

从核心区别来看,最明显的不同点就在于是否存在伴随的数据一同存储.我们称这种类型为'聚簇指针'的原因在于其与实际的数据内容共同存在于叶子节点中.在叶子节点中同时存储了对应的指针信息以及实际的数据内容.由于在数据库设计中,默认采用的是行优先的方式进行组织,在这种情况下进行查询操作时能够迅速定位所需记录的速度较快.需要注意的是,在这种组织下进行新增操作时会涉及重新排序这些操作可能会对性能产生影响.记住一点:主键字段即为聚簇键.

非聚集索引 被称为"非聚集"类型, 其原因在于其叶子节点中仅包含主键和相关的索引列. 在执行非聚集索引查询时, 系统会通过主键来定位所需的数据. 具体来说, 在叶子节点上使用主键进行数据查找的过程被称为回表.

【Spring Security】

三十二、 Spring Security 认证流程(原理)

复制代码
 客户端发起URL请求
    
    2. 判断URL是否是登录URL,如果不是登录URL执行十一个Filter内置Filter功能实现。所有Filter都是通过FilterChainProxy进行协调。最终跳转到登录页面。
    
    3. 如果是登录URL,依然执行内置Filter链,其中UsernamePasswordAuthenticationFilter负责获取到请求中的用户名和密码,并把用户名和密码封装到UsernamePasswordAuthenticationToken中。
    
    4. 通过AuthenticationManager调用自定义登录逻辑UserDetailsService
    
    5. 把返回结果UserDetails交给AuthenticationProvider进行处理
    
    
    6. 根据处理结果调用成功处理器或失败处理器显示最终结果。
在这里插入图片描述

流程图

在这里插入图片描述

【Spring Boot】

三十三、 Spring Boot配置文件支持路径和优先级(面试题)

Spring Boot包含一个名为application的全局配置文件,并支持Properties格式及YML格式。

在这里插入图片描述

配置文件加载顺序

1 不同格式的加载顺序
复制代码
    如果同一个目录下,有application.yml也有application.properties,默认先读取application.properties。
    
    如果同一个配置属性,在多个配置文件都配置了,默认使用第1个读取到的,后面读取的不覆盖前面读取到的。
2 不同位置的加载顺序
复制代码
    		config/application.properties
    		
    		config/application.yml
    		
    		application.properties
    		
    		application.yml
    		
    		resources/config/application.properties
    		
    		resources/config/application.yml
    		
    		resources/application.properties
    		
    		resources/application.yml

三十四、 Spring Boot中的注解

在Spring Boot项目中,对包括Spring framework、Spring MVC、MyBatis在内的多种注解都进行了支持。而纯Spring Boot自身常用的注解种类数量相对较少。

复制代码
    	@SpringBootApplication 启动器注解。
    	
    	@SpringBootTest 测试类注解。

下面的注解并不是特别常用,但是在自定义启动器时需要使用的注解

复制代码
    		@EnableAutoConfiguration 启动自动化配置
    		
    		@ConditionalOnBean 容器中存在指定Bean时才会去创建Bean
    		
    		@ConditionalOnClass 当前项目如果能找到指定类时才会创建这个类的Bean
    		
    		@ConditionalOnSingleCandidate 当容器中存在一个时或者虽然有多个但是指定首选Bean
    		
    		@EnableConfigurationProperties 启动配置属性类
    		
    		@AutoConfigureAfter 在加载配置类后在加载当前类
    		
    		@ConfigurationProperties 定义属性前缀
    		
    		@ConditionalOnMissingBean 只有当容器中不存在指定Bean时才会去实例化。

除此以外都是Spring 框架和Spring MVC框架以及MyBatis框架的注解了。

三十五、 Spring Boot 执行流程/Spring Boot 原理

Spring Boot项目的执行流程可以通过打断点作为考察项目执行路径的方式进行查看。随后将启动SpringApplication的构造过程

在这里插入图片描述

run方法中代码 含义如下

在这里插入图片描述

执行流程文字说明:

复制代码
    	SpringBoot项目通过SpringApplication.run()作为启动入口
    		
 进入SpringApplication构造方法,从META-INF/spring.factories中读取初始化和监听器,并记录主类。
    		
 创建计时器StopWatch
    		
 声明上下文对象ConfigurableApplicationContext
    		
 启动Headless服务器,设置系统属性
    		
 创建并启动运行时监听器
    		
 准备环境Environment并打印Banner
    		
 上下文环境准备及刷新,此时Tomcat被启动。
    		
 停止计时器,并打印启动时间。
    		
 运行时监听器启动。
    		
 返回上下文对象。

三十六、 Spring Boot自动化配置原理

在使用Spring官方产品时,默认情况下所有配置均通过$spring_boot_autoconfigure进行管理。对于依赖第三方框架的产品,在配置阶段需自行编写代码以实现自动化的配置管理。

复制代码
    以mybatis-spring-boot-starter举例,分析得出重要角色:
    
    mybatis-spring-boot-autoconfigure项目包含:
    	MyBatisAutoConfiguration自动化配置类
    	MyBatisProperties 支持的配置属性

查看mybatis-spring-boot-starter依赖的自动化配置

每个启动器都会有个自动化配置的依赖

在这里插入图片描述

查看spring.factories文件

复制代码
    在@SpringBootAutoConfiguration注解中包含了
    @EnableAutoConfiguration注解。在SpringBoot项目启动时会自动加载META-INF/spring.factories中EnableAutoConfiguration内容

该系统位于mybatis-spring-boot-autoconfigure中的/META-INF/spring.factories目录内,并开发了自动化配置系统

在这里插入图片描述

查看MyBatisAutoConfiguration

上面配置了自动化配置条件

在这里插入图片描述

查看MybatisProperties

在这里插入图片描述

MyBatisAutoConfiguration中提供了两个Bean

SqlSessionFactory的Bean

在这里插入图片描述

SqlSessionTemplate的Bean

在这里插入图片描述

提供了两个static的内部类

AuConfigure AutoSetUp Mapper Scanner Registrar 和 Mapper Not Found Configuration

提供了MapperScannerConfigurer扫描注解

在这里插入图片描述

自动装配文字说明:

复制代码
启动类中@SpringBootApplication中包含@EnableAutoConfiguration
    
    2.加载项目依赖的META-INF/spring.factories中EnableAutoConfiguration对应类,进行实例化。
    
    3. 实例化自动化配置类时会同时加载属性类,从配置文件中读取。
    
    4. 生效自动化配置,把对应的Bean放入到Spring容器中。

三十七、 Spring Boot如何自定义启动器

自定义SpringBoot启动器时一般包含两块

复制代码
    xxx-spring-boot-autoconfigure:里面编写自动化配置相关内容。
    xxx-spring-boot-starter:只是一个依赖,依赖xxx-spring-boot-autoconfigure

请定义bjsxt-spring-boot-starter类,并支持通过配置文件进行属性设置。具体来说,请按照如下方式进行设置:

新建bjsxt-spring-boot-autoconfigure项目

添加依赖

在pom.xml中引入支持自动化配置流程的依赖项,并由spring-boot-configuration-processor用于协助管理xml和properties配置。

复制代码
    <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.5</version>
    </parent>
    <dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-autoconfigure</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>
    </dependencies>
新建BjsxtProperties

创建一个名为BjsxtProperties的JDK项目根目录。
在@ConfigurationProperties注解中设置前缀字段名用于获取配置文件中的属性前缀。

复制代码
    @ConfigurationProperties(prefix = "bjsxt")
    public class BjsxtProperties {
    private String name;
    private int age;
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public int getAge() {
        return age;
    }
    
    public void setAge(int age) {
        this.age = age;
    }
    }

配置自动化类

在resources中下新建META-INF/spring.factories

复制代码
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.bjsxt.starter.BjsxtAutoConfiguration

安装

对bjsxt-spring-boot-autoconfigure进行本地install
maven install

在这里插入图片描述

建立bjsxt-spring-boot-starter此项目仅依赖于自动化的配置设置 使得其他项目的开发能够基于当前项目的配置运行 并严格遵循Spring Boot启动器的标准流程

复制代码
    <dependencies>
    <dependency>
        <groupId>com.bjsxt</groupId>
        <artifactId>bjsxt-spring-boot-autoconfigure</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    </dependencies>

对当前项目进行install 。
依然是选择maven --> lifecycle --> install

新建测试项目

所有新创建的项目必须依赖bjsxt-spring-boot-starter库。为了验证系统的功能模块是否正确实现,在单元测试阶段执行相应的测试用例。

复制代码
    @SpringBootTest
    class DemoApplicationTests {
      @Autowired
      private BjsxtProperties bjsxtProperties;
      @Test
      void contextLoads() {
    System.out.println(bjsxtProperties.getName());
    System.out.println(bjsxtProperties.getAge());
      }
    }

自定义启动器的流程 文字说明:

复制代码
 创建自动化配置项目 xxx-spring-boot-autoconfigure
    		
 在项目中提供自定义属性类,添加自定义属性。在类上添加@ConfigurationProperties
    		
 再创建一个自动化配置类。添加@Configuration、@EnableConfigrationProperties
    		
 在类路径下添加META-INF/spring.factories设置自动化配置类的全限定路径。
    		
 打包项目后新建xxx-spring-boot-starter,并且在启动器项目中添加上自动化配置项目的依赖。

三十八、 Spring Boot 支持的配置文件类型及优先级

Spring Boot支持三种格式配置文件:

复制代码
    	.properties 格式为key=value形式
    	
    	.yml 格式为key: value 采用树状结构
    	
    	 yaml 格式和yml 格式相同。

优先级: .properties > yml> .yaml

【Maven】

三十九、 请说一下POM模型

复制代码
    POM(Project Object Model)即项目对象模型,在Maven中使用pom.xml进行体现,声明式配置。
    可以在pom.xml中可以配置项目与项目的关系,插件等信息。
    POM模型中认为每个项目都是一个对象,项目和项目之间可以存在关系:依赖、继承、聚合。
    通过坐标引用其他项目的jar。如果引用时本地仓库不存在,会从中央仓库中下载到本地仓库。

四十、 Maven中项目与项目的关系

复制代码
    依赖:当一个项目需要使用另一个项目的类时可以设置为依赖关系。由于Maven具有依赖传递性,有时也通过依赖替代继承来获取到另一个项目的pom.xml(Spring Cloud)。
    继承:当一个项目需要继承另一个项目的pom.xml时,可以设置为继承关系。Maven中只支持单继承。
    聚合:聚合是在一个窗口中通过一个父项目管理整个项目的pom.xml,方便进行版本管理、依赖管理等。

四十一、 继承和聚合的区别

复制代码
    继承关系是指子项目可以继承父项目的pom.xml中配置。
    子项目中需要通过<parent>配置父项目的坐标,子项目的groupid和version都可以和父项目的不同。父项目中没有任何子项目的信息。
    
    聚合项目时父项目类型为pom类型,父项目和子项目都存在一个窗口中,可认为是一个项目,但是每个子项目又可以独立运行。
    在聚合项目中子项目需要通过<parent>配置父项目的坐标,子项目和父项目的groupid和version都和父项目的相同。父项目通过<modules>配置聚合的子项目。

四十二、 Maven中项目的类型

复制代码
    Maven项目中项目类型是在pom.xml中通过<packaging>进行配置的。可取值:
    jar:默认值,表示打包为jar类型项目。
    war:web项目。当需要打包为servlet容器支持时,需要配置此类型。
    pom:逻辑父项目。聚合项目中父项目的类型。当设置为pom时Maven只解析pom.xml,不会编译src中内容。在idea中创建聚合项目时,自动会把父项目设置为pom类型。

四十三、 Maven常用命令

第三方工具Maven一款独立软件也能够通过IDEA或Eclipse实现集成通常情况下IDEA会预装并配置好该功能通过其内置的 panel可以方便地配置和执行与之相关的各种命令。

在这里插入图片描述
复制代码
    mvn help:system 打印系统属性和环境变量。
    mvn clean 清理target目录。
    
    mvn validate 验证项目是否可用。
    mvn compile 编译项目,生成target目录。
    mvn test 执行测试。
    
    执行测试时会自动运行src/test/java 目录下所有
    **/*Test.java、**/Test*.java、**/*TestCase.java 文件,采用约定方式执行。
    
    mvn package 打包项目。
    mvn deploy 发布项目。
    mvn verify 校验包是否正确。
    mvn install 安装到本地仓库。
    mvn install -DskipTests 跳过测试
    mvn site 生成站点。但是需要添加插件才能使用。
复制代码
    <build>
    <plugins>
        <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-project-info-reports-plugin</artifactId>
                <version>2.7</version>
        </plugin>
    </plugins>
    </build>

四十四、 Maven项目的生命周期

Maven项目的生命周期是指其运行过程中所包含的各个阶段,在每一个阶段开始之前都必须等待前一阶段完成后才能进行后续的操作,并且整个流程必须按照规定的顺序进行。不同的操作各自具有独特的生命周期,在日常开发中我们通常所说的项目周期特指部署操作(deploy)所对应的生命周期。

在这里插入图片描述

可以选择运行全部流程或者启动特定环节;然而,在任何情况下都需要从初始环节开始操作。

四十五、 Maven和Ant对比

Ant和Maven均属于Apache的开源项目管理工具,并且它们也是Java程序开发过程中广泛应用于编译、运行、测试以及打包的重要工具。
尽管两者都属于项目管理工具,但Maven所具有的功能明显超出了Ant的能力。

复制代码
    无论是IDEA还是Eclipse,默认的Java项目就是使用Ant进行项目管理的。
    在老Ant项目需要自己添加build文件,但是在目前的java项目中都不需要自己进行管理,
    都可以通过idea或eclipse帮助进行管理即可。

创建Maven项目的前提是明确指定其类型为maven类型。其结构及内容均遵循既定规范,并通过pom模型实现了对项目的整体管理。其特点之一是拥有中央仓库与本地仓库两部分存储系统,并较之于Ant这一优势尤为显著。实现了源码转移的目的,并且由于具备完整的生命周期体系而区别于Ant这一缺乏系统性特征的情况。

#【Spring MVC面试题】

四十六、 请介绍一下什么是MVC?

复制代码
    MVC是一种软件开发模式,是目前Java项目使用的最多的一种软件开发模式。因为Sun公司推荐JavaEE开发使用MVC模式。针对于MVC,Sun公司也推出了JSP Model1和JSP Model2,我们现在使用的就是JSP Model 2。
    
    MVC适用于中大型项目,开发时由底层向上层开发,越底层复用性越高。
    
    MVC把项目由单一大文件拆分成多个部分。包含:
    
    	M(Model)即模型。包含数据模型和业务模型。数据模式就是项目中的实体类,实现了数据封装的重用性。业务模型就是项目中的Service,业务类。
    	
    	V(View)即视图。视图就是呈现给用户的具体内容,如数据表格显示,树状菜单。目前已经发展成了一个独立岗位,前后端分离开发时的前端工程师就负责的是V。
    	
    	C(Controller)即控制器。在以前的项目中控制器主要的作用是根据业务模型和数据模

视图管理决定了展示的内容。当前项目中绝大多数情况都是控制器直接返回给前端接口的数据,比如:如JSON格式、XML格式等。

四十七、 请说一下Spring MVC中你知道的组件

DispatcherServlet:前端控制器;Spring MVC的入口及流程控制核心;其他组件由DispatcherServlet统一调度;减少了组件间耦合度。

复制代码
    	MultipartResovler:多部分处理器。文件上传时需要使用。
    	
    	LocaleResolver:解决客户端的区域和时区问题。
    	
    	ThemeResolver:主题解析器。刻提供自定义布局。
    	
    	HandlerMapping: 映射处理器。主要负责处理URL,并找到对应的Handler
    	
    	HandlerAdapter:适配器。负责调用具体的Handler。
    		
    	HandlerExceptionResovler:异常处理器。异常处理,根据不同异常返回视图。
    	
    	RequestToViewNameTranslator:从请求中获取到视图名称。
    	
    	ViewResovler:视图解析器,负责解析字符串视图名和物理视图文件的。
    	
    	FlashMapManager:主要用于存储属性的,本质是一个Map。多用在重定向时。FlashMap在重定向之前存储,重定向之后删除。
    	
    	ModelAndView:模型和视图。Spring MVC中提供的模型和视图接口。
    	
    	HandlerInterceptor:拦截器。拦截控制器资源的。

其中第2个至第10个组件位于DispatcherServlet的initStrategies中被初始化。

在这里插入图片描述

四十八、 Spring MVC 运行原理

请添加图片描述
复制代码
 客户端向服务端发起请求,当URL匹配后执行DispatcherServlet
    	
    	2. DispatcherServlet根据URL类型调用HandlerMapping
    	
    	3. HandlerMapping查找Handler后产生HandlerExecutionChain
    	
    	4. HandlerExecutionChain中包含Handler和Interceptor返回给DispatcherServlet
    	
    	5. DispatcherServlet根据Handler类型调用HandlerAdapter
    	
    	6. HandlerAdapter调用Handler进行执行。
    	
    	7. Handler执行完成后返回ModelAndView
    	
    	8. ModelAndView返回给DispatcherServlet
    	
    	9. DispatcherServlet调用ViewResovler处理视图
    	
    	10. DispatcherServlet响应结果给客户端

四十九、 请说一下Spring MVC中注解

复制代码
    		@Controller 定义控制器
    		
    		@RestController 等效 @Controller+@ResponseBody
    		
    		@ControllerAdvice 基于Spring框架的异常通知实现。
    		
    		@RequestMapping 定义映射路径
    		
    		@PutMapping 接收put请求,整体更新
    		
    		@PatchMapping 属于对Put的补充,局部更新
    		
    		@DeleteMapping 接收delete请求
    		
    		@PostMapping 接收post请求
    		
    		@GetMapping 接收get请求
    		
    		@ResponseBody 把返回值转换为json并设置到响应体中
    		
    		@CrossOrigin 允许跨域,在响应头添加Access-Control-Allow-Origin属性
    		
    		@ExceptionHandler 异常处理
    		
    		@RequestParam 处理请求参数
    		
    		@RequestHeader 接收请求头
    		
    		@RequestBody 把请求体数据转换为对象
    		
    		@SessionAttribute 获取Session作用域的值
    		
    		@PathVariable 获取restful请求参数

五十、 请说一下拦截器(Interceptor)和过滤器(Filter)的区别

1. 来源不同

拦截器是SpringMVC中的技术,过滤器是Java EE中的技术

2. 生效位置不同

拦截器必须在DispatcherServlet启动后才能发挥作用;而过滤机制一旦被ServletContext启动就会开始工作。

3. 目标不同

拦截器拦截的目标是HandlerMethod,过滤器可以过滤所有的URL。

4. 运行机制不同

拦截器的执行过程主要分为三个阶段,在HandlerMethod之前前后完成,并且是在视图处理完成后。
该过滤器仅限于在目标资源的前后两个方面进行操作。

5. 接口中方法类型不同

拦截器中的所有方法均属于默认实现,在实际应用中可以选择性地对其进行修改或者保留原有功能。
在过滤器模块中定义的所有方法均为抽象方法,在实际使用时若当前类并非抽象类,则需进行重写以满足具体需求。

6. 上下文不同

拦截器能够访问Spring容器中的内容。然而Filter由于受Tomcat管理而无法访问该容器的内容。

【Spring面试题】

五十一、 请介绍一下什么是IoC/DI?

下图是Spring官方文档对IoC的介绍

在这里插入图片描述

本章节涉及Spring框架对IoC核心思想的实现与阐述。该技术体系也被称为依赖注入(DI),它是一个系统设计模式,在对象实例中定义属性时可选的方式。具体而言,在构造一个Bean时,默认由容器提供其所需的所有依赖项;而这个过程本质上是将Bean反转为其所需的属性集合(因此称为控制反转),主要通过直接构造实例或采用服务定位器模式等方式来控制其实例化位置。

在这里插入图片描述

在软件开发中,依赖项注入技术(DI)是一种机制,在这种机制下, 对象在其生命周期中仅依赖于由构造函数参数、工厂方法返回值或其他方式提供的属性来定义所需的服务. 当容器创建Bean时会自动注入这些预先配置好的组件. 该机制实际上与Bean自身的结构相辅相成, 因此得名控制反转模式.

复制代码
 IoC和DI是一件事。IoC又称DI,DI又称IoC。它是一个过程
    
    2. bean的创建和管理都是由容器完成,这个过程就是对象控制权的反转,也就是IoC
    
    3. 容器创建对象的过程难免需要给对象中属性进行赋值,尤其是属性是类类型,需要使用容器中另一个Bean,
    	就需要从容器中获取另一个Bean注入到当前Bean。这个过程就是DI这个过程也是IoC。
    
    4. 给对象属性设置值有二种方式:构造方法、set方法。
    对应的代码:
复制代码
    <bean id="teacher" class="com.bjsxt.pojo.Teacher"></bean>
    <bean id="student" class="com.bjsxt.pojo.Student">
    <property name="teacher" ref="teacher"></property>
    </bean>

所使用的技术:
XML解析(注解)+反射。
存储的Bean都放入到一个Map对象中。

五十二、 Spring注入有几种方式

在Spring 5中注入提供了二种注入方式:

在这里插入图片描述

Constructor-based : 构造注入

在这里插入图片描述

Setter-based: 设置性注射
无论是p命名空间还是自动注射的本质就是构造性和设置性注射。
注: 在Spring 4版本中将工厂视为一种注射方式,而在Spring 5版本中则将其定义为实例化Bean的方式,其中包含了静态工厂方法与实例工厂方法的实现细节.

在这里插入图片描述

五十三、 Spring中标签的scope可取值

Spring中的scope控制的是Bean的有效范围。
一共有6个可取值:

在这里插入图片描述

spring的基础环境中就可以实现singleton和prototype的创建。 其中request、session、application和websocket这些组件仅限于网络环境下才能实现。

在这里插入图片描述

五十四、 BeanFactory和ApplicationContext的区别

BeanFactory作为Spring的关键组件,在该接口明确了 Spring容器的基本功能。构成 Spring注入机制的基础架构。

在这里插入图片描述

最常用实现类是XmlBeanFactory

复制代码
    FileSystemResource fsr = new FileSystemResource("D:\ ideaws\ spring1\ src\ applicationContext.xml");
    
    XmlBeanFactory xmlBeanFactory = new XmlBeanFactory(fsr);
    
    Object teacher = xmlBeanFactory.getBean("teacher");
    
    System.out.println(teacher);
复制代码
    但是从Spring 3.1 版本开始,使用
复制代码
    DefaultListableBeanFactory和XMLBeanDefinitionReader替代了。
    
    DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
    
    XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(defaultListableBeanFactory);
    
    xmlBeanDefinitionReader.loadBeanDefinitions(new FileSystemResource("D:\ ideaws\ spring1\ src\ applicationContext.xml"));
    
    Object teacher = defaultListableBeanFactory.getBean("teacher");

不论采用何种方式,在真正调用getBean时才会进行实例化。然而,在真实使用中会发现一个有趣的现象:ApplicationContext作为贝恩工厂(BeanFactory)的子接口,则具有更强的功能性:不仅包含全部功能,并且新增了其他功能。

复制代码
 AOP 功能
    2. 国际化(MessageSource)
    3. 访问资源,如URL和文件(ResourceLoader)
    4. 消息发送机制(ApplicationEventPublisher)
    5. Spring集成Web时的WebApplicationContext

在使用时ApplicationContext时多使用

复制代码
    ClassPathXmlApplicationContext
    ClassPathXmlApplicationContext applicationContext =
        new ClassPathXmlApplicationContext("applicationContext.xml");
    Object teacher = applicationContext.getBean("teacher");

基于测试结果表明,在所有情况下ApplicationContext均会立即创建Bean。可通过设置lazy-init属性实现LazyLoading特性。

复制代码
    <bean id="teacher" class="com.bjsxt.pojo.Teacher" lazy-init="true"></bean>

五十五、 Spring如何解决循环依赖(循环注入)问题

循环注入即多个类相互依赖,产生了一个闭环。

在这里插入图片描述

但官方也指出,在使用构造式注入时可能存在循环引用的情况。具体而言,在尚未完成当前对象实例化的过程中需向关联的对象发送注解;而该关联的对象在尚未完成自身初始化的过程中仍需向当前对象发送注解;因此这种困境无法通过常规方法得到解决;最终会导致BeanCurrentlyInCreationException异常出现

在这里插入图片描述

如果不将两个类都配置为使用配置注入且scope设置为singleton,则不会出现问题。
在单例模式下,默认情况下存在DefaultSingletonBeanRegistry这一机制,能够暂时缓存尚未完全初始化的Bean对象。
相反地,在prototype模式下若两者的scope都被设定,则会导致系统抛出一个异常:BeanCurrentlyInCreationException。

五十六、 请说一下Spring中自动装配

自动装配是指Bean中属性可以由Spring容器自动生成注入配置信息的技术。无需手动指定相关参数即可完成配置。

复制代码
    no:不自动注入。
    
    default:默认值。根据default-autowire=”"配置进行自动注入。如果autowire和default-autowire都没有配置时等效于no。
    
    byName:通过名称自动注入。要求必须存在自定
    
    byType:通过类型进行注入。如果存在多个相同类型的Bean,会报NoUniqueBeanDefinitionException
    
    constructor:通过构造方法进行注入。默认按照构造方法参数byName进行注入,如果没有找到与参数同名的bean,会按照byType进行注入。如果Spring容器中存在多个相同类型的bean时,也不会报错,但是不会注入。

五十七、 请介绍一下什么是AOP

复制代码
    面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。
    			
    AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,
    
    是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,
    
    从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

主要功能

复制代码
    	日志记录,性能统计,安全控制,事务处理,异常处理等等。

主要意图

复制代码
    将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,
    
    通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,
    
    进而改变这些行为的时候不影响业务逻辑的代码。

在实际开发中AOP主要应用在Service层。

在这里插入图片描述

五十八、 Spring中AOP有几种实现方式,区别是什么?

Spring中AOP有两种实现方式:

基于模式的方法:Schema-based方法。通过接口实现的方法:基于接口的方法。为了确定通知类型而每个通知必须实现特定接口类型:即每个通知都必须实现相应的接口以确定其类型。因为类已经实现了相应的接口结构因此配置过程相对较为简便。特别地在配置过程中无需明确指定参数和返回值类型

复制代码
     ①    BeforeAdvice

     ②	AfterReturningAdvice
     ③	MethodInterceptor
     ④	ThrowAdvice
复制代码
    <bean id="myBefore" class="com.bjsxt.aop.MyBefore"></bean>
    <bean id="myafter" class="com.bjsxt.aop.MyAfter"></bean>
    <bean id="myarround" class="com.bjsxt.aop.MyArround"></bean>
    <bean id="mythrow" class="com.bjsxt.aop.MyThrow"></bean>
    <aop:config proxy-target-class="false">
    <aop:pointcut id="mingcheng" expression="execution(* com.bjsxt.aop.Demo.*(..))"/>
    <aop:advisor advice-ref="myarround" pointcut-ref="mingcheng"></aop:advisor>
    <aop:advisor advice-ref="myBefore" pointcut-ref="mingcheng"></aop:advisor>
    <aop:advisor advice-ref="myafter" pointcut-ref="mingcheng"></aop:advisor>
    <aop:advisor advice-ref="mythrow" pointcut-ref="mingcheng" ></aop:advisor>
    </aop:config>

基于**AB实现的AspectJ方式。AB是一种基于字符匹配的技术,在编写日志管理器时具有良好的可维护性特点。这种技术的主要优势在于能够自动识别日志级别的不同阶段并生成相应的日志条目(如错误、警告、信息等)。不过,在实际应用中需要注意的是,在编写日志管理器时可能会遇到一些问题(如日志级别转换逻辑设计复杂)。另外一种技术(如EventBus**)则完全基于事件驱动的方式进行日志管理,在编写过程中需要注意的是如何正确地将日志事件与目标组件关联起来(例如如何根据日志级别选择合适的组件作为接收者)。

aop:config
<aop:aspect ref=“myAdvice”>
<aop:pointcut id=“mypoint” expression=“execution(* com.bjsxt.aop.Demo.test(String,int)) and args(name,age)” />
<aop:before method=“mybefore” arg-names=“name,age” pointcut-ref=“mypoint”></aop:before>
<aop:after-returning method=“myafter2” arg-names=“name,age,suiyi” pointcut-ref=“mypoint” returning=“suiyi”></aop:after-returning>
<aop:after method=“myafter” arg-names=“name,age” pointcut-ref=“mypoint”></aop:after>
<aop:around method=“myarround” arg-names=“pro,name,age” pointcut-ref=“mypoint” ></aop:around>
<aop:after-throwing method=“mythrow” arg-names=“name,age,e” throwing=“e” pointcut-ref=“mypoint”></aop:after-throwing>
</aop:aspect>
</aop:config>

由于Schame-based属于运行时增强技术,在编译阶段不进行修改而是在运行时进行调整或补充功能;而AspectJ则是基于编译阶段的技术,在程序构建阶段就对其进行修改或增强功能。因此,在面对较少的切面数量时,在性能上两者的差异不大。然而在遇到较多的切面数量情况时,则建议优先采用的是基于编译阶段的增强技术(如AspectJ),因为这种技术在处理多切面的情况下表现更为高效;在这种情况下不仅能够有效提升性能表现还能显著减少实现复杂度。

五十九、 Spring中通知有几种类型?

复制代码
    前置通知、后置通知、环绕通知、异常通知。
    可以使用AOP中的Schema-based或AspectJ方式实现。
    其中AspectJ中后置通知又分为:after和after-returning,after无论切点是否出现异常都执行的后置通知,after-returning只有在切点正常执行完成,才会触发的通知。

六十、 什么是Spring声明式事务?

声明式事务是Spring框架中提供的基于声明式的事务管理方法。该功能使得程序员无需直接处理复杂的事务代码。只需关注哪些操作涉及特定的事务类型即可。

由于无需编写事务管理相关的代码

在早期单独采用MyBatis框架时,默认情况下必须调用commit sqlSession来提交事务;而通过rollback实现回滚事务则是编程式的事务处理方式。

使用Spring框架进行事务处理时,默认情况下会将所有操作都标记为可串行化,默认情况下会将所有操作都标记为可串行化,默认情况下会将所有操作都标记为可串行化。

复制代码
    <tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
        <tx:method name="insert*" isolation="SERIALIZABLE"/>
        <tx:method name="delete*" propagation="MANDATORY"/>
        <tx:method name="update*" rollback-for="java.lang.Exception" no-rollback-for="java.lang.Exception" />
        <tx:method name="select*" read-only="true"/>
    </tx:attributes>
    </tx:advice>
    
    <aop:config>
    <aop:pointcut id="mypoint" expression="execution(* com.bjsxt.service.impl.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="mypoint"></aop:advisor>
    </aop:config>

六十一、 Spring支持的事务传播行为

Spring技术的主要应用领域集中在Web项目开发中,在这一过程中基于传递机制实现的行为通常表现为在同一网络请求内调用了多个带有声明式事务管理功能的对象。该传递机制具体运作时会根据其传递机制进行控制后的情况判断最终结果可能是一个单一的完整事务或者将各个独立的子交易分别提交至数据库

可以通过tx:method或@Transactional中propagation进行配置。

可取值分别为:

REQUIRED:

复制代码
    默认值。如果当前有事务则加入到事务中。如果当前没有事务则新增事务。

NEVER:

复制代码
    必须在非事务状态下执行,如果当前没有事务,正常执行,如果当前有事务,报错.

NESTED:

复制代码
    必须在事务状态下执行.如果没有事务,新建事务,如果当前有事务,创建一个嵌套事务.

REQUIRES_NEW:

复制代码
    必须在事务中执行,如果当前没有事务,新建事务,如果当前有事务,把当前事务挂起. 在重新建个事务。

SUPPORTS:

复制代码
    如果当前有事务就在事务中执行,如果当前没有事务,就在非事务状态下执行.

NOT_SUPPORTED:

复制代码
    必须在非事务下执行,如果当前没有事务,正常执行,如果当前有事务,把当前事务挂起.

MANDATORY:

复制代码
    必须在事务内部执行,如果当前有事务,就在事务中执行,如果没有事务,报错.

六十二、 Spring支持的事务隔离级别

当多个事务同时对数据库进行操作时

事务隔离级别主要是解决高并发下脏读、幻读、不可重复读问题的。

脏读:

复制代码
    事务A没有提交事务,事务B读取到事务A未提交的数据,这个过程称为脏读。读取到的数据叫做脏数据。

不可重复读:

复制代码
    当事务A读取到表中一行数据时,同时另一个事务修改这行数据,事务A读取到的数据和表中真实数据不一致。

幻读:

复制代码
    事务A对表做查询全部操作,事务B向表中新增一条数据。事务A查询出来的数据和表中数据不一致,称为幻读。

在tx:method或@Transactional中isolation进行配置。

其中isolation可取值分别为数据库的隔离级别DEFAULT(MySQL 8默认采用),MySQL 8默认的事务隔离级别REPEATABLE_READ(可通过查询 select @@transaction_isolation获取),读未提交(脏读等)READ_UNCOMMITTED、读已提交(幻读)READ_COMMITTED、可重复读(幻读)REPEATABLE_READ以及串行读来通过牺牲性能的方式解决脏读、不可重复度及幻读等问题。

六十三、 说一下@Autowired和@Resource的区别和相同点

@Autowired和@Resource都是实现注入的注解。

@Autowired是Spring官方提供的注解符,默认采用byType方式进行注入。如果在Spring容器中有多个具有相同类型的组件,则会采用byName方式完成注入;此时被注入组件的名称即为其name属性的值。若需要自定义注 inject 的名称,则可配合使用@Qualifier注解来实现。

@Resource基于Java原生实现的注解。通常采用byName方式进行参数注入。当找不到指定类型的资源时,默认采用byType方式进行注入。另外一种方式是通过设置name和type属性来控制特定类型的资源注入方式,在同时指定name和type属性的情况下,默认采取逻辑与的关系进行资源注入。

六十四、 Spring中你知道的注解

通过组件框架中的关键组件如@Configuration@Service@Repository以及其下的@Controller等层级结构进行Bean对象的定义与配置。Spring框架自版本5.0后引入了一项重要优化措施——优化注解扫描性能的关键注解@Indexed。该注解的主要功能是通过生成META-INT/spring.components文件并转换为CandidateComponentsIndex表的方式,在不影响原有功能的前提下显著提升了注解解析效率。

该组件扫描器负责对@ComponentScan注解进行解析。当配置基于Spring框架的XML设置时,其应用频率较低,在Spring Boot开发中却频繁被采用。

@Scope定义Bean的作用域。

采用该方式能够实现对Bean的合理配置。
除了使用过AOP相关的注解 @Pointcut 定义切点,
另外还使用了 @Aspect 定义通知类,
以及 @Before 用于设置前置通知,
随后 @After 用于设置后置通知,
并在 @AfterReturning 和 @Around 的情况下分别执行环绕操作,
并在遇到异常时利用 @AfterThrowing 进行处理。

除了@Value外还使用它来读取属性文件中的值; 用于定义事务处理; 负责自动生成并注入到受控对象中; 指定将被注入的对象的具体名称。

六十五、 Spring中Bean是否线程安全的?

Spring中Bean是否是线程安全主要看scope取值。

当scope=singleton时,则表明bean被配置为单例实例;因此其全局属性将呈现为非线程安全状态。为了尽可能地降低全局属性的数量并减少其潜在风险, 然而完全消除这种属性仍是比较困难的事情。为了进一步提高安全性, 可以考虑采用ThreadLocal机制来确保相关的全局属性达到线程安全的状态。

当scope=prototype时,在这种情况下(或在该情况下),bean实现线程安全性。然而相比singleton而言,在这种情况下(或在该情况下),性能相对较低。

六十六、 Spring中Bean的生命周期

在Spring框架中,Bean的生命历程指的是从初始化到销毁的过程。该系统支持的基本实现方式是通过标签进行基本定义。

在这里插入图片描述

在这样的场景下,类仅会调用其构造方法来进行对象的初始化。在这样的场景下可以通过指定标签上的initMethod和destoryMethod属性来完成对对象初始化与销毁的操作。除了上述之外还可以使该类实现一系列Aware接口除了上述之外还可以使该类实现一系列Aware接口例如 BeanNameAware BeanFactoryAware 和 ApplicationContextAware 等等。除此之外 还可以通过InitializingBean 来创建新的 Bean 实例 并通过DisposableBean 来完成对 Bean 对象的回收功能。此外 也可以借助于 BeanPostProcessor 来增强功能 注意到的一点是 此类别目前无法支持此类接口 并且与其他后处理器 如 BeanFactoryPostProcessor 共存可能会产生冲突

生命周期流程图:

在这里插入图片描述

【Spring Cloud Netfix Hystrix:断路器】

六十七、 什么是灾难性的雪崩效应

在微服务架构的项目设计中,特别是规模较大的系统架构中,默认情况下都会呈现出一种 service 调用 service 的层级结构。这种层级关系下某个 service 会调用其他的多个 service ,而这些被调用的服务又会进一步传递请求至其他相关的 service ,最终形成了一种相互关联的服务网络结构。在处理较少请求的情况下,则不会对整个服务链条产生显著影响。

在这里插入图片描述

尽管个别服务的请求数量较少,然而这些请求最终都会导向服务T。因此从总量上看,针对服务T的流量相对较多。该服务器端由于负载较高而CPU使用率显著提升。

在这里插入图片描述

当其中某一个服务突然遇到大量请求时。整个链条上所有服务负载骤增。

在这里插入图片描述

因为服务U和服务T承受了过大的负载压力,导致系统运行效率受到影响,进而可能导致调用服务U和服务T的其他环节出现问题,最终可能影响整个项目的正常运行.这种情况被称之为灾难性的雪崩效应.

在这里插入图片描述

造成灾难性雪崩效应的原因,可以简单归结为下述三种:

复制代码
    	服务提供者(Application Service)不可用。如:硬件故障、程序BUG、缓存击穿、并发请求量过大等。
    	
    	重试加大流量。如:用户重试、代码重试逻辑等。
    	
    	服务调用者(Application Client)不可用。如:同步请求阻塞造成的资源耗尽等。

雪崩效应表现为:在服务网络中出现单一节点故障时会引发连锁反应,并直接引发系统性崩溃。这种现象产生的影响往往是非预期性的。

六十八、 如何防止灾难性雪崩效应

降级

在资源耗尽的情况下进行降级处理,在完成降级处理后,则会配合相应的机制返回基础数据。设计并实现一个fallback机制,在请求后端服务过程中发生异常时,则会利用该fallback机制返回基础数据以提供支持。确保即使服务出现问题,整个项目仍能持续运行。

熔断

当失败率(如因网络故障/超时造成的失败率高)达到阈值时自动触发降级, 熔断器引发的快速失败将迅速得到处理。

请求缓存

实现了请求缓存功能。
当服务A在添加了请求缓存之后,在第一次请求时会触发缓存机制,并因此不再访问服务B;即使在面对大量请求时也不会给B带来过高的负载压力。
请确保减少对Application Service的访问次数。

请求合并

整合请求。每当服务A发起对服务B的请求时,在5毫秒的时间窗口内将所有相关请求进行整合处理,从而使得对服务B的负载压力得到显著缓解,并有效地缓解了对服务B负载激增的风险。
确保减少了对Application Service的请求次数。

隔离

隔离主要包含线程池隔离与信号量隔离两种类型。系统通过检测线程池或信号量资源是否饱和,并在处理请求超过系统容量时自动将超出部分进行处理,并最终实现限流的效果。

六十九、 Hystrix简介

针对 SpringCloud 平台中的崩溃现象(即所谓的灾难性雪崩效应),该解决方案主要依赖于 SpringCloud Netflix Hystrix 的技术机制来确保系统的稳定性和可靠性。

Hystrix [hɪst’rɪks]意为野猪。
因为背上的棘刺提供了依靠这些棘刺提供了自我防护能力。
本文所指的断路器(英文:Hystrix)作为一项容错技术,在Netflix开源项目中被广泛采用,并具备自我防护功能。

Hystrix其主要功能是在高并发场景中能够保障服务稳定运行的一系列保障措施。其主要体现在两个方面:容错机制和流量控制机制。

为了处理Spring cloud中的服务雪崩效应问题,开发人员必须依赖hystrix组件进行保护。所有参与开发的应用开发人员均需在他们的application client项目pom文件中添加以下依赖项:

复制代码
    ```java
    <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
复制代码
    降级
    
    降级是指,当请求超时、资源不足等情况发生时进行服务降级处理,不调用真实服务逻辑,而是使用快速失败(fallback)方式直接返回一个托底数据,保证服务链条的完整,避免服务雪崩。
    解决服务雪崩效应,都是避免application client请求application service时,出现服务调用错误或网络问题。处理手法都是在application client中实现。我们需要在application client相关工程中导入hystrix依赖信息。并在对应的启动类上增加新的注解@EnableCircuitBreaker,这个注解是用于开启hystrix熔断器的,简言之,就是让代码中的hystrix相关注解生效。
    
    ```java
    17	新建ApplicationServiceDemo
    18	配置pom.xml
    <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.5.RELEASE</version>
    </parent>
    <dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Hoxton.SR3</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
    </dependencyManagement>
    <dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    </dependencies>
    19	新建配置文件
    新建application.yml
    spring:
      application:
    name: application-service-demo
    eureka:
      client:
    service-url:
      defaultZone: http://eurekaserver1:8761/eureka/
    20	新建控制器
    新建com.bjsxt.controller.DemoController
    @Controller
    public class DemoController {
    @RequestMapping("/demo")
    @ResponseBody
    public String demo(){
        return "demo-service";
    }
    }
    21	新建启动类
    新建com.bjsxt.DemoApplication
    @SpringBootApplication
    public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class,args);
    }
    }
    
    22	新建DemoFallback
    新建项目DemoFallback作为Application Client
    23	编写pom.xml
    <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.5.RELEASE</version>
    </parent>
    <dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Hoxton.SR3</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
    </dependencyManagement>
    <dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
    </dependencies>
    24	新建配置文件
    新建application.yml
    注意不要端口号冲突
    spring:
      application:
    name: fallback-demo
    eureka:
      client:
    service-url:
      defaultZone: http://eurekaserver1:8761/eureka/
    
    
    server:
      port: 8081
    
    25	新建配置类
    新建com.bjsxt.config.RibbonConfig
    此处使用@LoadBalancer方式快捷配置负载均衡。
    @Configuration
    public class RibbonConfig {
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
    }
    26	新建service及实现类
    新建com.bjsxt.service.DemoService及实现类。
    实现类中@HystrixCommand的fallbackMethod配置的就是降级方法。
    myFallback方法:
    参数:和test的参数是相同的。
    返回值:和test中调用的application-service-demo/demo控制器方法返回值相同
    public interface DemoService {
    String test();
    }
    @Service
    public class DemoServiceImpl implements DemoService {
    @Autowired
    private RestTemplate restTemplate;
    @HystrixCommand(fallbackMethod = "myFallback")
    @Override
    public String test() {
        String result = restTemplate.postForObject("http://application-service-demo/demo", null, String.class);
        System.out.println(result);
        return result;
    }
    
    public String myFallback(){
        return "托底数据";
    }
    }
    
    27	新建控制器
    新建com.bjsxt.controller.FallbackController
    @Controller
    public class FallbackController {
    
    @Autowired
    private DemoService demoService;
    
    @RequestMapping("/demo")
    @ResponseBody
    public String demo(){
        return demoService.test();
    }
    }
    28	新建启动类
    @SpringBootApplication
    @EnableCircuitBreaker
    public class ApplicationClientApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(ApplicationclientdemoApplication.class, args);
    }
    }
    29	访问
    在浏览器输入http://localhost:8081/demo
     
    30	测试降级
    停止ApplicationServiceDemo项目。再次访问

七十一、 熔断

当一定时间内,异常请求比例(请求超时、网络故障、服务异常等)达到阀值时,启动熔断器,熔断器一旦启动,则会停止调用具体服务逻辑,通过fallback快速返回托底数据,保证服务链的完整。
熔断有自动恢复机制,如:当熔断器启动后,每隔5秒,尝试将新的请求发送给Application Service,如果服务可正常执行并返回结果,则关闭熔断器,服务恢复。如果仍旧调用失败,则继续返回托底数据,熔断器持续开启状态。
降级是出错了返回托底数据,而熔断是出错后如果开启了熔断将会一定时间不在访问application service

在这里插入图片描述

31 属性
通过在远程服务方法调用处添加@HystrixCommand注解来实现熔断功能。当相关配置满足时,则会开启或关闭熔断器。
对于@HystrixProperty中的name属性而言,在其管理中可以获取HystrixPropertiesManager定义的常量值,并可通过直接输入字符串的方式进行操作。
注释属性描述:

  • CIRCUIT_BREAKER_ENABLED
    • "circuitBreaker.enabled";
      该属性用于指定是否启用熔断策略,默认设置为true。

CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD
"circuitBreaker.requestVolumeThreshold";
该参数用于设定每段时间内(默认为10秒)检测到的请求数量上限。当实际请求数超过该阈值时,则会触发熔断机制以防止服务性能下降或系统稳定性受损。默认设置为20,请求数达到此数值即会启动保护措施。通俗而言:该参数表示在规定时间内最多允许接受的请求数目是多少才能避免触发保护功能。

配置一个时间间隔用于检测该circuitBreaker.requestVolumeThreshold的时序特性,默认值为10秒。
其单位设为毫秒。

Circuit Breaker Sleep Window In Milliseconds
“电路断开时长窗口”;
一旦熔断策略启用,则延迟多久进行一次远程服务的重试尝试。
单位为毫秒。
在该5秒内将直接调用fallback方法,并不会进行远程application服务的请求。

CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE
"circuitBreaker.errorThresholdPercentage";
在单位时间周期内,当错误请求数量占比例超过阈值时,则执行熔断机制。其默认阈值设置为50%。

CIRCUIT_BREAKER_FORCE_OPEN
“circuitBreaker.forceOpen”;
该参数是否强制启用熔融保护机制?即所有请求都将触发fallback托底数据返回,默认值设为false。

CIRCUIT_BREAKER_FORCE_CLOSED
“circuitBreaker.forceClosed”;
是否强制关闭熔断策略。即所有请求一定调用远程服务。默认为false。
32 代码示例
在原有降级代码上修改@HystrixCommand如下。
关闭ApplicationSeviceDemo项目,访问DemoFallback控制器,刷新5次后会发现页面加载快了,这时就开启熔断了。此时打开ApplicationServiceDemo,发现依然返回托底数据。到达30秒后再次访问才能正常访问ApplicationServiceDemo中内容。
注意:单位时间时间内容请求数必须达到5个(无论成功还是失败)才能满足条件。
@HystrixCommand(fallbackMethod = “myFallback”,commandProperties = {
// 条件一: 远程服务请求错误数量到达3个
@HystrixProperty(name= “circuitBreaker.requestVolumeThreshold”,value=“3”),
// 熔断周期时间,每10秒作为一个熔断时间单位
@HystrixProperty(name=“execution.isolation.thread.timeoutInMilliseconds”,value=“10000”),
// 条件二: 远程服务请求错误占比到达50%
@HystrixProperty(name=“circuitBreaker.errorThresholdPercentage”,value=“50”),
// 结果: 开启熔断后,30秒不在请求远程服务
@HystrixProperty(name=“circuitBreaker.sleepWindowInMilliseconds”,value = “30000”)
})

七十二、 请求缓存

该框架旨在减少频繁访问服务的数量,并提供了将单个请求与其返回结果进行存储的功能。当再次发起相同URL的请求时,该框架不会主动发起新请求而是直接从存储中获取结果返回给客户端。这种机制显著减少了对服务的压力。
该技术内置有缓存机制。其中一个是...另一个是...

  1. 是一个本地存储。在集群环境中缓存是不能同步的。
  2. 不支持第三方缓存服务。Redis, memcached无法使用相关的功能。
    因此可以采用spring cache技术来实现请求级别的数据持久化存储。
    基于现有的降级处理代码框架进行功能扩展:

33 添加依赖

org.springframework.boot
spring-boot-starter-data-redis

第34步:更新配置文件

请对启动类进行修改。
请为启动类增加@EnableCaching注解。
请为HystrixService及其实现类增加注解,并对服务实现类的方法额外添加注解。
@Cacheable(key = “‘client’”,cacheNames = “com.bjsxt”)

请对启动类进行修改。
请为启动类增加@EnableCaching注解。
请为HystrixService及其实现类增加注解,并对服务实现类的方法额外添加注解。
@Cacheable(key = “‘client’”,cacheNames = “com.bjsxt”)

37 开发一个控制器并对其进行功能测试。
该控制器的代码保持不变,在运行时只需直接调用对应的service函数即可。
38 对结果进行详细检查。
在运行查询后查看是否成功添加或删除键。

七十三、 请求合并

不允许将请求进行合并;应用服务的负载量等于应用客户端提交的所有请求总数。

在这里插入图片描述

将请求集中至一个综合请求,在时间段内整合所有相关查询指令并减少重复计算,在实际应用中显著减少了A/S负载。

在这里插入图片描述

在什么情况下进行请求合并?在微服务架构设计中,默认的做法是将一个系统拆分为多个相对独立的服务。这些服务之间通过异步通信机制进行交互。然而,在高并发场景下,由于系统设计中的默认行为——即大量服务间频繁的消息交换——会随着通信次数的增长而导致总通信开销上升。与此同时由于线程池资源受限高并发环境下会导致大量线程长时间处于阻塞状态这可能导致系统响应延迟显著增加因此我们需要深入理解Hystrix的技术原理以实现高效的负载均衡和 request merging功能

在使用请求数量合并时面临的问题包括单个请求数量处理通常可以在5ms内完成这一情况的变化。然而,在开启请求数量合并后会等待额外,请检查是否还有其他 please数量同时处理的情况发生。这种情况下单个 please 处理时间因此从原本的5ms延长至15ms。然而,在发起具有较高延迟特性的命令时这一时间窗的时间消耗变得相对较小从而使得 please 合并成为一种有效的解决方案这一观点更加明确地呈现出来

  1. 探讨如何将多个请求进行整合与参数化处理
    通过理论学习掌握相关知识后,在实际编码过程中较为简便。其中核心部分涉及一个名为@HystrixCollapser的关键注解
在这里插入图片描述

40 代码实现
在进行请求合并操作时,
不仅需要对应用客户端进行相应的代码调整,
同时需要对应用服务端的代码进行相应的调整,
由于应用服务端必须能够整合所有参数并以这种方式处理,
此外还支持将多个结果值一并返回。
40.1 修改Application Service控制器
在未进行请求合并操作的情况下没有相应的控制器。

复制代码
    @RequestMapping("/service")
    public String demo(String name){
    return "姓名:"+name;
    }

每当存在需要合并的请求时,系统必须能够一次性接收多个参数并返回多条结果,同时还需要增加一个控制器方法以处理相关逻辑。由于应用客户端通常会通过HTTP/REST接口使用 request 体来传递数据,因此在配置管理类中添加 @RequestBody 标签是合理的做法。对于返回的数据类型,它们都属于列表类型,并且其中的数据具有明确顺序。

复制代码
    @RequestMapping("/service2")
    public List<String> demo2(@RequestBody List<String> names){
    System.out.println("接收到的内容:"+names);
    List<String> list = new ArrayList<>();
    for (String name : names){
        list.add("姓名:"+name);
    }
    return list;
    }

在此基础之上,在Service及其实现类中新增相关方法;通过@HystrixCollapser进行数据整合;batchMethod:用于处理数据整合的方法;其中scope:指定数据整合的作用域范围;其中global标志表示所有参与线程均可等待整合;而request标志允许同一线程内的多次远程服务调用进行整合;此外timerDelayInMilliseconds:设置等待时间,默认值为10毫秒;maxRequestInBatch则决定了每次整合的最大数量设置

在实现类中, 当client(String)方法一旦被@HystrixCollapser标记时, 则该方法将不会被调用/执行, 并且其方法体内无需编写具体实现代码即可完成功能. 可以直接调用/执行batchMethod对应的实现.
注意事项: 在实际测试过程中, 默认设置为REQUEST可能导致scope出现空指针异常, 请将scope设置为GLOBAL.

复制代码
    Future<String> client(String name);
    @Override
    @HystrixCollapser(batchMethod = "myBatchMethod",scope = com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL
    ,collapserProperties = {@HystrixProperty(name="timerDelayInMilliseconds",value="10"),@HystrixProperty(name="maxRequestsInBatch",value = "200")})
    public Future<String> client(String name) {
    System.out.println("client方法,有请求合并时将不支持这个方法");
    String result = restTemplate.getForObject("http://APPLICATION-SERVICE/service?name={1}", String.class, name);
    return null;
    }
    
    @HystrixCommand
    public List<String> myBatchMethod(List<String> name){
    System.out.println("传递过去的参数:"+name);
    List<String> list = restTemplate.postForObject("http://APPLICATION-SERVICE/service2", name, List.class);
    return list;
    }

第40.3条,在修改consumer中的控制器方法时,请注意以下要点:首先,在同一个控制器内反复调用service方法来进行模拟多线程请求处理;其次,请通过设置休眠状态来模拟两个并发请求的到达;最后,请确保这两个函数的输出能够正确地合并在一起,则需要将它们的输出语句放置在相应请求之后

复制代码
    @RequestMapping("/client")
    public String client(){
    try {
        Future<String> f1 = clientService.client("张三");
        Future<String> f2 = clientService.client("李四");
        System.out.println("f1:"+f1.get());
        System.out.println("f2:"+f2.get());
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }
    return "ok";
    }
    @RequestMapping("/client")
    public String client(){
    try {
        Future<String> f1 = clientService.client("张三");
        Future<String> f2 = clientService.client("李四");
        System.out.println("f1:"+f1.get());
        System.out.println("f2:"+f2.get());
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }
    return "ok";
    }

七十四、 隔离

41 多线程环境中的资源分配机制设计通常采用的一种技术手段是所谓的"线程池隔离"。
42 在多线程环境中采用隔离机制的原因是什么?
如果没有实施这种机制,在某些情况下可能会出现某个接口因高并发被占用而影响到其他接口可用性。

在这里插入图片描述

当使用线程池隔离。不同接口有着自己独立的线程池

在这里插入图片描述

即使某个线程池都被占用,也不影响其他线程。

在这里插入图片描述

43 Hystrix的线程池隔离
Hystrix采用基于舱壁隔离(Bulkhead Partition)的技术实现线程池管理。
该技术通过将船体划分为多个独立隔舱,在部分隔舱发生漏水情况下也不会影响其他区域的水流循环特性。
有效维持船舶的浮力和稳定性特征,并降低了沉船的风险。

在这里插入图片描述

44 线程池隔离的优缺点
优点:

  1. 任何服务都将独立于自身的独享级数而存在

  2. 当依赖的服务重新被激活时 可以通过释放其占用的资源 迅速地复用了相关的资源 但是tomcat进程若已处于饱和状态 就会面临较大的操作难度

  3. 每一个都拥有独立的独享空间 这一特点在一定程度上保证了系统的高并发能力

  4. 受限于单个进程所能拥有的最大数量 这一机制同时也具备着明显的限流效果

  5. 增加了一个额外的CPU负载项的原因在于除了原有的Tomcat线程池外还引入了Hystrix独立化的机制。

  6. 每个操作都由独立的新线程执行可能导致队列等待 进行调度以及频繁地切换上下文等潜在问题
    45 代码演示
    在尝试同时调用thread方法及thread2方法时发现已停止采用同一个线程池策略

复制代码
    @HystrixCommand(groupKey = "jqk",commandKey = "abc",threadPoolKey = "jqk",threadPoolProperties = {
        @HystrixProperty(name="coreSize",value="8"),
        @HystrixProperty(name="maxQueueSize",value="5"),
        @HystrixProperty(name="keepAliveTimeMinutes",value="2"),
        @HystrixProperty(name="queueSizeRejectionThreshold",value="5")
    })
    @Override
    public String thread() {
    System.out.println(Thread.currentThread().getName());
    return "thread1";
    }
    
    @Override
    public String thread2() {
    System.out.println(Thread.currentThread().getName());
    return "thread2";
    }

46 参数说明

在这里插入图片描述

47 信号量隔离
48 信号量是什么
java.util.concurrent.Semaphore用来控制可同时并发的线程数。通过构造方法指定内部虚拟许可的数量。每次线程执行操作时先通过acquire方法获得许可,执行完毕再通过release方法释放许可。如果无可用许可,那么acquire方法将一直阻塞,直到其它线程释放许可。
如果采用信号量隔离技术,每接收一个请求,都是服务自身线程去直接调用依赖服务,信号量就相当于一道关卡,每个线程通过关卡后,信号量数量减1,当为0时不再允许线程通过,而是直接执行fallback逻辑并返回,说白了仅仅做了一个限流。
49 代码实现

复制代码
    @HystrixCommand(commandProperties = {
        @HystrixProperty(name=HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY,value="SEMAPHORE"),
        @HystrixProperty(name=HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS,value="10")
    },fallbackMethod = "jqk")
    @Override
    public String semaphore() {
    System.out.println("执行了,访问service");
    return "结果";
    }

50 参数说明

在这里插入图片描述

51 信号量隔离优缺点
优点

复制代码
    	没有线程切换,性能好。

缺点

复制代码
    所有请求处理共用一个线程池。所有接口相互影响更大。容易因为一个接口问题,导致其他接口同时出错。

54 线程池隔离和信号量隔离

在这里插入图片描述

在高并发系统中通常会采取限流措施 以避免服务器承受过多请求带来的压力 并抵御网络攻击行为。 而Hystrix通过线程池隔离和信号量机制有效地限制了系统的负载水平

七十五、 OpenFeign的降级处理

若使用OpenFeign调用远程服务出现500错误,则表明存在超时问题。
如果不希望出现该错误,则可利用OpenFeign自带的Hystrix功能进行降级处理。
编写配置文件:
默认情况下,默认情况下Hystrix功能不会被启用;用户若希望启用该功能则需手动配置相关内容;此外,默认情况下与其他配置保持一致。

复制代码
    spring:
      application:
    name: hystrix-provider
    server:
      port: 8082
    eureka:
      client:
    service-url:
      defaultZone: http://eurekaserver1:8761/eureka/,http://eurekaserver2:8761/eureka/
    
    
    feign:
      hystrix:
    enabled: true

57 配置依赖项 Feign实现了Hystrix部分功能。因此无需额外引入Hystrix相关的依赖项。

复制代码
    <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.5.RELEASE</version>
    </parent>
    <dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Hoxton.SR3</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
    </dependencyManagement>
    <dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    </dependencies>

58 实现服务
实现HystrixService的功能。继续采用Feign来进行声明式的调用方式。
在案例中是直接使用内部类来开发功能的,或者可以创建外部新类来完成同样的功能。
该属性用于标识降级后的处理逻辑。

复制代码
    @FeignClient(name = "eureka-application-provider", fallback = HystricService.HystrixServiceFallback.class)
    public interface HystricService {
    @PostMapping("/show1")
    Map<String, Object> show(@RequestParam String content);
    
    @Component
    class HystrixServiceFallback implements HystricService {
        @Override
        public Map<String, Object> show(String content) {
            System.out.println("执行方法,出现服务降级,返回托底数据");
            Map<String, Object> map = new HashMap<>();
            map.put("a", "因为Provider连接不上了,返回托底数据");
            return map;
        }
    }
    }

59 编写启动器

复制代码
    @SpringBootApplication
    @EnableDiscoveryClient
    @EnableFeignClients
    public class HystrixApplication {
    public static void main(String[] args) {
        SpringApplication.run(HystrixApplication.class,args);
    }
    }

七十六、 OpenFeign的熔断处理

第60号配置文件

复制代码
    hystrix: # hystrix 容灾配置
      command: # hystrix 命令配置,就是具体的容灾方案
    default: # 默认环境,相当于全局范围,后续的配置,参考HystrixPropertiesManager中的常量配置
      circuitBreaker: # 熔断配置, 常用。 其他配置不常用。
        enabled: true
        requestVolumeThreshold: 2
        sleepWindowInMilliseconds: 2000
        errorThresholdPercentage: 50

【MyBatis全套面试题】

七十七、 请介绍下MyBatis框架及优缺点

官方解释:
MyBatis 是支持普通 SQL 查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的 JDBC 代码和参数的手工设置以及结果集的检索。MyBatis 使用简单的 XML 或注解用于配置和原始映射,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java 对象)映射成数据库中的记录。
口语化解释:
MyBatis前身是Apache的iBatis,2010年由iBatis更名为MyBatis,目前源码托管在Github
上。通过程序员自己编写SQL后交给MyBatis进行结果映射,极大的简化了JDBC冗余代码。除此还内置支持缓存、延迟加载、日志、动态SQL等功能。是目前Java项目首选的数据访问层框架。
优点:

  1. 灵活性强。程序员自行编写SQL即可实现所需结果。

  2. 内置缓存功能。通过缓存功能显著减少了数据库交互次数。

  3. 优化代码结构。相较于传统JDBC编码方式去除了大量模板代码。

  4. 广泛的数据库兼容性。MyBatis覆盖了市面上几乎所有主流数据库产品。

  5. 良好整合能力。若开发框架无法良好继承现有Spring体系,则难以推广普及。

  6. 需要程序员具备良好的SQL功底。因为MyBatis中所有的SQL都是由程序员手动编写,所有如何写出一个良好的SQL语句称为了使用MyBatis框架的最大考验。

  7. 移植性差。SQL是依赖数据库的,不同数据库有不同的SQL语句,所以在开发时需要选定好所使用的数据库,尽量不要更换数据库。

七十八、 请说一下你理解的ORM

O/R M全称Object/Relational Mapping(ORM),即对象/关系映射(ORM)。它是一种程序设计技术(programming technique)。其核心理念是通过指定对象与关系型数据库之间的映射关系(mapping relations between objects and relational databases),使得程序开发时能够采用面向对象的方法操作关系型数据库(operate relational databases using object-oriented paradigms)。
OR/R M是数据库发展速度不及编程语言的产物。

在这里插入图片描述

当采用ORM时,只需将目标对象传递给其框架;该框架会根据预先定义好的映射规则将目标对象中的属性值对应存储至数据库中的相应字段;从而自动完成对数据库中新增记录及其结果的数据填充,并自动生成所需的SQL语句;显著提升了程序开发效率

七十九、 为什么说MyBatis是半自动ORM框架

标准的ORM框架仅需将对象传递给该框架后即可完成操作,在其内部自动生成对应的SQL语句,并在执行完毕后返回查询结果。然而,在使用MyBatis时,则要求程序员手动编写部分SQL语句(通常用于复杂的业务逻辑),然后由该框架负责自动生成并完成其余的查询填充工作。因此可以说,在这种模式下实现了一定程度的自动化功能。值得注意的是,在这种模式下实现了一定程度的自动化功能。

在这里插入图片描述

八十、 MyBatis和Hibernate的区别

MyBatis和Hibernate被视为当前企业数据访问层框架中的主流工具,并均采用了JDBC的技术手段进行封装;然而,在市场应用中,MyBatis的应用程度远高于Hibernate。

  1. Hibernate是一种标准型ORM框架,在建立了映射关系之后会自动生成SQL并返回结果;而MyBatis则是一种半自动化的ORM框架,在编程人员需要手动编写SQL时使用。
  2. 在移植性方面更有优势的是Hibernate;它只需配置所需数据库方言即可切换至支持的框架;相比之下,MyBatis则需要修改执行SQL。
  3. 在缓存机制上更为强大的是Hibernate;其缓存基于对象ID(OID)判断数据是否已存在;而MyBatis则依据标签进行缓存;此外,Hibernate还会自动协助排查脏数据问题。
  4. Hibernate提供了更为全面的日志记录功能;包括执行SQL、SQL语句优化、缓存提示以及异常处理等信息记录;而MyBatis仅负责执行SQL、参数设置以及结果缓存操作日志记录。
  5. 相对于而言,MyBatis提供了更多自定义功能;虽然它也需要编程人员编写部分SQL代码;但这也是其一个显著优点所在。
  6. 从易用性角度来看,则MyBatis更具优势:其学习成本更低且操作更为简便;相比之下,Hibernate虽然功能更为强大但学习成本相对较高。
    通过对比可以看出,似乎Hibernate在某些方面确实优于MyBatis,但为何实际应用中使用率更高呢?原因在于:首先, MyBatis的学习门槛较低,上手更容易理解和掌握;其次, 现代程序员普遍具备一定的SQL技能基础,因此对零SQL的支持并没有像之前认为那样明显的优势了;
    另外,Hibernate由于其笨重的操作方式(每次查询都需要全字段投影)导致灵活性不如人意;
    最后,Hibernate由于其过重的性能负担(全字段投影)使得开发效率较低;
    相比之下, MyBatis可以根据具体需求进行灵活设置,从而展现出更高的适用性和便捷性

八十一、 MyBatis中是否必须有接口

MyBatis框架并不要求必须有接口。MyBatis提供了三种引入的方式:

  1. 专注于构建mapper.xml文件
  2. 分别构建与系统交互的业务接口,并完成对mapper.xml文件的开发
  3. 在实现业务逻辑的过程中,在每个相关的方法上标注对应的SQL语句
    这些策略对应于MyBatis全局配置文件中的三种设置方案(三选一)
在这里插入图片描述

在平时项目中为什么必须提供一个接口?其核心原因在于可以在其他层快速地注入接口对象。
主要原因是在其他层可以通过接口迅速地注入接口对象,并且能够方便地调用相关功能。

在这里插入图片描述

八十二、 MyBatis是如何实现接口和mapper.xml绑定的

在MyBatis中全局配置文件中指定要扫描的包

在这里插入图片描述

在包中提供提供同名的接口文件和mapper.xml文件

在这里插入图片描述

在mapper.xml中namespace指定接口的全限定路径

在这里插入图片描述

在标签的id属性值配置为接口中方法名称

在这里插入图片描述

在代码中通过SqlSession的getMapper方法获取接口的代理对象

在这里插入图片描述

MyBatis中通过动态代理获取到接口的代理对象

在这里插入图片描述

八十三、 MyBatis接口中是否支持方法重载

在Java的类设计中提供了方法重载功能,在这种情况下允许多个类共享相同名称的方法。然而,在MyBatis体系结构中并未实现这一功能。由于mapper.xml文件中的每个标签最终都会以namespace+id的方式作为键被存储。因此,在设计接口时若出现同名方法(即方法重载)的情况,则会导致mapper.xml文件中含有对应的同名id信息。例如,在接口设计中可能会出现同名方法的情况。

在这里插入图片描述

在mapper.xml中配置时就会出现两个同名id

在这里插入图片描述
复制代码
    当程序运行后会出现异常,异常信息如下
复制代码
    Caused by: java.lang.IllegalArgumentException: Mapped Statements collection already contains value for com.bjsxt.mapper.StudentMapper.selectAll. please check com/bjsxt/mapper/StudentMapper.xml and com/bjsxt/mapper/StudentMapper.xml
    	at org.apache.ibatis.session.Configuration$StrictMap.put(Configuration.java:1014)
    	at org.apache.ibatis.session.Configuration$StrictMap.put(Configuration.java:970)
    	at org.apache.ibatis.session.Configuration.addMappedStatement(Configuration.java:768)
    	at org.apache.ibatis.builder.MapperBuilderAssistant.addMappedStatement(MapperBuilderAssistant.java:297)
    	at org.apache.ibatis.builder.xml.XMLStatementBuilder.parseStatementNode(XMLStatementBuilder.java:113)
    	at org.apache.ibatis.builder.xml.XMLMapperBuilder.buildStatementFromContext(XMLMapperBuilder.java:138)
    	at org.apache.ibatis.builder.xml.XMLMapperBuilder.buildStatementFromContext(XMLMapperBuilder.java:131)
    	at org.apache.ibatis.builder.xml.XMLMapperBuilder.configurationElement(XMLMapperBuilder.java:121)
    	... 13 more

八十四、 MyBatis中mapper.xml除了、、、标签以外还支持哪些标签

在MyBatis的mapper.xml文件中,除了基本的增删改查四个标签之外,还提供了包括结果集映射在内的七种高级功能;同时支持了静态SQL和动态SQL的操作,并分别设置了二级缓存配置(4个)、二级缓存引用(3个)、参数映射(2个)以及 SQL 片段(1 个)等七项设置;此外还有其他常用的几个子标签如表单绑定等;这样的设计使得应用更加灵活高效。

八十五、 MyBatis中namespace的作用,有哪些要求

namespace属性是mapper.xml中的一个字段,在此位置上定义了当前文件所有SQL语句所使用的命名空间。即指一个由统一名称标识的语句集合。 namespace属性不可缺失且不允许为空,在未正确配置或留空的情况下将导致运行时错误。

在这里插入图片描述

多个mapper.xml文件中是可以支持同一个namespace的定义。然而,在实际开发项目中,默认情况下每个接口都会被绑定到唯一的全限定路径,并且每个mapper.xml文件仅负责一个特定的接口。因此,在项目的实际应用中是不存在出现同名namespace的情况。

八十六、 MyBatis中不同的mapper.xml中id是否允许重复

  1. id属性的目的在于与namespace相结合作为SQL的关键字(key)。每个SQL的关键字都是唯一的。
  2. namespace是一个必要的配置项,并且在不同的mapper.xml中同一个attribute可能被配置相同的ns。

如果不同namespace下标签拥有同一个父容器节点,则该父节点的所有子节点标签将共享相同的命名空间(即具有相同的前缀),因此这些子节点标签可以在同一个父容器下拥有相同的名称而不引发命名冲突。反之,在同一个命名空间下(即父容器节点具有相同的命名空间),任何子节点标签必须保证其名称唯一性以避免命名冲突的问题。例如,在BjsxtMapper.xml中父容器节点属于com.bjsxt命名空间,在SxtMapper.xml中对应的父容器节点也属于com.bjsxt命名空间,则这两个XML文件中都存在一个名为insert的操作(即操作名)时会分别生成带有相同标识符$com.bjsxt.insert的操作记录。在这种情况下同一个操作会被生成两次从而导致同一操作出现两次的情况。

八十七、 MyBatis的mapper.xml中如何获取方法参数

通过SQL语句中的#{}语法可以提取所需字段值。
在MyBatis 3.5版本中允许通过mapper.xml定义三种不同的方法来获取字段,并规定最多只能选择其中两种。
当仅需一个字段且类型为基本数据类型时,则可灵活地采用#{}语法来表示。
举例说明,在一个接口中有两个方法各接受一个字段作为输入。

在这里插入图片描述

在mapper.xml文件中使用paramN或argN-1的方法获取参数的值。
位于mapper.xml中的方法包括使用paramName或其他索引位置来访问数据。
可以在mapper.xml文件中采用特定的方法如getParam("name")或其他方式来提取所需信息。

在这里插入图片描述

argN-1的示例

在这里插入图片描述

也可以混合使用

在这里插入图片描述

当接口方法被配置时,在注释中指定参数时,请仅限于采用指定的键名以及参数名(即paramN)的形式;而采用argN-1方式进行参数传递则不可行。

在这里插入图片描述

在mapper.xml中可以通过key或paramN方式获取参数的值

在这里插入图片描述

如果参数是对象类型,则使用对象属性名后获取参数

八十八、 MyBatis的mapper.xml中${}和#{}的区别

#{} 被解析为占位符以防止SQL注入。
选择id和name字段自people表where条件id=#{param1}
选号id=?相当于进行字符串拼接。
其中 ${} 被称为占位符符号,在给列赋值时应避免直接使用它。
如果希望动态地设置表名或列名等参数,则可以采用类似的方式插入变量。
从people表选择id和name字段where条件id=123

select id,name from ${param1} where id=123

八十九、 MyBatis中结果填充(映射)的几种方式

MyBatis中结果映射分为三种方式:

  1. 自动生成映射。在查询时若遇到表中的字段名称与实体类属性名称一致的情况,则MyBatis会直接将该字段值赋值给对应的属性。
  2. 在别称机制下,在进行数据操作时如果发现数据库中的表字段名称与实体类属性名称不匹配,则可以通过设置别称来实现对应的字段关联。
  3. 结果集映射配置法同样提供了另一种解决方案:通过配置结果集映射明确指定各字段间的对应关系。

九十、 MyBatis中实体类的属性名和列名是否必须相同

在数据库命名规范中遵循多个单词间用下划线分隔的原则;如 stu_id。
Java 属性命名遵循小驼峰规则;如 stuId。
命名规范的一致性问题会引起经常出现属性与字段名称不符的情况。
当名称存在差异时可通过别称或映射机制来解决此类问题。
因此,在 MyBatis 中实体类属性与表中的列名称之间并无严格要求必须完全吻合。

九十一、 MyBatis中实体类是否必须有getter和setter方法

不是的。
主要采用反射机制来实现对gettersetter接口的获取。
当实体类不提供相应的gettersetter接口时,默认会直接通过反射机制访问其属性。
在进行参数获取的过程中会调用对应的方法来完成数据的传递。
通过查看源代码中的Configuration类来了解具体的配置方式。

复制代码
    protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();

继续跟踪发现在SetFieldInvoker中存在

在这里插入图片描述

九十二、 MyBatis如何实现关联对象查询

在MyBatis中支持两种方式实现关联对象查询。

在忽略缓存的情况下,在使用N+1模式时,
例如用于查询外键表的数据,
假设无缓存环境并存在大量数据,
则需一条专门用于查询外关键节点的SQL语句,
以及基于主关键值回查对应主关键值所需执行的 SQL 语句数量等于数据量 N。

  1. 多表联合查询方式。
    多表联合查询方式只需要一条SQL语句。

然而N+1查询模式具备延迟加载功能 而多表联合查询模式不具备该功能;
当关联的对象是一个单一实例时 默认会采用N+1模式来执行查询;
当关联的对象是一个集合体时 默认会采用多表联合模式来执行查询

九十三、 MyBatis中如何实现延迟加载

MyBatis中的延迟加载仅在N+1查询模式下启用,并且默认状态下并未开启该功能。
采用这两种配置方案之一即可实现预期效果

  1. 设置全局开关。在MyBatis全局配置文件中配置两个属性,开启延迟加载
复制代码
    <settings>
    <setting name="lazyLoadingEnabled" value="true"/>
    <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

用于或中的fetchType管理中,lazy表示延迟加载而eager则代表立即加载.若当fetchType与全局开关同时配置并置使用时,fetchType则会起作用.

九十四、 MyBatis的缓存机制

MyBatis支持一级和二级缓存机制。通过使用这些技术手段能够有效地减少对数据库的访问次数从而提升程序运行效率。其中一级缓存在功能上等同于SqllangSession组件它默认处于启用状态其有效范围必须是同一个SqllangSession对象并且每次会将相同的SQL语句进行一次存储。当不再需要该SQL结果时系统会将这些结果从内存中清除以释放资源。

而二级SqlSessionCache组件则被称为SqllangSessionFactory组件它同样支持同一实例的有效范围但默认处于禁用状态需要在映射文件中明确开启这一功能配置。每当一个SqllangSession完成提交或关闭操作后系统会自动将此时的一级SqlSessionCache内容转移至对应的SqllangSessionFactoryCache中以实现数据的一致性管理。由于在我们的项目环境中SqllangSessionFactory始终处于不关闭的状态因此其对应的Cache始终处于激活状态并负责存储那些频繁被访问且 rarely修改的数据项。

当系统执行一次查询操作时首先要检查是否存在对应的记录如果不存在则继续查找一阶Cache中的结果;如果仍然不存在则才会进行数据库查询操作并把结果返回给一阶Cache以便下次调用时能够快速获取数据这一机制保证了事务处理的安全性和高效性

九十五、 MyBatis中执行器有几种

MyBatis的所有执行器均遵循Executor接口的规定,在其运行过程中处理SQL指令的执行流程。

项目可以通过factory.openSession()方法的参数来指定执行器类型;使用Enum类型的ExecutorType来进行配置。此外,在全局配置文件中也可以使用defaultExecutorType来进行全局配置。

九十六、 说一下MyBatis四大核心接口及执行流程

MyBatis四大核心接口:
Executor: 执行器。负责SQL执行过程的总体控制。

在这里插入图片描述

ParameterHandler: 参数处理器。负责SQL语句的参数设置

在这里插入图片描述

StatementHandler主要负责与JDBC代码进行交互,并涵盖三个主要功能:首先是对代码进行preprocessing处理;其次执行查询操作;最后执行增删改三种更新操作。

在这里插入图片描述

ResultSetHandler:结果集处理器。负责把查询结果映射为Java对象。

在这里插入图片描述

执行流程:

在这里插入图片描述

九十七、 MyBatis中如何定义一个插件

类实例需要实现Interceptor接口,并重写其中的方法。具体来说:

  • 该方法负责执行拦截操作并提供所需数据
  • 插件功能生成代理实例
  • 设置属性功能可读取MyBatis配置文件中的属性信息

向指定类上添加截距注解@Intercepts(其中类型参数为@Signature类型),具体配置如下:

  • type字段指定监听的目标类/接口,默认支持四大核心接口及其实现类

  • method字段指定目标类/接口中的方法名称

  • args字段用于指定相关的方法参数

    1. 配置插件
复制代码
    <plugins>
    <plugin interceptor="com.bjsxt.plugin.PagePlugin"></plugin>
    </plugins>

九十八、 MyBatis的动态SQL是什么,支持哪些动态SQL标签

复制代码
    动态SQL本质就是SQL语句的拼接。根据不同条件产生不同的SQL语句。
    
    在MyBatis中动态SQL标签包含
    <if>、<choose>..<when>..<otherwise>、<trim>、<where>、<set>、<foreach>、<bind>七组标签。

九十九、 MyBatis支持的日志类型

我的batis日志被默认设置为未启用状态。
可以通过修改mybatis的整体配置参数来调节logImpl设置。
参考官方文档规定时可知,默认情况下系统支持六种不同的日志类型。

在这里插入图片描述

一百、 MyBatis使用了哪些设计模式

代理组件(Mapper),适配器组件(Log),工厂组件(SqlSession),装饰器组件(CachingExecutor),建造者组件(SqllSessionFactoryBuilder),策略对象(openSession可配置ExecutorType),模板组件(BaseExecutor),责任链节点(Interceptor)

一百〇一、 说一下MyBatis的运行原理

该系统采用断点调试方法对MyBatis运行流程进行详细跟踪分析;
该技术基于JDBC框架设计,并通过映射文件替代传统接口实现来简化开发流程;同时在该框架中实现了事务缓存机制;
系统启动前需加载MyBatis全局配置文件;通过Resources类将配置文件转换为InputStream对象以便后续处理;
随后利用XMLConfigBuilder将InputStream对象解析为Configuration对象完成初始化设置;
接着利用工厂对象创建SqlSession实例;在此过程中,默认采用SimpleExecutor作为执行器并创建Transaction事务对象;
之后会根据SQL类型判断接口中方法返回值类型进而决定调用SqlSession的具体方法:如果是查询操作且返回多个数据结果则调用selectList方法;如果仅有一个数据结果则调用selectOne方法;
当所有SQL操作完成后提交事务并释放相关资源以确保系统的稳定性;

JSP

JSP页面中包含内容:

复制代码
    	A、HTML
       
    B、注释[java注释   HTML注释   JSP注释]
    
    C、Java代码 [小脚本  声明式  表达式]

小脚本和声明之间区别

复制代码
    A、小脚本中定义java代码最后都会放到service方法中  声明定义代码最后都在方法外
    
     B、小脚本中定义所有变量都是局部变量 声明中定义变量是成员变量
     
     C、小脚本中不可以定义方法  声明中可以定义方法
    
     D、小脚本中可以使用JSP内置对象  声明中不可以使用JSP内置对象

JSP注释和HTML注释区别

复制代码
    		JSP注释更优越  JSP减少网络带宽

何为JSP的内置对象

JSP系统预设了一些常用的对象,并将其作为内置对象提供给开发者使用。当我们采用内置对象时,则无需自行搭建数据结构或获取外部信息;这些操作会在后台由JSP框架完成。因此,在开发过程中可以直接引用这些已建立的对象进行操作

JSP内置对象一共9个

复制代码
    	   例如:out /resuest/response

9个内置对象中 包含4个作用域 pageContext 、request、session、application

复制代码
    		  request---->HttpServletRequest;
    		 
    		  response---->HttpServletResponse;
    		  
    		  page-------->JspPage;  涵盖就是页面配置信息
    		  
    		  pageContext->PageContext;
    		  
    		  session----->HttpSession;
    		 
    		  application->ServletContext;
    		  
    		  config------>ServletConfig;
    		 
    		  out--------->PrintWriter;
    		  
    		  exception :需要在page上指定isErrorPage="true"

各种路径

复制代码
    <html>
    <head>
    <base href="<%=request.getContextPath()+"/"%>"/>
    <title>Title</title>
    </head>
    <body>
    
    <%--优点:任何服务器中任何资源都可以访问   缺点:路径书写比较长 比较麻烦--%>
      
      <h3>绝对路径(协议+IP+端口+跳转地址)</h3>
    <a href="http://127.0.0.1:8080/web05_war_exploded/path1/add.jsp">同一个项目中同一个目录中文件add.jsp</a><br/>
    <a href="http://127.0.0.1:8080/web05_war_exploded/path1/subpath/sub.jsp">同一个项目中子级目录中文件subpath/sub.jsp</a><br/>
    <a href="http://127.0.0.1:8080/web05_war_exploded/path2/update.jsp">同一个项目中同级目录中文件path2/update.jsp</a><br/>
    <a href="http://127.0.0.1:8080/web05_war_exploded/index.jsp">同一个项目中父级目录中文件index.jsp</a><br/>
    <a href="http://127.0.0.1:8080/web05_war_exploded/abc/PathServlet">同一个项目中Servlet如何访问</a><br/>
    <a href="http://127.0.0.1:8080/web04_war_exploded/index.jsp">web04项目中index.jsp文件(同一个服务器)</a><br/>
    <a href="http://www.bjsxt.com">北京尚学堂官网如何访问(不同服务器)</a><br/>
    
    
    <%--优点:路径书写方式比较简单  缺点:只可以访问同一个服务器中项目--%>
    
      <h3>根路径(必须/开始 后面紧跟【项目上下文】 +访问地址)</h3>
    <a href="/web05_war_exploded/path1/add.jsp">同一个项目中同一个目录中文件add.jsp</a><br/>
    <a href="/web05_war_exploded/path1/subpath/sub.jsp">同一个项目中子级目录中文件subpath/sub.jsp</a><br/>
    <a href="/web05_war_exploded/path2/update.jsp">同一个项目中同级目录中文件path2/update.jsp</a><br/>
    <a href="/web05_war_exploded/index.jsp">同一个项目中父级目录中文件index.jsp</a><br/>
    <a href="/web05_war_exploded/abc/PathServlet">同一个项目中Servlet如何访问</a><br/>
    <a href="/web04_war_exploded/index.jsp">web04项目中index.jsp文件(同一个服务器)</a><br/>
    <%--<a href="http://www.bjsxt.com">北京尚学堂官网如何访问(不同服务器)</a><br/>--%>
    
    
    <%--优点:路径书写方式比较简单  缺点:只可以访问同一个服务器中项目  必须找相对关系 如果path的位置改变了 这个相对路径全部不对--%>
    
      <h3>相对路径A(直接写路径 在最前面不需要写/ 相对于本身  ./当前目录  ../返回上级)</h3>
    <a href="add.jsp">同一个项目中同一个目录中文件add.jsp</a><br/>
    <a href="subpath/sub.jsp">同一个项目中子级目录中文件subpath/sub.jsp</a><br/>
    <a href="../path2/update.jsp">同一个项目中同级目录中文件path2/update.jsp</a><br/>
    <a href="../index.jsp">同一个项目中父级目录中文件index.jsp</a><br/>
    <a href="../abc/PathServlet">同一个项目中Servlet如何访问</a><br/>
    <a href="../../web04_war_exploded/index.jsp">web04项目中index.jsp文件(同一个服务器)</a><br/>
    
    
    <%--优点:书写最简单,并且不需要找相对关系只要顺着上面base继续写即可   缺点:只可以访问当前项目中内容--%>
      
      <h3>相对路径B 相对于base</h3>
    <a href="path1/add.jsp">同一个项目中同一个目录中文件add.jsp</a><br/>
      
    <a href="path1/subpath/sub.jsp">同一个项目中子级目录中文件subpath/sub.jsp</a><br/>
    <a href="path2/update.jsp">同一个项目中同级目录中文件path2/update.jsp</a><br/>
    <a href="index.jsp">同一个项目中父级目录中文件index.jsp</a><br/>
    <a href="abc/PathServlet">同一个项目中Servlet如何访问</a><br/>
    <%--    <a href="/web04_war_exploded/index.jsp">web04项目中index.jsp文件(同一个服务器)</a><br/>--%>
    
    
    	</body>
    </html>

路径规则适用于页面中 img a form …

路径使用场景

复制代码
    		如果访问的都是当前项目文件: 推荐使用基于base的路径
    		
    		如果访问是同一个服务器中其他项目文件:推荐使用根路径
    		
    		如果访问是不同服务器中项目:必须使用绝对路径

MySQL的优点

MySQL之所以受到业界人士的青睐,主要是因为其具有以下几方面优点:

• 1. 开放源代码

复制代码
    MySQL最强大的优势之一在于它是一个开放源代码的数据库管理系统。开源的特点是给予了用户根据自己需要修改DBMS的自由。MySQL采用了General Public License,这意味着授予用户阅读、修改和优化源代码的权利,这样即使是免费版的MySQL的功能也足够强大,这也是为什么MySQL越来越受欢迎的主要原因。

• 2. 跨平台

复制代码
    MySQL可在不同的操作系统下运行,简单地说,MySQL可以支持Windows系统、UNIX系统、Linux系统等多种操作系统平台。这意味着在一个操作系统中实现的应用程序可很方便移植到其他操作系统下。

• 3. 轻量级

复制代码
    MySQL的核心程序完全采用多线程编程,这些线程都是轻量级的进程,它在灵活地为用户提供服务的同时,又不会占用过多的系统资源。因此MySQL能够更快速、高效的处理数据。

• 4. 成本低

复制代码
    MySQL分为社区版和企业版,社区版是完全免费的,而企业版是收费的。即使在开发中需要用到一些付费的附加功能,价格相对于昂贵的Oracle、DB2等也是有很大优势的。其实免费的社区版也支持多种数据类型和正规的SQL查询语言,能够对数据进行各种查询、增加、删除、修改等操作,所以一般情况下社区版就可以满足开发需求了,而对数据库可靠性要求比较高的企业可以选择企业版。

注意:

复制代码
    社区版与企业版主要的区别是: 
    		 社区版包含所有MySQL的最新功能,而企业版只包含稳定之后的功能。换句话说,社区版可以理解为是企业版的测试版。  
    		
    		MySQL官方的支持服务只是针对企业版,如果用户在使用社区版时出现了问题,MySQL官方是不负责任的。

从整体来看 MySQL 是一款开源软件 无成本 且资源占用较低的关系型数据库。它具备占用资源少 运行效率高 维护费用低 以及开放源代码等特点。未来 MySQL 的发展前景十分广阔无垠。

MySQL中常见的数据类型

类型 描述
int 整型
double 浮点型
varchar 字符串型
char(M) 0~255字符
date 日期类型,格式为:yyyy-MM-dd
datetlme ‘YYYY-MM-DD HH:MM:SS’

抽象类和接口的区别

复制代码
    	抽象类:
 抽象方法,只有行为的概念,没有具体的行为实现。使用abstract关键字修饰,没有方法体。子类必须重写这些抽象方法。
 包含抽象方法的类,一定是抽象类。
 抽象类只能被继承,一个类只能继承一个抽象类。
    
    	接口:
 全部的方法都是抽象方法,属性都是常量
 不能实例化,可以定义变量。
 接口变量可以引用具体实现类的实例
 接口只能被实现,一个具体类实现接口,必须实现全部的抽象方法
    ·		5. 接口之间可以多实现
  一个具体类可以实现多个接口,实现多继承现象

值传递和引用传递的区别

复制代码
    值传递 传的是值
    引用传递 传递的是地址
    		理论上说,java都是引用传递,对于基本数据类型,传递是值的副本,而不是值本身。
    		对于对象类型,传递是对象的引用,当在一个方法操作操作参数的时候,其实操作的是引用所指向的对象。

Cookie和session的区别

复制代码
 数据储存位置  cookie在客户端浏览器  session 在服务器
    2. 安全方面 coolie相较session不安全 
    3. 数据大小 cookie保存的数据不能超4k 很多浏览器规定一个站点不能超20个对于session来说并没有上限,但出于对服务器端的性能考虑,session内不要存放过多的东西,并且设置session删除机制。
    4. 有效期不同开发可以通过设置cookie的属性,达到使cookie长期有效的效果。session依赖于名为JSESSIONID的cookie,而cookie JSESSIONID的过期时间默认为-1,只需关闭窗口该session就会失效,因而session不能达到长期有效的效果。
    5. 服务器压力不同cookie保管在客户端,不占用服务器资源。对于并发用户十分多的网站,cookie是很好的选择。 session是保管在服务器端的,每个用户都会产生一个session。假如并发访问的用户十分多,会产生十分多的session,耗费大量的内存。
    6.  跨域支持上不同cookie支持跨域名访问。session不支持跨域名访问

==和equals的区别

复制代码
    ==:
    == 比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象的地址是否相同,即是否是指相同一个对象。比较的是真正意义上的指针操作。
    1、比较的是操作符两端的操作数是否是同一个对象。
    2、两边的操作数必须是同一类型的(可以是父子类之间)才能编译通过。
    3、比较的是地址,如果是具体的阿拉伯数字的比较,值相等则为true,如:
    int a=10 与 long b=-10L 与 double c=10.0都是相同的(为true),因为他们都指向地址为10的堆。
    
    equals:
    equals用来比较的是两个对象的内容是否相等,由于所有的类都是继承自java.lang.Object类的,所以适用于所有对象,如果没有对该方法进行覆盖的话,调用的仍然是Object类中的方法,而Object中的
    equals方法返回的却是==的判断。
    
    总结:
    所有比较是否相等时,都是用equals 并且在对常量相比较时,把常量写在前面,因为使用object的
    equals object可能为null 则空指针
    在阿里的代码规范中只使用equals ,阿里插件默认会识别,并可以快速修改,推荐安装阿里插件来排查老代码使用“==”,替换成equals

Jsp和Servlet的区别

复制代码
 jsp经编译后就变成了Servlet。
    2. jsp更擅长表现于页面显示,servlet更擅长于逻辑控制。 
    3. Servlet中没有内置对象,Jsp中的内置对象都是必须通过HttpServletResponse对象以及HttpServlet对象得到。
    4. 而Servlet则是个完整的Java类,这个类的Service方法用于生成对客户端的响应 
    5. Servlet的应用逻辑是在Java文件中,并且完全从表示层中的HTML里分离开来。 
    6. 而JSP的情况是Java和HTML可以组合成一个扩展名为.jsp的文件。 
    7. JSP侧重于视图,Servlet主要用于控制逻辑 
    8. Servlet更多的是类似于一个Controller,用来做控制。

JDK,JRE,JVM的区别

复制代码
    Jdk是工具包 包含了jre(运行环境)和jvm是虚拟机
    
    1. JDK:java development kit:java开发工具包,是开发人员所需要安装的环境
    2. JRE:java runtime environment:java运行环境,java程序运行所需要安装的环境

Get请求方式和Post请求方式的区别

复制代码
    * GET请求会被浏览器主动cache(缓存),而POST不会,除非手动设置。
    	* GET请求只能进行url编码,而POST支持多种编码方式。* * GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
    	* GET请求在URL中传送的参数是有长度限制的,而POST则没有。对参数的数据类型GET只接受ASCII字符,从http协议的角度来说 get请求 和 post请求都没有限制, 这个长度限制是浏览器(各种浏览器) 和 服务器 提出来的	长度限制	http协议本身就是用来做数据通信用的(包含请求 & 响应)	不同的浏览器对长度的限制是不一样的:	IE浏览器对长度限制为 最大2KB	chrome浏览器对长度限制为 最大8kb 经过我们实测中文 1.81kb都报错  字符串1.81kb没问题,不报错	其他浏览器各自有各自的长度 需要的时候 去看一下就可以了 
    	* POST既可是字符也可是字节。
    	* GET相比POST来说不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。

String StringBuffer 和String Builder有什么区别

复制代码
     1、StringBuffer与StringBuilder中的方法和功能完全是等价的。
     2、只是StringBuffer中的方法大都采用了 synchronized 关键字进行修饰,因此是线程安全的,而StringBuilder没有这个修饰,可以被认为是线程不安全的。  
     3、在单线程程序下,StringBuilder效率更快,因为它不需要加锁,不具备多线程安全而StringBuffer则每次都需要判断锁,效率相对更低
复制代码
    String是只读字符串,它并不是基本数据类型,而是一个对象。从底层源码来看是一个fifinal类型的字符数组,所引用的字符串不能被改变,一经定义,无法再增删改。每次对String的操作都会生成新的
    String对象
    每次+操作 : 隐式在堆上new了一个跟原字符串相同的StringBuilder对象,再调用append方法 拼接
    +后面的字符。
    
    StringBuffer与StringBuilder都继承了AbstractStringBulder类,而AbtractStringBuilder又实现了CharSequence接口,两个类都是用来进    行字符串操作的。
    在做字符串拼接修改删除替换时,效率比string更高。
    StringBuffer是线程安全的,Stringbuilder是非线程安全的。所以Stringbuilder比stringbuffer效率更高,StringBuffer的方法大多都加了
    synchronized关键字

bean生命周期

复制代码
 bean的生命周期 通过构造函数(不管是有参还是无参)来实例化bean
 为bean的属性设置值 和对其他的bean引用(调用set方法)
 在初始化之前执行的方法    postProcessBeforeInitialization 
 调用bean的初始化的方法(需要进行配置) xml文件中加上init -method 
 在初始化之后执行的方法    postProcessAfterInitialization 
 获取创建bean实例对象 
 执行销毁方法

java有哪些异常

复制代码
    	NullPointerException:空指针异常.
    	SQLException:mysql语句异常
    	IndexOutOfBoundsException:下标超出

new arrayList() 默认的长度为多少?

复制代码
    ​				10

了解Map吗? HashMap底层原理

复制代码
    	HashMap底层基于数组+链表+红黑树。HashMap存储的是键值对,
    	将每个键值对保存到Node对象中,然后再把Node对象存到Node数组中。

重载和重写的区别

重写**(Override)**

复制代码
    从字面上看,重写就是 重新写一遍的意思。其实就是在子类中把父类本身有的方法重新写一遍。子类继承了父类原有的方法,
    但有时子类并不想原封不动的继承父类中的某个方法,所以在方法名,参数列表,返回类型(除过子类中方法的返回值是父类中方法
    返回值的子类时)都相同的情况下, 对方法体进行修改或重写,这就是重写。但要注意子类函数的访问修饰权限不能少于父类的。
复制代码
    public class Father {
    
    public static void main(String[] args) {
    
    // TODO Auto-generated method stub
    
    Son s = new Son();
    
    s.sayHello();
    
    }
    
    public void sayHello() {
    
    System.out.println("Hello");
    	}
    }
    
    class Son extends Father{
    
    @Override
    
    public void sayHello() {
    
    // TODO Auto-generated method stub
    
    System.out.println("hello by ");
    
    	}
    
    }
复制代码
    原因: 在某个范围内的整型数值的个数是有限的,而浮点数却不是。
    重写 总结:
    1.发生在父类与子类之间
    2.方法名,参数列表,返回类型(除过子类中方法的返回类型是父类中返回类型的子类)必须相同
    3.访问修饰符的限制一定要大于被重写方法的访问修饰符(public>protected>default>private)
    4.重写方法一定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常

重载(Overload)

复制代码
    在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同甚至是参数顺序不同) 则视为重载。同时,重载对返回类型没有要求,
    可以相同也可以不同,但不能通过返回类型是否相同来判断重载。
复制代码
    public class Father {
    
    public static void main(String[] args) {
    
    // TODO Auto-generated method stub
    
    Father s = new Father();
    
    s.sayHello();
    
    s.sayHello("wintershii");
    
    }
    
    public void sayHello() {
    
    System.out.println("Hello");
    
    }
    
    public void sayHello(String name) {
    
    System.out.println("Hello" + " " + name);
    
    	}
    
    }
复制代码
    重载总结:
    1.重载Overload是一个类中多态性的一种表现
    2.重载要求同名方法的参数列表不同(参数类型,参数个数甚至是参数顺序)
    3.重载的时候,返回值类型可以相同也可以不相同。无法以返回型别作为重载函数的区分标准

怎么保证线程安全

复制代码
 针对第一个因素,JDK里面提供了很多atomic类, AtomicLong, AtomicBoolean等等,
    	这些类本身可以通过CAS来保证操作的原子性;Java也提供了锁机制,
    	锁内的代码块在同一时刻只能有一个线程执行,例如synchronized关键字。 
    
    2. 针对第二个因素,可用synchronized关键字加锁保证,
    	将操作共享数据的语句加入synchronized关键字,
       同一时刻只能有一个线程执行,直到完成。 也可用volatile关键字,可以保证修改共享数据后对其他线程可见。 
    
    3. 针对第三个因素可以使用Lock锁。Lock锁很灵活,但需要手动释放和开启。
    	 在并发量比较高的情况下,Lock锁优于synchronized关键字。

arraylist怎么进行扩容的?什么时候扩容?

复制代码
    扩容的时候 会以新的容量建一个原数组的拷贝 修改原数组 指向这个新数组 原数组被抛弃 
    会被cg回收当数组的大小大于初始容量的时候 (比如初始值为10 当添加第11个元素的时候 )
    就会进行扩容 新容量为旧容量的1.5倍

MySQL事务隔离级别的支持情况概述了其在数据库操作中的安全性保障机制。具体来说,在MySQL中定义的四种隔离级别分别对应着解决不同类型事务冲突的有效策略:读锁(Read-ahead log isolation)能够防止并发读操作导致的数据不一致;共享锁(Shared-optimized isolation)则通过允许共享数据的一致性读取来平衡性能与安全的关系; pessimistic isolation则通过检查点机制确保所有并发操作都能获得互斥资源以维持数据一致性。默认情况下MySQL采用了共享锁(shared optimization)作为其默认的事务隔离级别以优化系统性能的同时保证数据的一致性。

复制代码
    未提交读(Read Uncommitted)允许脏读 可能读到其他会话中未提交事务修改的数据不重复度(Read Committed)
    						   只能读取到已经提交的数据 
    						   
    可重复读(Repeated Read)在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。
    
    在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读,但是innoDB解决了幻读串行读((Repeated Read))
    完全串行化的读 每次读都需要获得表级共享锁 读和写都会阻塞 默认 可重读读

char和vachar区别

复制代码
    Char最大长度是255字符 varchar最大长度65535个字符Char是定长的 不足的地方用隐藏空格填充  
    
    varchar是不定长的Char会浪费空间 varchar不会浪费空间Char查找效率高 varchar查找效率低

BIO、NIO、AIO 有什么区别

BIO (Blocking I/O)

复制代码
    同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。这里假设		
    一个烧开水的场景,有一排水壶在烧开水,BIO的工作模式就是, 
    叫一个线程停留在一个水壶那,直到这个水壶烧开,才去处理下一个水壶。但是实际上线程在等待水壶烧开的时间段什么都没有做。

NIO (New I/O)

复制代码
     同时支持阻塞与非阻塞模式,但这里我们以其同步非阻塞I/O模式来说明,
     那么什么叫做同步非阻塞?如果还拿烧开水来说,NIO的做法是叫一个线程不断的轮询每个水壶的状态,
     看看是否有水壶的状态发生了改变,从而进行下一步的操作。

AIO ( Asynchronous I/O)

复制代码
     异步非阻塞I/O模型。异步非阻塞与同步非阻塞的区别在哪里?
     异步非阻塞无需一个线程去轮询所有IO操作的状态改变,在相应的状态改变后,系统会通知对应的线程来处理。
     对应到烧开水中就是,为每个水壶上面装了一个开关,水烧开之后,水壶会自动通知我水烧开了。

java基本数据类型?

复制代码
    Java 中的基本数据类型只有  8 个:
    
    		byte 、short、int、long、float、double、  char、boolean;
    		
    		除了基本类型(primitive type), 剩下的都是引用类型(reference type),
    		Java 5 以后引入的枚举类型也算是一种比较特殊的引用类型。

如何实现序列化

复制代码
    序列化是将对象状态转换为可保持或传输的格式的过程。说明白点就是你可以用对象输出流输出到文件.
    如果不序列化输出的话.很可能会乱!
    
     实现方式是实现java.io.Serializable接口.这个接口不需要实现任何具体方法.
     只要implements java.io.Serializable 就好了
     
    java中的序列化机制能够将一个实例对象(只序列化对象的属性值,而不会去序列化什么所谓的方法。)的状态信息写入到一个
    字节流中使其可以通过socket进行传输、或者持久化到存储数据库或文件系统中;然后在需要的时候通过字节流中的信息来
    重构一个相同的对象。
    
    一般而言,要使得一个类可以序列化,只需简单实现java.io.Serializable接口即可。

怎么保证线程安全

复制代码
    主要有三点:
    			原子性:一个或者多个操作在CPU执行的过程中被中断
    			可见性:一个线程对共享变量的修改,另外一个线程不能立刻看到
    			有序性:程序执行的顺序没有按照代码的先后顺序执行

Redis的数据类型有哪些?

概述:

复制代码
    Redis的键值可以使用物种数据类型:字符串,散列表,列表,集合,有序集合。本文详细介绍这五种数据类型的使用方法。
    本文命令介绍部分只是列举了基本的命令,至于具体的使用示例,可以参考Redis官方文档:Redis命令大全

1.字符串类型

复制代码
    字符串是Redis中最基本的数据类型,它能够存储任何类型的字符串,包含二进制数据。可以用于存储邮箱,JSON化的对象,甚至是一张图片,
    一个字符串允许存储的最大容量为512MB。字符串是其他四种类型的基础,与其他几种类型的区别从本质上来说只是组织字符串的方式不同而已。

基本命令

字符串操作

复制代码
    1、SET赋值,用法:SET key value
    
    2、GET取值,用法:GET key
    
    3、INCR递增数字,仅仅对数字类型的键有用,相当于Java的i++运算,用法:INCR key
    
    4、INCRBY增加指定的数字,仅仅对数字类型的键有用,相当于Java的i+=3,用法:INCRBY key increment,意思是key自增increment,increment可以为负数,表示减少。
    
    5、DECR递减数字,仅仅对数字类型的键有用,相当于Java的i–,用法:DECR key
    
    6、DECRBY减少指定的数字,仅仅对数字类型的键有用,相当于Java的i-=3,用法:DECRBY key decrement,意思是key自减decrement,decrement可以为正数,表示增加。
    
    7、INCRBYFLOAT增加指定浮点数,仅仅对数字类型的键有用,用法:INCRBYFLOAT key increment
    
    8、APPEND向尾部追加值,相当于Java中的”hello”.append(“ world”),用法:APPEND key value
    
    9、STRLEN获取字符串长度,用法:STRLEN key
    
    10、MSET同时设置多个key的值,用法:MSET key1 value1 [key2 value2 ...]
    
    11、MGET同时获取多个key的值,用法:MGET key1 [key2 ...]

位操作

复制代码
    1、GETBIT获取一个键值的二进制位的指定位置的值(0/1),用法:GETBIT key offset
    
    2、SETBIT设置一个键值的二进制位的指定位置的值(0/1),用法:SETBIT key offset value
    
    3、BITCOUNT获取一个键值的一个范围内的二进制表示的1的个数,用法:BITCOUNT key [start end]
    
    4、BITOP该命令可以对多个字符串类型键进行位运算,并将结果存储到指定的键中,BITOP支持的运算包含:OR,AND,XOR,NOT,用法:BITOP OP desKey key1 key2
    
    5、BITPOS获取指定键的第一个位值为0或者1的位置,用法:BITPOS key 0/1 [start, end]

2.散列类型

复制代码
    散列类型相当于Java中的HashMap,他的值是一个字典,保存很多key,value对,每对key,value的值个键都是字符串类型,
    换句话说,散列类型不能嵌套其他数据类型。一个散列类型键最多可以包含2的32次方-1个字段。

基本命令

复制代码
    1、 HSET赋值,用法:HSET key field value
    
    2、 HMSET一次赋值多个字段,用法:HMSET key field1 value1 [field2 values]
    
    3、 HGET取值,用法:HSET key field
    
    4、 HMGET一次取多个字段的值,用法:HMSET key field1 [field2]
    
    5、 HGETALL一次取所有字段的值,用法:HGETALL key
    
    6、  HEXISTS判断字段是否存在,用法:HEXISTS key field
    
    7、 HSETNX当字段不存在时赋值,用法:HSETNX key field value
    
    8、 HINCRBY增加数字,仅对数字类型的值有用,用法:HINCRBY key field increment
    
    9、 HDEL删除字段,用法:HDEL key field
    
    10、 HKEYS获取所有字段名,用法:HKEYS key
    
    11、 HVALS获取所有字段值,用法:HVALS key
    
    12、 HLEN获取字段数量,用法:HLEN key

3.列表类型

复制代码
    列表类型(list)用于存储一个有序的字符串列表,常用的操作是向队列两端添加元素或者获得列表的某一片段。
    列表内部使用的是双向链表(double linked list)实现的,所以向列表两端添加元素的时间复杂度是O(1),
    获取越接近列表两端的元素的速度越快。但是缺点是使用列表通过索引访问元素的效率太低(需要从端点开始遍历元素)。
    所以列表的使用场景一般如:朋友圈新鲜事,只关心最新的一些内容。借助列表类型,Redis还可以作为消息队列使用。

基本命令

复制代码
    1、 LPUSH向列表左端添加元素,用法:LPUSH key value
    
    2、 RPUSH向列表右端添加元素,用法:RPUSH key value
    
    3、 LPOP从列表左端弹出元素,用法:LPOP key
    
    4、 RPOP从列表右端弹出元素,用法:RPOP key
    
    5、 LLEN获取列表中元素个数,用法:LLEN key
    
    6、 LRANGE获取列表中某一片段的元素,用法:LRANGE key start stop,index从0开始,-1表示最后一个元素
    
    7、 LREM删除列表中指定的值,用法:LREM key count value,删除列表中前count个值为value的元素,当count>0时从左边开始数,count<0时从右边开始数,count=0时会删除所有值为value的元素
    
    8、 LINDEX获取指定索引的元素值,用法:LINDEX key index
    
    9、 LSET设置指定索引的元素值,用法:LSET key index value
    
    10、 LTRIM只保留列表指定片段,用法:LTRIM key start stop,包含start和stop
    
    11、 LINSERT像列表中插入元素,用法:LINSERT key BEFORE|AFTER privot value,从左边开始寻找值为privot的第一个元素,然后根据第二个参数是BEFORE还是AFTER决定在该元素的前面还是后面插入value
    
    12、 RPOPLPUSH将元素从一个列表转义到另一个列表,用法:RPOPLPUSH source destination

4.集合类型

复制代码
    集合在概念在高中课本就学过,集合中每个元素都是不同的,集合中的元素个数最多为2的32次方-1个,集合中的元素师没有顺序的。

基本命令

复制代码
  SADD添加元素,用法:SADD key value1 [value2 value3 ...]
    
    2.  SREM删除元素,用法:SREM key value2 [value2 value3 ...]
    
    3.  SMEMBERS获得集合中所有元素,用法:SMEMBERS key
    
    4.  SISMEMBER判断元素是否在集合中,用法:SISMEMBER key value
    
    5.  SDIFF对集合做差集运算,用法:SDIFF key1 key2 [key3 ...],先计算key1和key2的差集,然后再用结果与key3做差集
    
    6.  SINTER对集合做交集运算,用法:SINTER key1 key2 [key3 ...]
    
    7.  SUNION对集合做并集运算,用法:SUNION key1 key2 [key3 ...]
    
    8.  SCARD获得集合中元素的个数,用法:SCARD key
    
    9.  SDIFFSTORE对集合做差集并将结果存储,用法:SDIFFSTORE destination key1 key2 [key3 ...]
    
    10.  SINTERSTORE对集合做交集运算并将结果存储,用法:SINTERSTORE destination key1 key2 [key3 ...]
    
    11.  SUNIONSTORE对集合做并集运算并将结果存储,用法:SUNIONSTORE destination key1 key2 [key3 ...]
    
    12.  SRANDMEMBER随机获取集合中的元素,用法:SRANDMEMBER key [count],当count>0时,会随机中集合中获取count个不重复的元素,当count<0时,随机中集合中获取|count|和可能重复的元素。
    
    13.  SPOP从集合中随机弹出一个元素,用法:SPOP key

5.有序集合类型

复制代码
    有序集合类型与集合类型的区别就是他是有序的。有序集合是在集合的基础上为每一个元素关联一个分数,
    这就让有序集合不仅支持插入,删除,判断元素是否存在等操作外,还支持获取分数最高/最低的前N个元素。
    有序集合中的每个元素是不同的,但是分数却可以相同。有序集合使用散列表和跳跃表实现,
    即使读取位于中间部分的数据也很快,时间复杂度为O(log(N)),有序集合比列表更费内存。

基本命令

复制代码
  ZADD添加元素,用法:ZADD key score1 value1 [score2 value2 score3 value3 ...]
    
    2.  ZSCORE获取元素的分数,用法:ZSCORE key value
    
    3.  ZRANGE获取排名在某个范围的元素,用法:ZRANGE key start stop [WITHSCORE],按照元素从小到大的顺序排序,从0开始编号,包含start和stop对应的元素,WITHSCORE选项表示是否返回元素分数
    
    4.  ZREVRANGE获取排名在某个范围的元素,用法:ZREVRANGE key start stop [WITHSCORE],和上一个命令用法一样,只是这个倒序排序的。
    
    5.  ZRANGEBYSCORE获取指定分数范围内的元素,用法:ZRANGEBYSCORE key min max,包含min和max,(min表示不包含min,(max表示不包含max,+inf表示无穷大
    
    6.  ZINCRBY增加某个元素的分数,用法:ZINCRBY key increment value
    
    7.  ZCARD获取集合中元素的个数,用法:ZCARD key
    
    8.  ZCOUNT获取指定分数范围内的元素个数,用法:ZCOUNT key min max,min和max的用法和5中的一样
    
    9.  ZREM删除一个或多个元素,用法:ZREM key value1 [value2 ...]
    
    10.  ZREMRANGEBYRANK按照排名范围删除元素,用法:ZREMRANGEBYRANK key start stop
    
     11.  ZREMRANGEBYSCORE按照分数范围删除元素,用法:ZREMRANGEBYSCORE key min max,min和max的用法和4中的一样
    
    12.  ZRANK获取正序排序的元素的排名,用法:ZRANK key value
    
    13.  ZREVRANK获取逆序排序的元素的排名,用法:ZREVRANK key value
    
    14.  ZINTERSTORE计算有序集合的交集并存储结果,用法:ZINTERSTORE destination numbers key1 key2 [key3 key4 ...] 
    		WEIGHTS weight1 weight2 [weight3 weight4 ...] AGGREGATE SUM | MIN | MAX,numbers表示参加运算的集合个数,
    		 weight表示权重,aggregate表示结果取值
    
    15.  ZUNIONSTORE计算有序几个的并集并存储结果,用法和14一样,不再赘述。

描述下边路缓存?

复制代码
    数据先找cache,cache没有数据,找数据库,在将数据库查询结果保存在cache。先写DB,再删cache,在同步cache(解决写穿透)

cache aside pattern ----采用缓存机制的方式进行操作,在将来自数据存储区的数据加载至缓存后能够显著提升性能,并且确保在内存中的所有相关项始终处于一致状态;此外,在执行相关操作时还能够保证底层数据存储的完整性

在redis中经常使用这种方式保证数据库和缓存双写的数据的一致性

当处理数据读取时,在确认当前是否有缓存在的情况下(若无则直接从数据库中获取),随后将获取的数据放入缓存后再返回相应的响应信息。处理更新操作时,在完成数据库更新后清除过期的缓存项。

为什么是删除缓存,不是更新缓存

因为在复杂的缓存场景中,缓存不单单是数据库中直接取出来的值

这个缓存到底会不会被频繁访问到?

例如,在修改某个表的字段时(比如可能),其对应的缓存则需从另外两个表获取数据并进行计算(以便),从而得到最新的缓存值(才能)。此外,在频繁更新或维护这些数据源时所造成的成本通常也比较高(那么)。是不是意味着每次修改数据库都需要同步相应的缓存?然而,在某些特定的情况下(比如),也许并不总是如此(比如)。比如说,在较为复杂的系统或业务需求下(比如),就可能出现不同的处理方式(比如)。

最初级的缓存不一致问题及解决方案

在开始更新缓存之前,在完成一次完整的操作后。如果在这一过程中遇到故障,则会使得数据库处于最新状态。然而,在当前的缓存内容仍为旧版本的数据情况下,则会出现系统间信息不一致的问题。

处理问题的方法是在尝试更新 databases 之前清理 cache 状态。当遇到无法完成 databases 更新时,请注意此时 databases 内的数据已为旧版本,并且对应的 cache entries 已经为空。因此不会导致 data inconsistency 的问题。

比较复杂的数据不一致问题及解决

数据发生变更

首先进行了清除原有缓存的操作
接着转而去处理数据库更新
此时还未完成当前的数据更新
接着来了一个请求
它先是尝试读取现有的状态
结果发现当前缓存为空
于是就调用系统来获取最新的信息
最后又将这些旧数据存储在缓冲区供后续使用
整个流程下来后完成了对数据库系统的更新操作

为了解决这个问题,在处理数据变更时会采用以下步骤:首先清除缓存数据再更新数据库信息;当一个读请求到来时系统会立即发送相关的缓存更新请求至队列中,在队列中等待直到缓存更新完成。系统发现优化点在于串行处理多个更新请求没有实际意义因此会过滤掉已经在队列中的相同类型请求。处理完上一个操作后进行下一次操作也就是从数据库读取最新的值并将其写入缓存中;如果在规定时间内无法获取新值则系统将返回当前缓存值;而当等待时间超出设定阈值时系统则切换为从数据库读取旧值以保证数据一致性。

redis的持久化策略有哪些?分别有什么特点?

你知道反射吗,可以说一下什么是反射吗

复制代码
    	反射是一种计算机处理方式。有程序可以访问、检测和修改它本身状态或行为的这种能力。
    	能提供封装程序集、类型的对象。(程序集包含模块,而模块包含类型,类型又包含成员。)
    优点:
    	1、反射提高了程序的灵活性和扩展性。
    	2、降低耦合性,提高自适应能力。
    	3、它允许程序创建和控制任何类的对象,无需提前硬编码目标类。
    缺点:
    	1、性能问题:使用反射基本上是一种解释操作,用于字段和方法接入时要远慢于直接代码。
    		因此反射机制主要应用在对灵活性和拓展性要求很高的系统框架上,普通程序不建议使用。
    	2、使用反射会模糊程序内部逻辑;程序员希望在源代码中看到程序的逻辑,反射却绕过了源代码的技术,
    		因而会带来维护的问题,反射代码比相应的直接代码更复杂。

AOP/OOP

区分

复制代码
    AOP、OOP在字面上虽然非常类似,但却是面向不同领域的两种设计思想。OOP(面向对象编程)针对业务处理过程的实体及其属性和行为进行抽象封装,
    以获得更加清晰高效的逻辑单元划分。
    
    而AOP则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。
    这两种设计思想在目标上有着本质的差异。
    
    上面的陈述可能过于理论化,举个简单的例子,对于“雇员”这样一个业务实体进行封装,自然是OOP/OOD的任务,我们可以为其建立一个“Employee”类,
    并将“雇员”相关的属性和行为封装其中。而用AOP设计思想对“雇员”进行封装将无从谈起。
    
    同样,对于“权限检查”这一动作片断进行划分,则是AOP的目标领域。而通过OOD/OOP对一个动作进行封装,则有点不伦不类。
    
    换而言之,OOD/OOP面向名词领域,AOP面向动词领域。

关系

复制代码
    很多人在初次接触 AOP 的时候可能会说,AOP 能做到的,一个定义良好的 OOP 的接口也一样能够做到,我想这个观点是值得商榷的。
    AOP和定义良好的 OOP 的接口可以说都是用来解决并且实现需求中的横切问题的方法。但是对于 OOP 中的接口来说,它仍然需要我们在相应的模块中去调用
    该接口中相关的方法,这是 OOP 所无法避免的,并且一旦接口不得不进行修改的时候,所有事情会变得一团糟;AOP 则不会这样,
    你只需要修改相应的 Aspect,再重新编织(weave)即可。 当然,AOP 也绝对不会代替 OOP。
    核心的需求仍然会由 OOP 来加以实现,而 AOP 将会和 OOP 整合起来,以此之长,补彼之短。

什么是缓存击穿?如何解决?

复制代码
    如果缓存中的数据在某个时刻批量过期,导致大部分用户的请求都会直接落在数据库上,这种现象就叫作缓存击穿。
    
    造成缓存击穿的主要原因就是:我们为缓存中的数据设置了过期时间。如果在某个时刻从数据库获取了大量的数据,并设置了相同的过期时间,
    这些缓存的数据就会在同一时刻失效,造成缓存击穿问题

解决:

复制代码
    对于比较热点的数据,我们可以在缓存中设置这些数据永不过期;也可以在访问数据的时候,在缓存中更新这些数据的过期时间;
    如果是批量入库的缓存项,我们可以为这些缓存项分配比较合理的过期时间,避免同一时刻失效。
    
    还有一种解决方案就是:使用分布式锁,保证对于每个 Key 同时只有一个线程去查询后端的服务,某个线程在查询后端服务的同时,
    其他线程没有获得分布式锁的权限,需要进行等待。不过在高并发场景下,这种解决方案对于分布式锁的访问压力比较大。

什么是缓存穿透?如何解决?

复制代码
    	缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库再
    	查询一遍,然后返回空(相当于进行了两次无用的查询)。这样请求就绕过缓存直接查数据库,这也是经常提的缓存命中率问题。

解决办法;

复制代码
    	最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从
    	而避免了对底层存储系统的查询压力。另外也有一个更为简单粗暴的方法,如果一个查询返回的数据为空(不管是数据不存在,还是系统故
    	障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。通过这个直接设置的默认值存放到缓存,这样第二次
    	到缓冲中获取就有值了,而不会继续访问数据库,这种办法最简单粗暴。

43.什么是缓存雪崩?如何解决?

复制代码
    	由于原有缓存失效,新缓存未到期间(例如:我们设置缓存时采用了相同的过期时间,在同一时刻出现大面积的缓存过期),
    	所有原本应该访问缓存的请求都去查询数据库了,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机。
    	从而形成一系列连锁反应,造成整个系统崩溃。

解决办法:

复制代码
    	大多数系统设计者考虑用加锁( 最多的解决方案)或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,
    	从而避免失效时大量的并发请求落到底层存储系统上。还有一个简单方案就时讲缓存失效时间分散开。

什么Redis的脑裂?如何解决

----- 哨兵模式下的脑裂;

------集群模式下的系统性能波动;
异地部署——确保服务的稳定性,
无法彻底消除此类问题,但可通过优化手段最大限度地降低其影响;
-------主从切换过程中可能出现的不一致性问题

复制代码
    (1)什么是redis集群的脑裂
    redis的集群脑裂是指因为网络问题,导致redis master节点和redis slave节点和sentinel集群处于不同的网络分区,此时因为sentinel集群无法感知到master的存在,所以讲slave节点提升为master节点,此时存在两个不同的master节点,就像一个大脑分裂成两个
    (2)redis集群的脑裂造成的问题
    如果客户端还是基于原来的master节点继续写入数据,那么新的master节点吴法同步这些数据,当网络问题解决后sentinel(哨兵)集群将原来的master节点降为slave节点,此时在从新的master中同步数据,将会造成大量的数据流失
    (3)解决redis脑裂问题的方案
    在redis的配置文件中存在两个参数
复制代码
    min-slaves-to-write 3  //表示连接到master的最小slave数量
    min-slaves-max-lag 10  //表示slave连接到master的最大延迟时间
复制代码
    若连接到master的slave数量小于第一个参数,且延迟时间小于等于第二个参数,那么master就会拒绝写请求,配置这两个参数后,
    若发生集群脑裂,原来的master节点接收到客户端的写入请求会拒绝,就可以减少数据同步之后的数据丢失

Lrf和lru缓存淘汰算法的方式是什么?

LRU 的全称是 Least recently used(LRU),其采用淘汰策略最长未被访问的数据(也就是最早的数据)。
LFU 的全称是 Least frequently used(LFU),其采用淘汰策略最长未被访问的数据(也就是最早的数据)。
在服务器缓存系统中广泛使用的 LRU 方法具有高效的 O(1) 时间复杂度;而 LFU 方法也具有类似的高效性能,并借鉴了 LRU 的实现思路以达到优化效果。

LRU

复制代码
    首先实现对每个数据的 O(1) 访问,那么只需要将数据保存在hashmap中,利用[key, value],实现对数据的快速访问。除此之外,实现LRU算法我们需要:
    
    在缓存已满时,再插入数据时,就需要淘汰最老的数据,怎么确定哪些数据是最老的。
    在插入数据时,需要将当前插入的数据标记为最新访问
    同理,在查找数据时,需要将查找的数据标记为最新访问
    为了解决这3个问题,我们可以引入一条双向链表:
    
    如果数据为第一次插入,则将其key插入在尾部
    如果数据查询一次,则将其key从链表中删除,重新将key从尾部插入
    这样,我们就可以确定链表的头部为最老数据、尾部为最新数据。那么第一个问题也就迎刃而解:
    
    在插入数据时,如果当前的数据已满,需要淘汰最老的数据,即删除链表的头部节点数据。同时从hashmap中删除数据。
复制代码
    struct MapValue {
    int value;
    list<int>::iterator node_list_iter;
    };
    
    class LRU {
    public:
    LRU(size_t capacity) : capacity_(capacity) { }
    optional<int> Get(int key) {
        auto it = map_.find(key);
        if(it == map_.end()) {
            return {};
        }
    
        // 找到了,因为本次访问,该key对应的数据变为最新
        list_.erase(it->second.node_list_iter);
        it->second.node_list_iter = list_.insert(list_.end(), key);
        return it->second.value;
    }
    
    void Set(int key, int value) 
    {
        // 先查找该节点是否存在
        auto it = map_.find(key);
        if(it == map_.end()) { // 不存在,插入数据
            // 插入数据之前,要看看数据是否已满,如果已满,需要淘汰最老数据
            if(map_.size() >= capacity_) {
                map_.erase(list_.front());
                list_.erase(list_.begin());
            }
    
            auto&& [it2, b] = map_.insert(make_pair(key, MapValue{value}));
            assert(b);
            it = it2;
        } else { // 存在
            list_.erase(it->second.node_list_iter);
        }
    
          // 将该key对应的value设置为新的值,并将该key设置为最新(既放入链表末尾)
          it->second.node_list_iter = list_.insert(list_.end(), key);
          it->second.value = value;
      }
    
    private:
    const size_t capacity_; // LRU最多缓存多少条数据
    unordered_map<int, MapValue> map_;
    list<int> list_; // 链表,存储每个节点的key。头节点为最老的节点,尾节点为最新
    };

测试代码:

复制代码
    void test_lru()
    {
    LRU lru(3);
    
    lru.set(9001, "9001");
    lru.set(9002, "9002");
    lru.set(9003, "9003");
    
    lru.set(9004, "9004");
    string value;
    assert(lru.get(9001, value) == false);
    
    assert(lru.get(9002, value) == true);
    assert(value == "9002");
    }

LFU(Least-frequently used)是一种数据淘汰策略,在这种策略下,我们会按照访问频率对数据进行排序,并删除访问频率最低的那一项数据。具体而言,在实现过程中我们需为每一个key维护其相应的访问频率记录,并根据这些记录值对数据进行排序以确定淘汰对象。

Spring security 认证流程

你知道的异常有哪些

1、Java中异常分为哪两种?

复制代码
    	编译时异常
    	运行时异常

2、异常的处理机制有几种?

异常捕捉 :try…catch…finally,
异常抛出 :throws。

3、如何自定义一个异常

复制代码
    	继承一个异常类,通常是RumtimeException或者Exception

4、try catch fifinally,try里有return,finally还执行么?

复制代码
    	执行,并且finally的执行早于try里面的return

结论

复制代码
    	1、不管有木有出现异常,finally块中代码都会执行;
    	
    	2、当try和catch中有return时,finally仍然会执行;
    	
    	3、finally是在return后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,
    	   管finally中的代码怎么样,返回的值都不会改变,任然是之前保存的值),所以函数返回值是在finally执行前确定的;
    	   
    	4、finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值。

5、 Excption与Error包结构

复制代码
    Java可抛出(Throwable)的结构分为三种类型:被检查的异常(CheckedException),运行时异常(RuntimeException),错误(Error)。

1、运行时异常

定义 :
RuntimeException 及其子类都被称为运行时异常。
特点 :
Java编译器不会检查它。也就是说,当程序中可能出现这类异常时,倘若既"没有通过throws声明抛出它",也"没有用try-catch语句捕获它",还是会编译通过。例如,除数为零时产生的ArithmeticException异常 ,数组越界时产生的IndexOutOfBoundsException异常 ,failfast机制产生的ConcurrentModi?cationException异常 (java.util包下面的所有的集合类都是快速失败的,“快速失败”也就是fail-fast,它是Java集合的一种错误检测机制。当多个线程对集合进行结构上的改变的操作时,有可能会产生fail-fast机制。记住是有可能,而不是一定。
例如:假设存在两个线程(线程1、线程2),线程1通过Iterator在遍历集合A中的元素,在某个时候线程2修改了集合A的结构(是结构上面的修改,而不是简单的修改集合元素的内容),那么这个时候程序就会抛出ConcurrentModi?cationException 异常 ,从而产生fail-fast机制,这个错叫并发修改异常。Fail-safe,java.util.concurrent包下面的所有的类都是安全失败的,在遍历过程中,如果已经遍历的数组上的内容变化了,迭代器不会抛出ConcurrentModicationException异常 。如果未遍历的数组上的内容发生了变化,则有可能反映到迭代过程中。这就是ConcurrentHashMap迭代器弱一致的表现。ConcurrentHashMap的弱一致性主要是为了提升效率,是一致性与效率之间的一种权衡。要成为强一致性,就得到处使用锁,甚至是全局锁,这就与Hashtable和同步的HashMap一样了。)等,都属于运行时异常。

常见的五种运行时异常:
复制代码
    		ClassCastException(类转换异常)
    		
    		IndexOutOfBoundsException(数组越界)
    		
    		NullPointerException(空指针异常)
    		
    		ArrayStoreException(数据存储异常,操作数组是类型不一致)
    		
    		Bu?erOver?owException

2、被检查异常

复制代码
    定义:Exception类本身,以及Exception的子类中除了"运行时异常"之外的其它子类都属于被检查异常。
    特点 : Java编译器会检查它。此类异常,要么通过throws进行声明抛出,要么通过try-catch进行捕获处理,否则不能通过编译。
    例如,CloneNotSupportedException就属于被检查异常。当通过clone()接口去克隆一个对象,而该对象对应的类没有实Cloneable接口,
    就会抛出CloneNotSupportedException异常。被检查异常通常都是可以恢复的。
复制代码
    如:
    IOException
    FileNotFoundException
    SQLException
复制代码
    被检查的异常适用于那些不是因程序引起的错误情况,比如:读取文件时文件不存在引发的FileNotFoundException 。
    然而,不被检查的异常通常都是由于糟糕的编程引起的,比如:在对象引用时没有确保对象非空而引起的 NullPointerException 。

3、错误

定义 : Error类及其子类。
特点 : 和运行时异常一样,编译器也不会对错误进行检查。当资源不足、约束失败、或是其它程序无法继续运行的条件发生时,就产生错误。程序本身无法修复这些错误的。例如,VirtualMachineError就属于错误。出现这种错误会导致程序终止运行。OutOfMemoryError、ThreadDeath。
Java虚拟机规范规定JVM的内存分为了好几块,比如堆,栈,程序计数器,方法区等

6、Thow与thorws区别

位置不同

复制代码
    	throws 用在函数上,后面跟的是异常类,可以跟多个;而 throw 用在函数内,后面跟的
    	是异常对象。

功能不同:

复制代码
 throws 用来声明异常,让调用者只知道该功能可能出现的问题,可以给出预先的处理方式;
    	 throw 抛出具体的问题对象,执行到 throw,功能就已经结束了,跳转到调用者,并将具体的问题对象抛给调用者。
    	 也就是说 throw 语句独立存在时,下面不要定义其他语句,因为执行不到。
    	 
    	2. throws 表示出现异常的一种可能性,并不一定会发生这些异常;throw 则是抛出了异常,执行 throw 则一定抛出了某种异常对象。
    	
    	3. 两者都是消极处理异常的方式,只是抛出或者可能抛出异常,但是不会由函数去处理异常,真正的处理异常由函数的上层调用处理。

7、Error与Exception区别?

复制代码
    	Error和Exception都是java错误处理机制的一部分,都继承了Throwable类。
    	Exception表示的异常,异常可以通过程序来捕捉,或者优化程序来避免。
    	Error表示的是系统错误,不能通过程序来进行错误处理。

8、error和exception有什么区别

复制代码
    	 error 表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出。不可能指望程序能处理这样的情况 exception 表示一种设
    	计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况

49. 有哪些注解可以创建Bean对象

java8新特性有

  1. Lambda 表达式
  2. 函数接口
  3. 方法引用
  4. 流式编程
  5. Optional

线程创建的方式有哪些?

Spring boot自动化配置原理

创建索引的sql语句

复制代码
    创建索引可以加快对数据的查询速度,加速表和表之间的连接,使用 sql 语句创建索引的语法有:

创建表的同时指定索引语法:

复制代码
    				create table [表名](
    			    [属性名1] [数据类型1] [约束1],
    			    ...
    			    [属性名n] [数据类型n] [约束n],
    			    unique  index [索引名]  //唯一索引
    				)在这里插入代码片

alter 语句在已经存在的表上创建索引语法:

复制代码
    			alter table [表名] add index [索引名]

使用 CREATE TABLE 语句创建索引语法:

复制代码
    			create index [索引名] ON [表名]
复制代码
    注:unique 为唯一索引,通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。

有哪些技巧可以提高SQL查询的性能记录详细的需求文档

1. 记录详细的需求文档

复制代码
    		在写SQL之前必须弄清楚需求, 具体要取什么数据, 有些什么具体的约束条件, 
    		在数据仓库环境中还可以考虑补上这个需求具体对应哪些报表, 对应的基础表如何. 到开发环境的话, 
    		可以考虑加上这条SQL服务于哪些业务(页面),调用频率如何.

2. 不要重新制造轮子

复制代码
    		对于一些已经比较成熟的解决方案,有比较现成的例子的SQL,要善于利用已有SQL,已有模板.

3. 降低语句的复杂度

复制代码
    		有些同学可能比较喜欢使用比较炫的技术,比较炫的SQL来解决问题. 但是要切记一点, 
    		使用过于复杂过于新的技术, 如果不能在性能(以及其他方面)带来好处的话, 只会使得这条SQL难于维护, 使得其他相关人员难于理解.

4. 小心处理NULL

复制代码
    		NULL在Oracle数据库中是一个非常特别的值, 它不等于任何值, 所以如果你的SQL返回的值数量偏少,
    		或者根本不对很可能就是使用NULL出现了问题..

常见的情况是:

复制代码
 查询的时候直接使用条件 colx = xxx,而这个colx里面是有NULL值的,  这种情况下查询的返回结果是不会包含NULL值对应的记录的, 
    		      如果要查询出NULL对应的记录, 需要使用 colx is null (is not null).
 var 为null的时候, 在plsql中给var赋值, var := var + xxx;这种情况下var的值会一直是null的, 这一点需要特别注意,

5. 自己核对数据类型

复制代码
    		在where条件里面要仔细地核对数据类型, 由于隐形转换的问题, 在数据类型错误的时候, Oracle无法正确使用索引, 可能会导致SQL运行非常慢.

6. 小心处理重复数据

复制代码
    		在需求明确的情况下, 如果你不在乎是否出现重复记录, 或者明确知道不会出现重复数据的情况下,
    		 尽量使用Union All而不是Union进行查询, Union会涉及到昂贵的排序操作.

7. 避免不必要的优化操作

复制代码
    		SQL的性能调优可能非常有趣非常带劲, 但是很多时候调优可能意义不大, 比如对于只会使用一次的查询, 你可能很少在乎是1秒钟结束还是2秒钟结束..	
    		只查询你需要的字段, 而不要所有的查询都是用select *来进行
    		在通过索引来查询更合适的时候, 尽量在查询条件中指定有索引的字段来查询. 
    		(在返回的记录条数很少的时候, 使用索引一般都能更加快速的得到查询结果.不要可以避免使用表连接. 关系数据库就是为了表连接而设计的.

8. 尽可能使用绑定变量

复制代码
    		在开发环境使用的SQL语句尽量使用绑定变量, 这样可以大大缓解Oracle数据库解析SQL的消耗, 也可以大大提高数据库的可扩展性.

9. 使用源码控制工具

复制代码
    		最好使用CVS/SVN一类的源码控制工具来管理你的SQL/PLSQL代码, 
    		这对于后期的维护有非常大的帮助, 也有助于其他人更好的理解你最初写这条SQL的意图.

10. 测试,测试,测试.

复制代码
    		在SQL写好之后, 要深入的进行测试, 以确保其正常运行

redis能支持的最大并发量

复制代码
    	redis可以支持小数据可以达到10W的并发量。

Redis是一种基于键值对的数据存储系统,在值类型方面具有更大的多样性,并支持多种基本操作以及更为复杂的运算功能。与Memcached相似,在提高性能的同时Redis通过定期将更新的数据回写至磁盘并将其持久化到附加的日志文件中实现了数据持久化功能,在此之上还提供了多种排序方式的支持

全部评论 (0)

还没有任何评论哟~