++前缀,先++再运算
++后缀。先运算再++
–前缀,先–再运算
–后缀,先运算再–
基本数据类型:
数值型:
整数型:byte(1)-128~127 取值范围 -2的7次方~2的7次方-1
short(2)-32768~32767 -2的15次方~2的15次方-1
int(4)-2的31次方~2的31次方-1
long(8)-2的63次方~2的63次方-1
浮点型:float(4)单精度 准确精度7位
double(8)双精度 准确精度15位
字符型:char(2)0~2的16次方-1
布尔型:boolean(4)
引用数据类型:
类(class)、接口(interface)、数组(Array)、枚举(enum)、注解(annocation)
for循环:循环次数已知
while循环:循环次数未知
while循环与if语句有些类似,检查布尔类型的真假,与if不同的是它会循环执行,执行一次后会重复执行,直到执行到假为止,而while Ture是在不知道结果时给的一个条件,作为大部分不知道结果的语句应用。
do while循环:无论条件是否成立,都会先执行一次循环体内的内容
break:跳出当前所在循环,跳出switch语句
continue:跳出当前本次循环
return:返回方法,方法结束
编译错误,等号左边是short类型,右边是int类型
次数2,1
效率
+=自动强转
byte、short、int、char、Srting、枚举
原因:swich对应的JVM字节码lookupswich、tableswich指令只支持int类型,char、String、枚举都是转为int后再进行swich
swich中能放变量和常量,但case中只能放常量
&:逻辑与:两边为true则为true,否则为false;左边为false,右边仍然执行
按位与:两数同位对比,有0则为0
&&:短路与:两边为true则为true,否则为false;左边为false,右边不执行
| : 逻辑或,两边为false则为false,否则为true;左边为true,右边仍然执行
||: 短路或,两边为false则为false,否则为true;左边为true,右边不执行
1、在类中的位置不同
成员变量:类中,方法外
局部变量:方法中或者方法声明上(形参)
2、作用范围不同
成员变量:类中
局部变量:方法中
3、初始化值不同
成员变量:有默认值
局部变量:没有默认值,必须先定义赋值,最后使用
4、在内存中的位置不同
成员变量:堆内存或方法区中
局部变量:栈内存
5、生命周期不同
成员变量:随着对象的创建而存在,随着对象的消失而消失
局部变量:随着方法的调用而存在,随着方法的调用完毕而消失
java.long包中有
Boolean Byte Short Character Integer Long Float Double(八大基本数据类型的包装类)
String StringBuffer StringBilder(字符串)
Math StrictMath(系统)
java.util包中有
Scanner Arrays
java.lang.reflect包中有
Array Constructor Field Parameter
java.net包中有
URLEncoder URL URI
Lambda表达式是JDK1.8之后提供的一种函数式编程语法
简化匿名内部类的写法
forEach 遍历
count 统计个数
filter 过滤
limit 保留前几个
skip 丢弃前几个
map 映射
sorted 排序
distinct 去重
collector 收集流中的数据,流转为集合
1、使用for循环遍历map
2、使用迭代遍历map
3、使用keySet迭代遍历
map遍历键,通过键查找值
KeySet 返回键的集合
4、使用entrySet遍历map
同时遍历键和值
entrySet 返回键和值的集合
5、使用stream流遍历map
Spring是一个轻量级的企业级框架,作用是解耦,并且可以统一管理Bean的生命周期。Spring核心功能分为IOC和AOP,其中IOC是控制反转,AOP表示面向切面编程
Core:核心组件,提供IOC功能
Context:上下文组件,提供包扫描、国际化、事件等功能
Beans:JavaBean的管理
SpEl:Spring表达式
AOP:面向切面编程
Test:集成单元测试
MVC:SpringMVC框架
JDBC:集成JDBC开发
ORM:数据库开发
Cloud:微服务开发框架
Security:权限控制框架
IOC:控制反转,指的是将对象的生命周期交给IOC容器进行管理的过程就叫控制反转。底层原理:反射+工厂模式
IOC的作用: 减少了代码的耦合度,降低了程序的维护成本
DI:依赖注入,指Spring容器会将类与类之间的依赖关系在容器内部完成绑定,进一步解耦
AOP:面向切面编程。SpringAOP底层使用JDK动态代理和CGLIB代理。如果目标类有接口,则使用JDK动态代理,如果没有接口,则使用CGLIB代理
AOP的作用:核心业务和非核心业务的解耦,在不改变原有执行流程和代码的情况下,扩展新的功能
构造函数:(默认)没有无参构造函数会报错,且无法主动控制对象创建的过程
静态工厂:可以控制对象创建的过程,但工厂类不被IOC容器管理
实例工厂:可以控制对象创建的过程,工厂类被IOC容器管理
set方法、构造函数、注解(@Autowired)
1、no:默认的方式是不进行自动装配,通过显式设置ref 属性来进行装配
2、byName:通过参数名自动装配,Spring 容器在配置文件中发现bean 的autowire 属性被设
置成byname,之后容器试图匹配、装配和该bean 的属性具有相同名字的bean
3、byType:通过参数类型自动装配,Spring 容器在配置文件中发现bean 的autowire 属性被
设置成byType,之后容器试图匹配、装配和该bean 的属性具有相同类型的bean。如果有多
个bean 符合条件,则抛出错误
4、constructor:这个方式类似于byType,但是要提供给构造器参数,如果没有确定的带参数
的构造器参数类型,将会抛出异常
5、autodetect:首先尝试使用constructor 来自动装配,如果无法工作,则使用byType 方式
1.@Autowired是Spring开发的,而@Resource是jdk开发的
2.@Autowired默认按类型装配 ,而@Resource 默认按名称装配 ,如果名称找不到,那么就按照类型装配
反射+工厂模式
动态创建代理类,初始化和赋值
创建和管理对象
1、启动类的@SpringBootApplication注解包含@EnableAutoConfiguration注解,启动自动配置
2、在spring-boot-autoconfigure依赖中包含了大量第三方框架的自动配置类
3、自动配置类包含了对应框架的配置的内容
4、自动配置类生效的条件是当某些类被导入项目中,也就是导入了相应的依赖
5、开发者不写配置就会应用默认的配置
6、开发者可以通过application.properties文件改写配置
SpringBoot能让开发更加简单,“约定大于配置”
包含以下优点:
1、自动配置:由SpringBoot帮助对整合的第三方框架进行配置,不需要开发人员编写xml文件
2、依赖管理:用starter整合所需要的框架,对依赖的版本进行管理
3、辅助功能:内嵌tomcat服务器,数据统计,健康检查等
1.properties文件:书写格式为键值对的形式
2.yml,yaml文件:书写格式为首行缩进
优先级:properties > yml > yaml
第一步:在入口类使用注解@EnableTransactionManagement开启事务支持
第二步:在需要使用事务的service方法上添加注解@Transactional即可
SpringAOP底层使用JDK动态代理和CGLIB代理
如果目标类有接口,则使用JDK动态代理,如果没有接口,则使用CGLIB代理
JDK动态代理是通过生成一个代理类,且代理类实现目标类的接口,然后将接口的引用指向代理类从而实现代理
CGLIB代理是通过继承目标类,重写父类方法,将父类引用指向代理类对象,从而实现代理
区别:BeanFactory是生产Bean实例的一个工厂(Factory) ,也就是IOC容器或对象工厂,作用是管理Bean
FactoryBean是一种JavaBean,里面定义了各种方法 ,作用是创建对象,并返回IOC容器,在Spring中所有的 Bean 都是由 BeanFactory (也就是 IOC 容器) 来进行管理
1) 用户发送请求
2)前端控制器获得用户请求的URL,发送URL给处理器映射
3)处理器映射将Handler(包装方法信息)返回给前端控制器
4)前端控制器发送Handler给处理器适配器,适配器执行Handler方法
5)执行Handler方法后,返回ModelAndView(逻辑视图)给前端控制器
6)前端控制器将ModelAndView发送给视图解析器,解析出物理视图返回给前端控制器
7)前端控制器渲染视图,发送视图给用户
Spring MVC的核心组件:
DispatcherServlet:中央控制器,把请求给转发到具体的控制类
Controller:具体处理请求的控制器
HandlerMapping:映射处理器,负责映射中央处理器转发给controller时的映射策略
ModelAndView:服务层返回的数据和视图层的封装类
ViewResolver:视图解析器,解析具体的视图
Interceptors :拦截器,负责拦截我们定义的请求然后做处理工作
数据结构:一维数组+单向链表+红黑树
添加数据的过程:
1、通过键的hashcode计算出下标
2、判断下标是否存在数据,没有就直接存放数据,
3、如果有数据就调用键的equals进行比较,如果相等就用新值覆盖旧值,不相等就将新数据放到原数据后面,形成单向链表
4、在JDK1.8后如果链表长度超过8就会自动转为红黑树
1、接口使用interface关键字,抽象类使用abstract关键字
2、接口中只能有常量,抽象类可以有变量
3、接口没有构造方法,抽象类有构造方法
4、接口中抽象方法只能是public,抽象类中可以是其他的
5、接口可以多实现,抽象类只能单继承
6、接口中有默认方法,抽象类中没有默认方法
7、接口只有默认方法,静态方法,抽象方法,抽象类有普通方法
应用场景:如果希望在系统中某个类的对象只能存在一个,单例模式是最好的解决方案。
应用场景如下:
a、 在编码时不能预见需要创建哪种类的实例。
b、 系统不应依赖于产品类实例如何被创建、组合和表达的细节。
HashMap和Hashtable异同
相同点:数据结构和用法相似
不同点:1、HashMap非线程安全,Hashtable线程安全(同步关键字)
2、HashMap性能高于Hashtable
3、HashMap能够接受空的键和值,Hashtable不能
Hashtable和ConcurrentHashMap异同
1、Hashtable锁整个集合,ConcurrentHashMap将集合分为多段,分段锁
2、ConcurrentHashMap性能优于Hashtable
事务的ACID:
原子性(atomicity):事务中所有操作要么成功,要么失败
一致性(consistency):事务发生状态改变后,前后数据的完整性必须一致
隔离性(isolation):事务与事务之间相互隔离,互不影响
持久性(durability):事务一旦提交,就必须永久保存到数据库中
事务并发有哪些问题:
1、更新丢失:两事务同时对同一数据进行修改,后面的事务会覆盖前面事务的修改
2、脏读:事务A读取到事务B已修改但未提交的数据
3、不可重复读:事务A在事务中读取两次数据,第二次读取的数据与第一次不一致(改、删)
4、幻读:事务A在事务中读取两次数据,第二次读取的数据与第一次不一致(新增)
隔离级别有哪些:
1、读未提交:可以读取到其他事务未提交的数据(性能最高)
(更新丢失、脏读、不可重复读、幻读)
2、读已提交:只能读取到其他事务已提交的数据
(更新丢失、不可重复读、幻读)
3、可重复读:事务A在事务中读取两次数据,会读取到一致的结果,不会因为其他事务修改而改变
(幻读)修复
4、串行化:事务排队挨个执行,不存在上面任何问题(效率最低,不会存在并发)
MySQL默认是可重复读,Orecle默认是读已提交
final表示最终的最后的,用于修饰类、方法、变量
finally是异常处理的一部分,只能用在try/catch语句中,表示finally的语句一定会被执行
finalize是一个方法,来自object类,该方法是垃圾回收机制回收该对象之前调用的方法,由垃圾回收机制主动调用,不需要人为调用
get方法
安全性低,数据被放在请求的URL中
效率高,是form表单默认的提交方法
传送的数据量较小,这主要是因为受URL长度限制
post方法
安全性高,所有操作对用户来说都是不可见的
效率低(耗时稍长)会发送几次请求
传送的数据量较大,一般被默认为不受限制
1、转发是发起一次请求,重定向是发起两次请求
2、转发的地址栏不会发生改变,重定向的地址栏会发生改变
3、转发可以获取request作用域中的数据,重定向不可以,因为重定向发起了两次请求
4、forward是在服务器内部、性能高,redirect是在服务器外部
未提交读(读未提交):最低隔离级别、事务未提交前,就可被其他事务读取
(会出现幻读、脏读、不可重复读)
提交读(读已提交):一个事务提交后才能被其他事务读取到
(会造成幻读、不可重复读)
可重复读:默认级别,保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据
(会造成幻读)
序列化(串行化):代价最高最可靠的隔离级别,该隔离级别能防止脏读、不可重复读、幻读
面向对象(oo)是一种编程思想,一切皆为对象,更贴近人的思维,便于大型项目的设计和管理
面向对象有三大核心,封装、继承、多态
封装:对细节屏蔽,隐藏内部信息,外部只能通过指定的入口进行访问和操作
封:信息隐藏,属性私有化,提供get/set方法对其访问,提高数据安全性;装:将属性和方法装到类中统一管理
优点:安全
1、良好的封装能够减少耦合
2、类内部的结构可以自由修改
3、可以对类成员进行更精确的控制
4、隐藏信息,实现细节
继承:子类继承父类的属性和方法
提高代码复用性、维护性
优点:1、获得更好的层次关系
2、可以继承到父类的方法和属性
3、减少代码的冗余
多态:同一形为,不同事物具有不同的表现形式
提高程序灵活性,通用性,基于封装和继承·,将父类引用指向子类对象
实现运行时多态的条件有哪些?
1.继承/实现接口
2.重写
3.向上转型(父类引用指向子类对象)
集合就是一个放数据的容器,准确的说是放数据对象引用的容器。
1、集合类存放于java.util包中。
2、集合类型主要有3种:set(集)、list(列表)和map(映射)。
3、集合存放的都是对象的引用,而非对象本身。所以我们称集合中的对象就是集合中对象的引用。
JDK:Java程序开发环境
JRE:Java程序运行时的环境
JVM:Java虚拟机
JDK > JRE > JVM
==如果比的是基本数据类型,则比的是值,如果比的是引用数据类型,则比的是地址
equals只能用于引用类型,
默认比的是地址,String类重写了equals方法,比的是值
操作字符串的类有:String、StringBuffer、StringBuilder
String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String
StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,关键字synchronized,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer
StringBuilder和StringBuffer的默认容量都是16,其扩容机制首先是把容量变为原来容量的2倍加2
indexOf():返回指定字符的索引。
charAt():返回指定索引处的字符。
replace():字符串替换。
trim():去除字符串两端空白。
split():分割字符串,返回一个分割后的字符串数组。
getBytes():返回字符串的 byte 类型数组。
length():返回字符串长度。
toLowerCase():将字符串转成小写字母。
toUpperCase():将字符串转成大写字符。
substring():截取字符串。
equals():字符串比较。
软件开发的核心是数据,而数据的传输、保存、读取都是通过IO技术实现的
Java中的IO模型主要有三种:
BIO 同步阻塞式IO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。
NIO 同步非阻塞式IO:New IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。
AIO 异步非阻塞式IO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。
BIO:Blocking IO 同步阻塞式IO,是比较常用的IO模型
特点是
一旦服务器的线程阻塞,服务器就不能处理其他客户端的业务,为解决这种问题,一般我们会为每个客户端单独启动线程,这样造成了新的问题,就是如果客户端比较多,就会大量消耗服务器的线程资源,导致服务器压力过大,所以这种模型比较适合连接数不大、数量相对固定的情况。
NIO:jdk1.4出现了NIO(Noblocking IO 同步非阻塞式IO) ,与BIO不同的是,NIO进行读取操作时,不会阻塞线程
NIO有三大核心组件:
Channel 通道,类似BIO的输入输出流的作用,不同IO流的是Channel同时可以完成读写操作
Buffer 缓冲区,类似BIO中的byte数组,用于存放数据
Selector 多路复用器,每个Channel都会注册到selector上,selector会在多个通道间轮询,一旦有读写事件出现就会立即进行处理
优点:单线程就可以处理大量的IO请求,这样就大大降低了服务器的压力。
缺点:如果每个IO请求处理时间比较长,会产生长时间排队的情况;编程也比较复杂。
比较适合处理连接数比较多并且时间较短的场景,如:聊天、发弹幕等。
AIO(异步非阻塞),由操作系统完成后回调通知服务端程序启动线程去处理
适合于连接数多且时间较长的操作,JDK1.7开始
BIO、NIO、AIO对比
BIO | NIO | AIO | |
---|---|---|---|
IO模型 | 同步阻塞 | 同步非阻塞 | 异步阻塞 |
编程难度 | 较低 | 较高 | 较高 |
可靠性 | 差 | 好 | 好 |
吞吐量 | 低 | 高 | 高 |
举个栗子:去餐厅吃饭等位子
BIO:拿号后在门口排队等候,什么都不做
NIO:拿号后去旁边逛街,每隔一段时间来看看有没有到自己
AIO:在手机上预订后,去旁边逛街,到自己后手机通知再过去
按功能来分:输入流(input)、输出流(output)
按类型来分:字节流和字符流
字节流和字符流的区别是:处理所有的数据,byte数组 字节流按 8 位传输以字节为单位输入输出数据,处理文本,基于字节流 char 字符流按 16 位传输以字符为单位输入输出数据
java集合分三种,List、Set、Map,这三种集合适用于不同的场景
List:有序、可重复、可随机访问
Set:无序、不可重复、不可随机访问
Map:键值对结构、键不可重复、通过键访问值
注:通常List与Map最为常用
Collection : 集合接口(集合类的一个顶级接口),它提供了对集合对象进行基本操作的通用接口方法,其直接继承接口List和Set
Collections:集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、二分查找以及线程安全等各种操作。
List:有序、可重复、可随机访问
Set:无序、不可重复、不可随机访问
Map:键值对结构、键不可重复、通过键访问值
相同点:数据结构和用法相似
不同点:1、HashMap非线程安全,Hashtable线程安全(同步关键字)
2、HashMap性能高于Hashtable
3、HashMap能够接受空的键和值,Hashtable不能
相同点:用法相似
不同点:1、数据结构不同
ArrayList:数组
LinkedList:双向链表
2、优缺点不同
ArrayList 优点:访问速度快
缺点:插入速度慢,需要一个一个移动数据
LinkedList 优点:插入删除快,无需移动数据,只需要改变前后指针
缺点:访问速度慢,需要一个一个遍历
特点:不暴露数据结构的前提下进行数据迭代访问
迭代器是一种设计模式,它是一个对象,它可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。迭代器通常被称为“轻量级”对象,因为创建它的代价小。
相同点:数据结构都是数组、使用方法相同
不同点:1、ArrayList是非线程安全,Vector是线程安全
2、ArrayList性能优于Vector
3、ArrayList自动扩容,是原始容量的1.5倍
Vector扩容长度可以设置,默认是两倍
反射是Java的特征之一,是一种动态调用的技术
Java反射机制指的是在Java程序运行状态中,对于任何一个类,都可以获得这个类的所有属性和方法;对于给定的一个对象,都能够调用它的任意一个属性和方法。这种动态获取类的内容以及动态调用对象的方法称为反射机制。
Java的反射机制允许编程人员在对类未知的情况下,获取类相关信息的方式变得更加多样灵活,调用类中相应方法,是Java增加其灵活性与动态性的一种机制。
序列化:将Java中的对象转为二进制数据或文件的过程
反序列化:将二进制数据或文件转为Java中的对象的过程
什么情况下需要序列化:持久化和网络传输
a)当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;
b)当你想用套接字在网络上传送对象的时候;
c)当你想通过RMI传输对象的时候;
动态代理:当想要给实现了某个接口的类中的方法,加一些额外的处理。比如说加日志,加事务等。可以给这个类创建一个代理,故名思议就是创建一个新的类,这个类不仅包含原来类方法的功能,而且还在原来的基础上添加了额外处理的新类。这个代理类并不是定义好的,是动态生成的。具有解耦意义,灵活,扩展性强。
动态代理的应用:Spring的AOP,加事务,加权限,加日志。
jdk动态代理实现:首先必须定义一个接口,还要有一个InvocationHandler(将实现接口的类的对象传递给它)处理类。再有一个工具类Proxy(习惯性将其称为代理类,因为调用他的newInstance()可以产生代理对象,其实他只是一个产生代理对象的工具类)。利用到InvocationHandler,拼接代理类源码,将其编译生成代理类的二进制码,利用加载器加载,并将其实例化产生代理对象,最后返回。
补充{
JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了。
CGLib采用了非常底层的字节码技术( ASM),其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。
“JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。”
JDK动态代理和CGLIB代理有什么区别?
JDK动态代理是通过生成一个代理类,且代理类实现目标类的接口,然后将接口的引用指向代理类从而实现代理。
CGLIB代理是通过继承目标类,重写父类方法,将父类引用指向代理类对象,从而实现代理。
}
想对一个对象进行处理,又想保留原有的数据进行接下来的操作,就需要克隆了,Java语言中克隆针对的是类的实例。
如何实现对象克隆:
方式一:实现Cloneable接口并重写Object类中的clone()方法
方式二:实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆
浅拷贝只是复制了对象的引用地址,两个对象指向同一个内存地址,所以修改其中任意的值,另一个值都会随之变化,这就是浅拷贝(例:assign())
深拷贝是将对象及值复制过来,两个对象修改其中任意的值另一个值不会改变,这就是深拷贝(例:JSON.parse()和JSON.stringify(),但是此方法无法复制函数类型)
(1)Servlet的应用逻辑是在Java文件中,并且完全从表示层中的HTML里分离开来。而JSP是Java和HTML组合成一个扩展名为.jsp的文件
(2)JSP侧重于前端视图显示,Servlet主要用于后端控制逻辑
jsp经编译后就变成了Servlet.(JSP的本质就是Servlet,JVM只能识别java的类,不能识别JSP的代码,Web容器将JSP的代码编译成JVM能够识别的java类)
jsp更擅长表现于页面显示,servlet更擅长于逻辑控制。
Servlet中没有内置对象,Jsp中的内置对象都是必须通过HttpServletRequest对象,HttpServletResponse对象以及HttpServlet对象得到。
Jsp是Servlet的一种简化,使用Jsp只需要完成程序员需要输出到客户端的内容,Jsp中的Java脚本如何镶嵌到一个类中,由Jsp容器完成。而Servlet则是个完整的Java类,这个类的Service方法用于生成对客户端的响应。
out:输出服务器响应的输出流对象(用于页面输出)
request:封装客户端的请求,其中包含来自GET或POST请求的参数(得到用户请求的信息)
response:封装服务器对客户端的响应(服务器向客户端的回应信息)
config:Web应用的配置对象(服务器配置,可以获得初始化参数)
session:封装用户会话的对象(用来保存用户信息)
application:封装服务器运行环境的对象(所有用户共享信息)
page:JSP页面本身(指当前页面跳转后的servlet类的实例)
pageContext:通过该对象可以获取其他对象(Jsp页面容器)
exception:封装页面抛出异常的对象(表示Jsp页面所发生的异常,在错误页中才起作用)
application 作用域:作用在整个应用中
session作用域:作用在整个会话
request作用域:作用在当前请求的范围内
pageContext作用域:作用在Jsp页面,在当前页有效
1、Cookie数据存放在客户端上,安全性较差,Session数据放在服务器上,安全性相对更高
2、单个cookie保存的数据不能超过4K,session无此限制
3、session一定时间内保存在服务器上,当访问增多,占用服务器性能,考虑到服务器性能方面,应当
使用cookie。
session 是浏览器和服务器会话过程中,服务器会分配的一块储存空间给session。
服务器默认为客户浏览器的cookie中设置 sessionid,这个sessionid就和cookie对应,浏览器在向服务
器请求过程中传输的cookie 包含 sessionid ,服务器根据传输cookie 中的 sessionid 获取出会话中存储
的信息,然后确定会话的身份信息
throws:声明异常,向上抛出,不处理异常,交给上一级处理,一般用在方法声明上
throw:抛出异常,用在方法中
throws是用来声明一个方法可能抛出的所有异常信息,throws是将异常声明但是不处理,而是将异常往上传,谁调用我就交给谁处理。而throw则是指抛出的一个具体的异常类型
NullPointerException 空指针异常
ClassNotFoundException 找不到指定类
ArrayIndexOutofBoundsException 数组下标越界
NumberFormatException 数字格式化异常
StarkOverflowException 内存空间溢出异常
ClassCastException 类型转换异常
IOException 异常
RuntimeException 运行时异常
ParseException 时间格式化异常
SqlException
throwable:
Error:错误,无法被处理的异常,也不需要程序员去处理该类问题
OutOfMemoryError、StackOverflowError
Exception:异常
编译时异常(checkedException)IOException、SQLException、FileNotFoundException、ClassNotFoundException
运行时异常(runtimeException)NullPointerException、ClassCastException、ArrayIndexOutofBoundsException
CORS policy 同源策略
服务器自我保护的机制,会信任同一个服务器下的程序
协议://域名:端口
三个如果相同,则认为是一个域下,否则就是跨域
解决方案:
跨域可以在前端通过 JSONP 来解决,但是 JSONP 只可以发送 GET 请求,无法发送其他类型的请求,在
RESTful 风格的应用中,就显得非常鸡肋,因此我们推荐在后端通过 (CORS,Cross-origin resource
sharing) 来解决跨域问题。这种解决方案并非 Spring Boot 特有的,在传统的 SSM 框架中,就可以通
过 CORS 来解决跨域问题,只不过之前我们是在 XML 文件中配置 CORS ,现在可以通过实现
WebMvcConfigurer接口然后重写addCorsMappings方法解决跨域问题。
方式一:图片ping或script标签跨域
方式二:JSONP跨域
方式三:CORS
方式四:window.name+iframe
方式五:window.postMessage()
方式六:修改document.domain跨子域
方式七:WebSocket
方式八:代理
1、重写是方法名相同,参数列表相同,返回值相同或者返回值类型的子类,权限修饰符不能比父类更严格重载是方法名相同,参数列表不同(参数类型、顺序、数量)
2、重写是发生在父子关系中,重载是发生在同类中
3、重写可以用@override修饰
#是占位符(预编译),可以防止SQL注入
$是字符串拼接,不能防止SQL注入
一级缓存(会话):基于SqlSession进行缓存,不同会话不共享
二级缓存(全局):基于SqlSessionFactory级别,不同会话之间可以共享
对于缓存数据更新机制,当某一个作用域(一级缓存 Session/二级缓存Namespaces)的进行了C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear。
使二级缓存失效的情况:两次查询之间执行了任意的增删改,会使—级和二级缓存同时失效
1、添加数据时,当数据个数超过数组的初始长度后,ArrayLIst集合会自动扩容
2、计算出新的容量,是原始容量的1.5倍
3、创建新的数组,把旧数组复制过去
HashMap计算添加元素的位置时,使用的位运算,这是特别高效的运算;另外,HashMap的初始容量是2的n次幂,扩容也是2倍的形式进行扩容,是因为容量是2的n次幂,可以使得添加的元素均匀分布在HashMap中的数组上,减少hash碰撞,避免形成链表的结构,使得查询效率降低
HashMap有加载因子,loadfactor默认是0.75倍
HashMap有加载因子,loadfactor 默认值是0.75,添加数量达到整个长度*加载因子,就发生扩容,目的为了避免发生哈希碰撞,提高查询速度。 长度是2的幂,减少哈希碰撞,算下标的代码:hash值 & 长度 - 1 (等价于 hash值 % 长度)2的幂- 1 位上都是1,位上是0,容易出现碰撞,是1,减少碰撞
1、使用HttpServletRequest作为SpringMVC 控制器里面方法的参数,同Servlet中使用Request对象获取页面参数的方法一样
2、使用注解@RequestParam绑定请求参数到形参中
3、使用注解@PathVariable获取路径中的参数
4、使用注解@ModelAttribute获取POST请求的FORM表单数据绑定到方法参数的实体类中
5、使用注解@RequestBody将JSON字符串反序列化为实体类
1.简单数据绑定,当前端请求的参数比较简单时,可在后台方法的形参中直接使用Spring MVC提供的默认参数类型进行数据绑定
2.自定义数据绑定有些特殊类型的参数无法进行直接转换,例如日期数据就需要自定义转换器(Converter)或格式化(Formatter)来进行数据绑定。
Model,HttpSession
“pwd 命令 cd 命令ls 命令cat 命令cp 命令mv 命令mkdir 命令rmdir 命令rm 命令touch 命令locate 命令find 命令sudo 命令df 命令history命令
du命令head 命令tail 命令diff 命令tar 命令chmod 命令chown 命令Jobs 命令kill 命令ping 命令wget 命令uname 命令top 命令”
索引:是一种数据,保存实际的数据的位置
如:书籍的目录、楼层索引
作用:加快查询速度,MySQL查询时,先找索引,通过索引找到实际数据的位置,再直接定位到实际数据上。
优点:
极大提高查询速度
缺点:
按使用效果分为:
按结构分为:
聚簇索引
索引键的顺序和实际数据的顺序一致,一个表只能有一个
如:新华字典的拼音目录
非聚簇索引
索引键的顺序和实际数据的顺序不一致,一个表有多个
如:字典的偏旁部首目录
B-Tree和B+Tree
为什么使用树?查找效率高,使用二分查找算法
B-Tree 分为多个节点,每个节点由键和数据组成,节点的两边还有指针,指向下面的子节点
查找效率由树的高度决定,树越高查找越慢
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PvfyR6qi-1660044523026)(F:/%E6%A1%8C%E9%9D%A2%E6%96%87%E4%BB%B6/Java%E7%AC%AC%E5%9B%9B%E9%98%B6%E6%AE%B5/20220719-MySQL%E9%AB%98%E7%BA%A7-DAY01/MySQL%E9%AB%98%E7%BA%A7.assets/image-20220719104840465.png)]
B+Tree,类似B-Tree,区别是:把实际数据都放在叶子节点中,减少了每个节点数据大小,每层可以放更多节点,树高度就降低了
查找效率更高,索引默认使用B+Tree
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mwVbwsk4-1660044523027)(F:/%E6%A1%8C%E9%9D%A2%E6%96%87%E4%BB%B6/Java%E7%AC%AC%E5%9B%9B%E9%98%B6%E6%AE%B5/20220719-MySQL%E9%AB%98%E7%BA%A7-DAY01/MySQL%E9%AB%98%E7%BA%A7.assets/image-20220719105530001.png)]
是一种虚拟的表,表的数据存在实际的表中,视图将数据组织在一起
Stored Procedure 封装一系列SQL指令,编译后保存在数据库中,可以传递参数并进行调用。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qnyS1WAw-1660044523028)(F:/%E6%A1%8C%E9%9D%A2%E6%96%87%E4%BB%B6/Java%E7%AC%AC%E5%9B%9B%E9%98%B6%E6%AE%B5/20220719-MySQL%E9%AB%98%E7%BA%A7-DAY01/MySQL%E9%AB%98%E7%BA%A7.assets/image-20220719144733221.png)]
优点:
缺点:
架构优化
系统规模大,并发量大、数据量大
解决表数据量大
分库分表
垂直分库
将相关的表,放入不同的数据库,如将电商数据库分为:订单数据库、库存数据库
水平分库
将一个数据库的数据,分开存储到不同的服务器上
垂直分表
将表的字段分开,放到不同的表中
如:商品表(id,名称,价格,详细信息,图片。。。)分为商品和详情表
某些数据在分页中查看,某些大的数据text\blob放详情表
水平分表
数据量特别大,会严重影响查询
将一个表的数据分到多张表中
解决并发量大
MySQL集群,主从复制
多台MYSQL服务器,分为主(master)和从(slave)服务器
一主多从,多主多从
读写分离
建立在MySQL主从复制的基础上,分为主数据库和从数据库
主数据库负责写(增删改)从数据库负责读(查询)
主数据库写入数据后,会记录到bin-log文件中,从数据库会将bin-log中的数据同步过来
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wMpfIHg0-1660044523029)(F:/%E6%A1%8C%E9%9D%A2%E6%96%87%E4%BB%B6/Java%E7%AC%AC%E5%9B%9B%E9%98%B6%E6%AE%B5/20220719-MySQL%E9%AB%98%E7%BA%A7-DAY01/MySQL%E9%AB%98%E7%BA%A7.assets/image-20220719154417541.png)]
设计优化
规范化设计
范式
优点: 规范数据库设计,消除冗余,方便数据的修改
缺点: 降低查询效率
反范式 在表中加入冗余字段
提高查询效率
表的设计按具体情况范式和反范式结合使用
选用合适的存储引擎
存储引擎是数据库中存储数据的方式,如存储的位置、存储约束、数据结构等的结合
不同的存储引擎有不同的功能和性能
常用存储引擎:
- InnoDB
- MyIsam
- Memory (不能持久化)
- Blackhole
- Performance Schema
- ....
不同点:
InnoDB | MyIsam | |
---|---|---|
事务 | 支持 | 不支持 |
查询性能 | 略低,并发性能高,数据安全性高 | 高 |
锁 | 支持表锁和行锁,并发性能高 | 支持表锁 |
外键 | 支持 | 不支持 |
行数保存 | 不保存,需要用count(*) | 保存 |
全文索引 | 不支持 | 支持 |
容错 | 支持数据恢复,通过bin-log日志 | 不支持 |
推荐使用InnoDB
字段优化
主键
必须给表设置主键id
尽量不用业务字段,使用没有意义字段如(int自增)
int自增效率高于UUID
数据类型
字符串尽量使用varchar,不使用char (varchar存储空间更小,数据少查询效率高)
尽量使用小的数据类型,如:性别 varchar(1) 男 女 --> int 1 0 --> small int --> tiny int (1字节 -128~127)
有限的值使用 enum, 而不是 varchar
字段尽量加not null约束
使用优化
1) 加索引
介绍索引的应用场景、分类
2) 加缓存
介绍Redis缓存、MyBatis缓存
3) 使用连接池
介绍Druid\Hikari\c3p0\dbcp
4) 分页查询
查询优化
1. 查询之前,使用explain查看执行计划
2. 尽量避免select *
3. 尽量避免where中使用<>和!=
4. 尽量避免where中列避免使用函数和表达式
5. 尽量避免模糊查询时,通配符在前面
6. 尽量使用exists代替in
7. 尽量避免where中使用or,union 代替
1、第一次访问该servlet时,servlet会调用构造方法创建对象,然后调用init方法进行初始化操作
2、访问servlet时,会调用service方法来处理请求
3、tomcat停止时,会调用destory方法销毁servlet
4、最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收
(1)URL重写技术:就是在URL结尾添加⼀个附加数据以标识该会话,把会话ID通过URL的信息传递过去,以便在服务端进行识别不同的用户;
(2)隐藏表单域:将会话ID添加到HTML表单元素中提交到服务器,此表单不再客户端显示;
(3)cookie的方式:Cookie是Web服务器发送给客户端的⼀小段信息,客户端请求时可以读取该信息发送到服务器端,进而进行用户的识别。对于客户端的每次请求,服务器都会将Cookie发送到客户端,在客户端可以进行保存,以便下次使用。
(4)session的方式: 在服务器端会创建⼀个session对象,产生⼀个sessionID来标识这个session对象,然后将这个sessionID放入到Cookie中发送到客户端,下⼀次访问时,sessionID会发送到服务器,在服务器端进行识别不同的用户,Session是依赖Cookie的,如果Cookie被禁用,那么session也将失效, session默认的会话时长为30分钟
(1)Ajax 即“Asynchronous Javascript And XML”(异步 JavaScript 和 XML),是指⼀种创建交互式网页应用的网页开发技术,技术内容包括: HTML 或 XHTML, CSS, JavaScript, DOM, XML, XSLT, 以及最重要的XMLHttpRequest。用于浏览器与服务器之间使用异步数据传输(HTTP 请求),做到局部请求以实现局部刷新
(2) 1.不刷新页面而更新网页(局部刷新)
2.在页面加载后从服务器请求数据
3.在页面加载后从服务器接收数据
4.在后台向服务器发送数据
(3)实现方式:
1.创建XMLHttpRequest对象
2.使用open方法设置和服务器的交互信息
3.设置requestHeader() request.setRequestHeader(属性名称, 属性值);
4.send() 设置发送的数据,开始和服务器端交互
5.取得响应,注册事件
1、同步:发送⼀个请求,需要等待响应返回,然后才能够发送下⼀个请求,如果该请求没有响应,不能发送下⼀个请求,客户端会处于⼀直等待过程中。
2、异步:发送⼀个请求,不需要等待响应返回,随时可以再发送下⼀个请求,即不需要等待。
1、内连接又分为等值连接、自然连接和不等连接三种。
2、外连接分为左外连接(LEFT OUTER JOIN或LEFT JOIN)、右外连接(RIGHT OUTER JOIN或RIGHT JOIN)和全外连接(FULL OUTER JOIN或FULL JOIN)三种。与内连接不同的是,外连接不只列出与连接条件相匹配的行,而是列出左表(左外连接时)、右表(右外连接时)或两个表(全外连接时)中所有符合搜索条件的数据行。
3、交叉连接(CROSS JOIN)没有WHERE 子句,它返回连接表中所有数据行的笛卡尔积,其结果集合中的数据行数等于第一个表中符合查询条件的数据行数乘以第二个表中符合查询条件的数据行数
左连接:以左表为基准,左表数据会全部加载出来,右表只加载与左表重叠的部分
右连接:以右表为基准,右表数据会全部加载出来,左表只加载与左表重叠的部分
内连接:加载左右重叠的部分
1、git clone 克隆远程资源到本地目录,作为工作目录;
2、然后在本地的克隆目录上添加或修改文件;
3、如果远程修改了,需要同步远程的内容,直接git pull就可以更新本地的文件;
4、本地在修改之后,可以通过git status 查看修改的文件。然后使用git add 添加修改的文件暂到缓冲区;
5、在添加之后,可以使用git commit添加到当前的工作区;
6、在修改完成后,如果发现错误,可以撤回提交并再次修改并提交;
7、git push将本地的修改推送到远程的git服务器。
SpringSecurity是一个强大且高效的安全框架,能够提供用户验证和访问控制服务,能够很好地整合到以Spring为基础的项目中。
SpringBoot对SpringSecurity进行了大量的自动配置,使开发者通过少量的代码和配置就能完成很强大的验证和授权功能,下面我们就体验下SpringSecurity的基本使用。
功能包含:
1.maven的继承与聚合是两个不同的概念,其实互相是没有什么关系的。
2.聚合模块知道它聚合了哪些模块,但是被聚合的模块不知道聚合模块的存在;父模块不知道子模块的存在,但是子模块都必须知道自己的父模块是谁。
3.一般聚合模块是为了方便统一编译,继承模块是为了统一jar包管理。
4.在一些最佳实践中我们会发现:一个POM既是聚合POM,又是父POM,这么做主要是为了方便。
项目启动大会,立项:确定项目、团队组成、开发周期、资金投入等
需求分析:外包和甲方沟通,确定功能需求,签字,自研,产品经理确定需求(竞品分析)
项目设计:系统架构(技术栈、项目部署)架构师/项目经理
前端设计,UI画原型图,确定页面设计和功能,设计接口文档
后端设计,设计数据库、设计接口
项目开发:实现数据库、项目搭建、功能开发、单元测试、提交代码
测试:编写测试用例(黑盒、白盒、灰度、回归…)
项目上线:部署到生产环境,交给用户使用
运维:管理服务器,保证程序正常运行
一个CPU内核一个时间段只能运行一个线程的指令,因为CPU执行的速度特别快所以感觉多个程序同时运行
并发:一个CPU在多个线程间来回切换执行,不是真正同时执行
并行:多个CPU同时执行多个线程,是真正同时执行
进程是程序运行和资源分配的基本单位,一个程序至少有一个进程,一个进程至少有一个线程。进程在执行过程中拥有独立的内存单元,而多个线程共享内存资源,减少切换次数,从而效率更高。
线程是进程的一个实体,是cpu调度和分派的基本单位,是比程序更小的能独立运行的基本单位。同一进程中的多个线程之间可以并发执行。
进程:运行后的程序,是操作系统分配系统资源(内存空间、CPU)的最小单位
线程:每个进程由一个或多个线程组成,线程是CPU进行分配和调度的最小单位(分配时间片)
对比进程和线程:
1、内存方面:
进程需要的资源更多(堆、方法区、本地方法区),线程更轻量级(栈、程序计数器)
线程共享所在进程的内存空间(堆、方法区、本地方法区)
2、创建和销毁以及上下文切换:
进程需要更多时间和资源,线程更快
3、相互通信方面:
进程之间的通信比较麻烦(RPC、网络),线程之间通信更容易(通过进程共享的内存空间)
程序指令执行过程分为:
同步:多个程序指令排队执行
执行有序,数据更加安全
异步:多个程序指令同时执行
效率更高,执行无序,数据可能会有问题
守护线程(即daemon thread),是个服务线程,准确地来说就是服务其他的线程。
后台线程也叫守护线程(精灵线程),后台线程的任务是为其它线程提供服务,当其它线程都死亡后,后台线程会自动死亡
继承Thread类
1) 定义类继承Thread类
2) 重写run方法
3) 创建线程对象,调用start方法
实现Runnable接口
1) 定义类实现Runnable接口
2) 实现run方法
3) 创建Thread对象,传入Runnable对象,调用start方法
实现Callable接口
前面两种方式都实现的run方法没有返回值,如果需要进行运算后返回值,就需要使用Callable接口
1) 实现Callable接口的call方法
2) 创建FutureTask对象传入Callable实现对象
3) 创建Thread线程传入FutureTask对象
4) 启动线程
5) 通过FutureTask的get方法获得返回值
1、Java是单继承的,继承Thread类就不能继承其它类,实现接口没有此限制
2、继承Thread不强制要求重写run,实现Runnable强制要求
3、Runnable可以使用Lambda表达式,语法简介
推荐使用Runnable方式
调用run是在主线程中同步执行的,调用start后才会启动新线程去执行
会抛出异常IllegalThreadStateException,线程是一次性的,不允许执行两次
上下文切换过程,多线程的执行是抢占式的,线程会去抢占CPU,抢到后执行自己的指令,执行过程中CPU可能被其它线程抢占,其它线程执行
每个线程有自己的程序计数器,保存当前线程执行的行数,切换回来后继续执行下面的行代码
线程的生命周期(状态)分为:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HAUl6H4Q-1660044523030)(F:/%E6%A1%8C%E9%9D%A2%E6%96%87%E4%BB%B6/Java%E7%AC%AC%E5%9B%9B%E9%98%B6%E6%AE%B5/20220727-%E7%BA%BF%E7%A8%8B-DAY01/01%E8%AF%BE%E5%A0%82%E8%B5%84%E6%96%99/%E7%BA%BF%E7%A8%8B%E5%9F%BA%E7%A1%80.assets/image-20220727153338822.png)]
常用方法:
1、调用对象不同:sleep是当前线程调用,wait是锁对象调用
2、唤醒机制不同:sleep当时间结束自动唤醒,线程进入wait后,要通过锁对象notify/notifyAll唤醒
3、锁释放不同:线程sleep不会释放锁,线程进入wait后,会自动释放锁
多个线程同时访问一个资源(变量、代码块、数据等),可能出现线程同步问题
多个线程的执行是抢占式的,一个线程在执行一系列程序指令中途时,就被其它线程抢占,可能导致数据状态出现问题。
解决方式:锁机制
synchronized 关键字
实现锁机制,可以用于修饰方法或代码块
提升数据的安全性,降低性能
同步方法:当第一个线程进入方法后,自动上锁,其它线程访问不了,前面线程执行完方法后,自动释放锁,其它线程就可以访问
同步代码块:
锁对象:
1. 任意的Java对象都可以作为锁
2. 不能是局部变量
同步锁:
常见的实现类:
ReentrantLock 重入锁
ReadLock 读锁
WriterLock 写锁
构造方法可以带参数
new ReentrantLock(是否是公平锁);
对比三种锁机制:
粒度: 同步方法 > 同步块/同步锁
性能: 同步锁 > 同步块 > 同步方法
方便: 同步方法 > 同步块 > 同步锁
公平锁:等待锁时间长的线程,更容器获得锁
非公平锁:等待锁时间长和短的线程,获得锁几率相同
1) 语法不同,一个写在方法上,一个写在方法内部
2) 锁粒度不同,同步方法作用于整个方法,同步代码块作用于一段代码
3) 性能不同,同步方法低于同步块
4) 锁对象不同,同步方法是固定的,静态就是类.class,非静态的就是this
同步代码块可以指定锁对象
通过监视器(monitor)完成
当对方法或一段代码上锁后,会启动监视器对这段代码监控,监视器中有计数器,当计数器为0时,允许线程进入,线程进入后,计数器加1,其它线程访问时,计数器不为0,不允许线程进入,线程执行完代码块后,计数器减1为0 ,监视器允许其它线程进入。
线程死锁描述的是这样一种情况:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。是操作系统层面的一个错误,是进程死锁的简称,最早在 1965 年由 Dijkstra 在研究银行家算法时提出的,它是计算机操作系统乃至整个并发程序设计领域最难处理的问题之一。
上锁:synchronized是自动上锁和解锁,同步锁是手动完成的
性能:同步锁的性能高于synchronized
使用:synchronized使用简单,功能单一,同步锁提供更多方法,使用灵活
线程同步有三大特性:
volatile用于修饰成员变量,保证变量的可见性和有序性
修饰的对象:synchronized修饰方法或代码块,volatile只能修饰变量
保证的特性:synchronized保证原子性、可见性;volatile不能保证原子性,可以保证可见性和有序性
性能:volatile是轻量级的线程同步方式,性能更高
ThreadLocal叫做线程变量,它填充的变量属于当前线程,该变量对其它线程而言是隔离的,该变量是当前线程独有变量;两个常见的使用场景①每个线程需要有自己单独的实例②实例需要在多个方法中共享,但不想被多个线程共享。
悲观锁:比较悲观,认为线程同步问题会经常出现,倾向于给资源上锁
乐观锁:比较乐观,认为线程同步问题不会常出现,不给资源上锁,通过其它方式解决
版本号机制,对数据设置版本,每次修改后版本会更新,修改后将版本和前面版本进行比较,相同就提交,不同就不提交
CAS机制,CompareAndSwap
通过内存偏移量获得数据的原始值,通过原始值计算出预期的值,将预期的值和实际的值进行比较,相同就更新,否则就不更新进入循环等待状态,直到比较相等
如果线程的竞争比较激烈,应该使用悲观锁;
线程竞争不强的时候,使用乐观锁。
锁对象:
同步方法
同步代码块
写在synchronized括号中的对象
可以通过锁对象调用Object类的方法:
注意:如果不是锁对象调用上面的方法会出现异常IllegalMonitorStateException
是一种设计模式,用于解决两个点(线程、进程、服务器)之间数据通信的协调问题。
生产数据的点叫生产者,使用数据的点叫消费者,生产者和消费者可能存在速度不一致的情况。生产者速度过快,消费者消费速度慢,会浪费大量资源;反过来,消费者速度快,生产者速度慢,消费者浪费时间取等待。
过程:
解决的问题:
是一种特殊的集合,会自动阻塞对队列添加或删除数据的线程,也可以自动通知BlockingQueue接口
put(T) 添加数据到队尾,到临界值阻塞
T take 从队头取出并删除数据,到空时阻塞
常用实现类:
ArrayBlockingQueue 数组结构的阻塞队列
LinkedBlockingQueue 链表结构的阻塞队列
SynchronousQueue 同步机制的队列
线程局部变量,也是解决线程同步问题的一种方式
锁机制 多个线程排队访问一个资源 以时间换空间
ThreadLocal 多个线程并发访问自己独立的资源 以空间换时间
每个线程内部有Map集合ThreadLocalMap,获得数据时候返回当前线程的ThreadLocalMap,Map中有大量键值对Entry,键是当前的ThreadLocal对象,值是保存的数据。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y2maSs3Q-1660044523030)(F:/%E6%A1%8C%E9%9D%A2%E6%96%87%E4%BB%B6/Java%E7%AC%AC%E5%9B%9B%E9%98%B6%E6%AE%B5/20220729-%E7%BA%BF%E7%A8%8B-DAY03/01%E8%AF%BE%E5%A0%82%E8%B5%84%E6%96%99/%E7%BA%BF%E7%A8%8B%E9%80%9A%E4%BF%A1.assets/image-20220729170244858.png)]
强引用: 一般情况下创建的对象
Person person = new Person()
如果对象有引用指向,就不会被垃圾收集,没有引用指向就可能被垃圾收集
person = null;
软引用: SoftReference
如果即将出现OOM,gc会优先回收软引用,比强引用更容易被回收
主要用于缓存
弱引用:WeakReference
只要出现gc,都会进行回收
可以缓存临时数据
虚引用: PhantomReference
形同虚设,不能作为对象的引用使用,主要用于跟踪垃圾收集器的活动
强引用 > 软引用 > 弱引用 > 虚引用
线程是一种有限的宝贵的系统资源,创建线程需要很多时间和系统资源,需要对线程进行回收利用,降低对系统资源的消耗,提升服务器的执行效率
顶层接口:
Executor
子接口:
ExecutorService
扩展:
ExecutorService的子接口:ScheduledExecutorService
扩展:
在固定的延迟时间后执行
scheduleWithFixedDelay(执行的Runnable任务,初始的延迟,延迟,时间单位)
在固定的周期执行
scheduleAtFixedRate(执行的Runnable任务,初始的延迟,周期,时间单位)
工具类:
Executors 提供了几个静态方法,帮助方便的创建线程池
线程池类型线程池类型 | 说明 |
---|---|
ExecutorService newCachedThreadPool() | 创建线程个数不限的线程池 |
ExecutorService newFixedThreadPool(int) | 创建个数固定的线程池 |
ExecutorService newSingleThreadExecutor() | 创建单个线程的线程池 |
ScheduledExecutorService newScheduledThreadPool(int) | 创建可以调度的线程池 |
ExecutorService newWorkStealingPool() | 创建工作窃取机制的线程池 |
线程池的实现类:
ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
参数 | 说明 |
---|---|
corePoolSize | 核心线程数(线程池初始的线程数,核心线程会一直保留) |
maximumPoolSize | 最大线程数(线程池最大线程数,非核心线程可能被干掉) |
keepAliveTime | 保持存活的时间(非核心线程如果在此时间内没有接收任务,就可能被干掉) |
unit | 时间单位 |
BlockingQueue | 用于保存任务的阻塞队列 |
ThreadFactory | 线程工厂,用于创新线程 |
RejectedExecutionHandler | 拒绝策略(任务数超过最大线程数,拒绝执行任务的方式) |
核心线程数 和cpu核心数以及任务数量和执行时间相关
核心线程数 = cpu核心数 * n
n >= 1 上限和任务数以及执行时间相关
本机的cpu核心数
Runtime.getRuntime().availableProcessors();
最大线程数
最大线程数和核心线程数相同,减少系统创建线程和销毁线程的时间和资源,提高性能
keepAliveTime
如果最大线程数和核心线程数相同,可以设置为0;
否则可以尽量大一点,减少系统创建线程和销毁线程的时间和资源
BlockingQueue
设置为LinkedBlockQueue,任务经常添加和删除时,性能更高
设置为SynchorousQueue,能处理的任务数更多,吞吐量更高(每秒处理请求数)
问题1: 线程池总的执行过程?
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OrACUiRB-1660044523031)(F:/%E6%A1%8C%E9%9D%A2%E6%96%87%E4%BB%B6/Java%E7%AC%AC%E5%9B%9B%E9%98%B6%E6%AE%B5/20220802-%E7%BA%BF%E7%A8%8B-DAY04/01%E8%AF%BE%E5%A0%82%E8%B5%84%E6%96%99/%E7%BA%BF%E7%A8%8B%E6%B1%A0.assets/image-20220802150814997.png)]
问题2: 线程池的线程是如何回收?
怎么让线程不死,能执行下一个任务
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CvAmeXi4-1660044523031)(F:/%E6%A1%8C%E9%9D%A2%E6%96%87%E4%BB%B6/Java%E7%AC%AC%E5%9B%9B%E9%98%B6%E6%AE%B5/20220802-%E7%BA%BF%E7%A8%8B-DAY04/01%E8%AF%BE%E5%A0%82%E8%B5%84%E6%96%99/%E7%BA%BF%E7%A8%8B%E6%B1%A0.assets/image-20220802152904323.png)]
步骤:
1) 通过线程池的execute执行任务
2) 通过调用线程池的addWorker方法添加工作线程
3) 同时把Runnable执行任务添加到workQueue中
4) 创建Worker添加到HashSet中,Worker中包含线程
5) 启动Worker中的线程
6) 线程调用runWorker方法
7) runWorker中判断任务不为空就执行任务
8) 任务如果为空,就调用getTask()方法取任务
9) 循环不断调用workerQueue阻塞队列的take()方法
10) 任务队列为空就自动阻塞当前线程,直到任务队列不为空唤醒线程去执行任务
UDP | TCP | |
---|---|---|
是否连接 | 无连接 | 面向连接 |
是否可靠 | 不可靠传输 | 可靠传输 |
连接个数 | 支持一对一,一对多,多对多通信 | 只能是一对一通信 |
传输方式 | 面向报文 | 面向字节流 |
适用场景 | 适用于实时应用(IP电话、视频会议、直播等) | 适用于要求可靠传输的应用,例如文件传输 |
超文本传输协议(Hyper Text Transfer Protocol)用于规范在网络中对文本数据的传输,属于应用层协议,底层是基于TCP/IP协议。
三次握手
TCP可靠性基于三次握手
点对点通信前通过三次握手建立连接
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f42Cqgt0-1660044523032)(F:/%E6%A1%8C%E9%9D%A2%E6%96%87%E4%BB%B6/Java%E7%AC%AC%E5%9B%9B%E9%98%B6%E6%AE%B5/20220804-Socket%E7%BD%91%E7%BB%9C%E9%80%9A%E4%BF%A1-DAY01/01%E8%AF%BE%E5%A0%82%E8%B5%84%E6%96%99/Socket%E7%BD%91%E7%BB%9C%E9%80%9A%E4%BF%A1.assets/20210119102417176.png)]
1) 客户端向服务端发送报文,标志位SYN(同步请求),seq(序列号)给服务器,客户端进入SYN_SENT(请求发送)状态
2) 服务端接收了客户端的报文,进入SYN_RCVD(同步接收)状态,然后发送报文SYN(同步),ACK(应答),ack(应答序列号),seq(序列号)给客户端
3) 客户端收到服务端的报文进入Established(连接建立),发送应答报文ACK,ack给服务端,服务端进入Established状态,双方建立连接
为什么三次?
四次挥手
点对点断开连接需要进行四次挥手
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SZ1neDLI-1660044523032)(F:/%E6%A1%8C%E9%9D%A2%E6%96%87%E4%BB%B6/Java%E7%AC%AC%E5%9B%9B%E9%98%B6%E6%AE%B5/20220804-Socket%E7%BD%91%E7%BB%9C%E9%80%9A%E4%BF%A1-DAY01/01%E8%AF%BE%E5%A0%82%E8%B5%84%E6%96%99/Socket%E7%BD%91%E7%BB%9C%E9%80%9A%E4%BF%A1.assets/20210119102910159.png)]
1) 客户端向服务端发送结束报文FIN,进入等待状态1
2) 服务端收到结束报文后,进入等待关闭状态,发送应答报文ack
3) 客户端接收服务端的应答报文ack,进入等待状态2
4) 服务端释放资源后,发送结束FIN给客户端,进入LAST_ACK状态
5) 客户端收到服务端的结束报文,发送应答报文ACK给服务端,连接关闭
七层分别是应用层( Application)、表示层( Presentation)、会话层( Session)、传输层( Transport)、
网络层( Network)、数据链路层(Link)、物理层( Physical)。
从下到上分为:
1) 物理层,底层硬件
2) 数据链路层,通信的介质
3) 网络层,寻址和路由, IP协议
4) 传输层,连接和数据通信,TCP协议\UDP协议
5) 会话层,管理会话
6) 表示层,处理数据格式、加密
7) 应用层,程序之间的通信,http协议\ftp协议\smtp协议\pop3协议
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xA4blchM-1660044523033)(F:/%E6%A1%8C%E9%9D%A2%E6%96%87%E4%BB%B6/Java%E7%AC%AC%E5%9B%9B%E9%98%B6%E6%AE%B5/20220804-Socket%E7%BD%91%E7%BB%9C%E9%80%9A%E4%BF%A1-DAY01/01%E8%AF%BE%E5%A0%82%E8%B5%84%E6%96%99/Socket%E7%BD%91%E7%BB%9C%E9%80%9A%E4%BF%A1.assets/20210119100058516.png)]
套接字,基于TCP/IP协议
实现文件传输
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hvOxQ0zO-1660044523034)(F:/%E6%A1%8C%E9%9D%A2%E6%96%87%E4%BB%B6/Java%E7%AC%AC%E5%9B%9B%E9%98%B6%E6%AE%B5/20220804-Socket%E7%BD%91%E7%BB%9C%E9%80%9A%E4%BF%A1-DAY01/01%E8%AF%BE%E5%A0%82%E8%B5%84%E6%96%99/Socket%E7%BD%91%E7%BB%9C%E9%80%9A%E4%BF%A1.assets/image-20220804155424959.png)]
客户端:
服务端:
1、属性不可分割
2、非主属性必须完全依赖主键
3、属性不依赖于其他非主属性
第一范式:强调的是列的原子性,即数据库表的每一列都是不可分割的原子数据项。
第二范式:要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性。
第三范式:任何主属性不依赖于其它非主属性。
①lnnoDB
②MyISAM
③MEMORY
InnoDB 引擎:数据库默认引擎,InnoDB 引擎提供了对数据库ACID事务的支持,并且还提供了行级锁和外键的约束,它的设计的目标就是处理大数据容量的数据库系统。MySQL 运行的时候,InnoDB 会在内存中建立缓冲池,用于缓冲数据和索引。但是该引擎是不支持全文搜索,同时启动也比较的慢,它是不会保存表的行数的,所以当进行 select count(*) from table 指令的时候,需要进行扫描全表。由于锁的粒度小,写操作是不会锁定全表的,所以在并发度较高的场景下使用会提升效率的。
MyIASM 引擎:不提供事务的支持,也不支持行级锁和外键。因此当执行插入和更新语句时,即执行写操作的时候需要锁定这个表,所以会导致效率会降低。不过和 InnoDB 不同的是,MyIASM 引擎是保存了表的行数,于是当进行 select count(*) from table 语句时,可以直接的读取已经保存的值而不需要进行扫描全表。所以,如果表的读操作远远多于写操作时,并且不需要事务的支持的,可以将 MyIASM 作为数据库引擎的首选。
group by:按照某个字段或者某些字段进行分组,是在where执行之后才会执行。
having:是对分组后的数据进行在过滤,在group by之后执行,没有它就没有having。
内连接(inner join):取出两张表中匹配到的数据,匹配不到的不保留
外连接(outer join):取出两张表中匹配到的数据,匹配不到的也会保留,其值为NULL
SpringCloud提供了一系列工具让我们更快地开发分布式/微服务应用。
功能包含:
Nacos (配置中心与服务注册与发现)
Sentinel (分布式流控)
RocketMQ (消息队列)
Seata (分布式事物)
Dubbo (RPC)
其他 SpringCloud Alibaba还有一些其他的组件选择,例如schedulerX、SMS、OSS等。
什么是 CAP理论
CAP 原则又称 CAP 定理,指的是在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性)这三个基本需求,最多只能同时满足其中的 2 个。
一致性 :数据在多个副本之间能够保持一致的特性。可用性:系统提供的服务一直处于可用的状态,每次请求都能获得正确的响应。分区容错性:分布式系统在遇到任何网络分区故障的时候,仍然能够对外提供满足一致性和可用性的服务。
什么是 BASE 理论
BASE 是 Basically Available(基本可用) 、Soft-state(软状态) 和 Eventually Consistent(最终一致性) 三个短语的缩写。
BASE 理论的三个特性基本可用什么是基本可用呢?假如系统出现了不可预知故障,允许损失部分可用性,当然也不能完全不可用。
损失的这部分可用性指的是什么?响应时间上的损失:正常情况下的搜索引擎 0.5 秒即返回给用户结果,而基本可用的搜索引擎可以在 2 秒作用返回结果。
功能上的损失:在一个电商网站上,正常情况下,用户可以顺利完成每一笔订单。但是到了大促期间,为了保护购物系统的稳定性,部分消费者可能会被引导到一个降级页面。
软状态:软状态指允许系统中的数据存在中间状态(CAP 理论中的数据不一致),并认为该中间状态的存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时。
最终一致性 最终一致性强调的是系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。分布式一致性的 3 种级别:强一致性 :系统写入了什么,读出来的就是什么。弱一致性 :不一定可以读取到最新写入的值,也不保证多少时间之后读取到的数据是最新的,只是会尽量保证某个时刻达到数据一致的状态。最终一致性 :弱一致性的升级版,系统会保证在一定时间内达到数据一致的状态。
微服务架构 就是 对微服务进行管理整合应用的。
微服务架构 依赖于 微服务,是在微服务基础之上的。高并发问题,等其他问题 造成单体服务太难以把控和治理了,而随着时间的推移单体服务将会越来越难以维护和治理,因此引入了 相对复杂的微服务架构 来 治理 更复杂的 服务
服务注册和发现的意思是服务进程在注册中心注册自己的位置,客户端应用进程向注册中心发起查询,来获取服务的位置,服务发现的一个重要作用就是提供一个可用的服务列表。注册中心主要涉及到三大角色:服务提供者、服务消费者、注册中心,它们之间的关系大致如下:
各个微服务在启动时,将自己的网络地址等信息注册到注册中心,注册中心存储这些数据。
服务消费者从注册中心查询服务提供者的地址,并通过该地址调用服务提供者的接口。
各个微服务与注册中心使用一定机制(例如心跳)通信。如果注册中心与某微服务长时间无法通信,就会注销该实例。
微服务网络地址发送变化(例如实例增加或IP变动等)时,会重新注册到注册中心。这样,服务消费者就无需人工修改提供者的网络地址了。
负载均衡(Load balancing),是一种计算机技术,能将用户请求按一定的负载均衡算法,分配给集群中的每一台服务器上,从而避免单台服务器过载,也提高了服务器集群处理请求的响应速度,达到服务器资源的优化配置。
负载均衡可以通过客户端、服务器端和硬件实现,Nginx属于服务器端负载均衡技术。
(1)HTTP重定向负载均衡。
这种负载均衡方案的优点是比较简单,缺点是浏览器需要每次请求两次服务器才能完成一次访问,性能较差。
(2)DNS域名解析负载均衡。
DNS域名解析负载均衡的优点是将负载均衡工作交给DNS,省略掉了网络管理的麻烦,缺点就是DNS可能缓存A记录,不受网站控制。
(3)反向代理负载均衡。
优点是部署简单,缺点是反向代理服务器是所有请求和响应的中转站,其性能可能会成为瓶颈。
(4)IP负载均衡。
优点:IP负载均衡在内核进程完成数据分发,较反向代理均衡有更好的处理性能。缺点:负载均衡的网卡带宽成为系统的瓶颈。
(5)数据链路层负载均衡。
避免负载均衡服务器网卡带宽成为瓶颈,是目前大型网站所使用的最广的一种负载均衡手段。
Nginx是一个轻量级、高性能、稳定性高、并发性强的HTTP和反向代理服务器。目前在互联网企业中应用非常广泛,如:百度、京东、新浪、网易、腾讯、淘宝等。
Nginx的作用主要有:
http服务器
Nginx一般用于部署静态资源,和部署动态资源的服务器(如:Tomcat) 分开部署,实现动静分离,达到服务器性能的最大化。
反向代理
代理后台服务器,通过配置实现灵活的路由
负载均衡
将大量的用户请求,均衡的分配给多台服务器,提高系统的负载能力
服务熔断:服务熔断的作用类似于我们家用的保险丝,当某服务出现不可用或响应超时的情况时,为了防止整个系统出现雪崩,暂时停止对该服务的调用。
服务降级:服务降级是从整个系统的负荷情况出发和考虑的,对某些负荷会比较高的情况,为了预防某些功能(业务场景)出现负荷过载或者响应慢的情况,在其内部暂时舍弃对一些非核心的接口和数据的请求,而直接返回一个提前准备好的fallback(退路)错误处理信息。这样,虽然提供的是一个有损的服务,但却保证了整个系统的稳定性和可用性。
Feign是一种负载均衡的HTTP客户端, 使用Feign调用API就像调用本地方法一样,从避免了调用目标微服务时,需要不断的解析/封装json 数据的繁琐。Feign集成了Ribbon。Ribbon+eureka是面向微服务编程,而Feign是面向接口编程。 Fegin是一个声明似的web服务客户端,它使得编写web服务客户端变得更加容易。使用Fegin创建一个接口并对它进行注解。它具有可插拔的注解支持包括Feign注解与JAX-RS注解,Feign还支持可插拔的编码器与解码器,Spring Cloud 增加了对 Spring MVC的注解,Spring Web 默认使用了HttpMessageConverters, Spring Cloud 集成 Ribbon 和 Eureka 提供的负载均衡的HTTP客户端 Feign。
网关(Gateway)又称网间连接器、协议转换器。网关在传输层上以实现网络互连,是最复杂的网络互连设备,仅用于两个高层协议不同的网络互连。网关的结构也和路由器类似,不同的是互连层。网关既可以用于广域网互连,也可以用于局域网互连。 网关是一种充当转换重任的计算机系统或设备。在使用不同的通信协议、数据格式或语言,甚至体系结构完全不同的两种系统之间,网关是一个翻译器。与网桥只是简单地传达信息不同,网关对收到的信息要重新打包,以适应目的系统的需求。同时,网关也可以提供过滤和安全功能。大多数网关运行在OSI 7层协议的顶层–应用层。
顾名思义,网关(Gateway)就是一个网络连接到另一个网络的“关口”。在OSI中,网关有两种:一种是面向连接的网关,一种是无连接的网关。当两个子网之间有一定距离时,往往将一个网关分成两半,中间用一条链路连接起来,我们称之为半网关。就是将两个使用不同协议的网络段连接在一起的设备。它的作用就是对两 个网络段中的使用不同传输协议的数据进行互相的翻译转换。
好比是个门,对家庭来说门是门,对国家来说海关是门。在局域网里来说集线器就是网关,在二层网络里,交换机就是网关,在三层网络里路由就是网关,说网关要看你的网是多大的,要拿中国来说,连着美国那台世界服务器的设备就是网关。工作机制:https://blog.csdn.net/caolei129118/article/details/119319716
RabbitMQ是一个消息队列中间件:是指利用高效可靠的消息传递机制进行与平台无关的数据交流,并基于数据通信来进行的分布式系统的集成。
作用:1.解耦 消息中间件在处理过程中插入了一个接口层,两边处理数据都需要实现这个接口,这可以允许你独立扩展和修改两边的过程
2.异步通信 很多时候业务不需要马上处理消息。消息中间件提供了异步处理机制
3.削峰:在访问量剧增的情况下,应用仍然可以发挥作用。但是这类的情况是某个时刻发生的而不是常况。为此投入过多资源无疑造成了巨大的浪费。使用消息中间件可以使系统撑住突然爆发的访问压力。
4.扩展性
因为消息中间件解耦了应用的处理过程,所以提高消息入队和处理的效率是很容易的,只需要另外增加处理过程即可。
5.可恢复性
当系统系统一部分服务失效时,不糊影响整个系统。消息中间件降低了进程间的耦合度,所以即使进程挂掉消息也可以等恢复后进行处理。
1.点对点的队列
2 工作队列模式Work Queue
3 发布/订阅模式Publish/Subscribe
4 路由模式Routing
5 通配符模式Topics
6.spring集成rabbitmq配置
分不同发生的情况:
1.生产者端丢失
① RabbitMQ 事务机制(同步)
② 开启 confirm 模式(异步效率高)
设置开启 confirm 模式之后,你每次写的消息都会分配一个唯一的 id,然后如果写入了 RabbitMQ 中,RabbitMQ 会给你回传一个 ack 消息,告诉你说这个消息 ok 了。如果 RabbitMQ 没能处理这个消息,会回调你的一个 nack 接口,告诉你这个消息接收失败,你可以重试。而且你可以结合这个机制自己在内存里维护每个消息 id 的状态,如果超过一定时间还没接收到这个消息的回调,那么你可以重发。
事务机制和 cnofirm 机制最大的不同在于,事务机制是同步的,你提交一个事务之后会阻塞在那儿,但是 confirm 机制是异步的,你发送个消息之后就可以发送下一个消息,然后那个消息 RabbitMQ 接收了之后会异步回调你的一个接口通知你这个消息接收到了。
所以一般在生产者这块避免数据丢失,都是用 confirm 机制的。
2.RabbitMq宕机丢失
开启 RabbitMQ 的持久化
默认会先缓存到oscache中,再异步持久化到磁盘。
如果缓存到oscache中还未来得及持久化磁盘就宕机,也可能造成丢失。因此可以开启同步刷盘+主从备份
3.消费者端丢失
消息刚发给消费者,业务逻辑尚未处理完,此时已经自动确认,消费者宕机,会造成消息丢失。
关闭自动ack,在业务逻辑处理完之后,手动进行ack确认
可以采用mysql主键唯一索引,或者redis做消息幂等表。
单一、普通、镜像、远程双活、多活
**单一模式:**即单机情况不做集群,就单独运行一个 rabbitmq 而已。
**普通模式:**默认模式,以两个节点(rabbit01、rabbit02)为例来进行说明。对于 Queue 来说,消息实体只存在于其中一个节点 rabbit01(或者 rabbit02),rabbit01 和 rabbit02 两个节点仅有相同的元数据,即队列的结构。当消息进入 rabbit01 节点的 Queue 后,consumer 从 rabbit02 节点消费时,RabbitMQ 会临时在 rabbit01、rabbit02 间进行消息传输,把 A 中的消息实体取出并经过 B 发送给 consumer。所以 consumer 应尽量连接每一个节点,从中取消息。即对于同一个逻辑队列,要在多个节点建立物理 Queue。否则无论 consumer 连 rabbit01 或 rabbit02,出口总在 rabbit01,会产生瓶颈。当 rabbit01 节点故障后,rabbit02 节点无法取到 rabbit01 节点中还未消费的消息实体。如果做了消息持久化,那么得等 rabbit01 节点恢复,然后才可被消费;如果没有持久化的话,就会产生消息丢失的现象。
**镜像模式:**把需要的队列做成镜像队列,存在与多个节点属于 RabbitMQ 的 HA 方案。该模式解决了普通模式中的问题,其实质和普通模式不同之处在于,消息实体会主动在镜像节点间同步,而不是在客户端取数据时临时拉取。该模式带来的副作用也很明显,除了降低系统性能外,如果镜像队列数量过多,加之大量的消息进入,集群内部的网络带宽将会被这种同步通讯大大消耗掉。所以在对可靠性要求较高的场合中适用。
远程模式:远程模式可以实现双活的一种模式,简称Shovel模式,所谓Shovel就是我们可以把消息进行不同数据中心的复制工作,我们可以跨地域的让两个MQ集群互联,比如说一个集群,我们都会放在一个机房里面,那么如果北京的机房出现了一些事故停电,或者自然灾害,那么这个集群都会宕机了,那么在我们对数据要求极高的大型应用我们需要设置多活或者双活的模式,也就是要搭建多个数据中心,或者多套集群,那么这些集群可以一个会放在上海,一个放在北京,还有应放在广州,三个集群数据都是同步的,中间有任何一个集群出现了问题,马上灵活的切换,那么这三个集群都是可以访问的话,我们可能会按照距离,或者访问速度来进行优先选择哪组集群,或者数据中心进行访问,所有多活模式,在银行开发的时候一般也叫做容灾的机制,至少构建两套集群放在不同的地域,一个有问题了,立马进行切换,不至于整个系统宕机,这就是多活模式,在多活模式中MQ也提供了相应的实现方式,早期使用的Shovel模式,这个模式是mq自带的一种模式,主要就是可以把消息在不同的数据中心进行负载分发,主要就是可以实现跨地域的让两个mq集群进行互联。
那么这个shovel模式需要统一的版本,网络达到一个什么样的水平,配置的话也是有些复杂,这种的话目前已经淘汰了,在真正的数据复制的情况下,会使用多活模式。
**多活模式:**这种模式也是实现异地数据复制的主流模式,这种模式是依赖rabbimq的fedrtation插件的模式
本身不是mq自带的东西,是在mq上进行了扩展,而这种扩展是实现的可靠的AMQP的数据通信,因此配置起来也比较容易,相当于配置简单化之后的shovel
RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件
消息队列是什么
是一种应用间的异步协作机制
作用
以常见的订单系统为例,用户点击【下单】按钮之后的业务逻辑可能包括:扣减库存、生成相应单据、发红包、发短信通知。在业务发展初期这些逻辑可能放在一起同步执行,随着业务的发展订单量增长,需要提升系统服务的性能,这时可以将一些不需要立即生效的操作拆分出来异步执行,比如发放红包、发短信通知等。这种场景下就可以用 MQ ,在下单的主流程(比如扣减库存、生成相应单据)完成之后发送一条消息到 MQ 让主流程快速完结,而由另外的单独线程拉取MQ的消息(或者由 MQ 推送消息),当发现 MQ 中有发红包或发短信之类的消息时,执行相应的业务逻辑。
通常用作数据解耦的情况,其它常见场景包括最终一致性、广播、错峰流控等等。
应用场景
场景说明:用户注册后,需要发注册邮件和注册短信,传统的做法有两种
1.串行方式:将注册信息写入数据库后,发送注册邮件,再发送注册短信,以上三个任务全部完成后才返回给客户端。 这有一个问题是,邮件,短信并不是必须的,它只是一个通知,而这种做法让客户端等待没有必要等待的东西.
2.并行方式:将注册信息写入数据库后,发送邮件的同时,发送短信,以上三个任务完成后,返回给客户端,并行的方式能提高处理的时间。
假设三个业务节点分别使用50ms,串行方式使用时间150ms,并行使用时间100ms。虽然并性已经提高的处理时间,但是,前面说过,邮件和短信对我正常的使用网站没有任何影响,客户端没有必要等着其发送完成才显示注册成功,英爱是写入数据库后就返回.
3.消息队列 :引入消息队列后,把发送邮件,短信不是必须的业务逻辑异步处理
1、什么是Redis
Redis是一款内存高速缓存数据库。Redis全称为:Remote Dictionary Server (远程数据服务),使用C语言编写,Redis是一个key-value存储系统(键值存储系统)
Redis的特点
主要应用场景:
Redis数据类型有:
string
字符串是基本的key-value结构
应用场景:
适合保存单个的值,比如:商品的库存 键 商品id 值 库存
设置值
set 键 值
set name "zhagnsan"
set name "zhagnsan" EX 60 //EX是过期时间,单位是秒
set 键 值 NX //NX键如果不存在就设置成功,存在就失败
读取值
get 键
get name
删除键
del 键名
递增/递减(值必须为数字)
incr 键 递增1
decr 键 递减1
incrby 键 递增量
decrby 键 递减量
示例:
127.0.0.1:6379> set name "zhangsan"
OK
127.0.0.1:6379> get name
"zhangsan"
hash
hash可以保存一个对象的多个key-value
保存对象,student是对象名称,name和age是属性名称
应用场景:保存一个商品对象的各种信息
hmset student name "zhangsan" age 20
读取对象属性
hmget student name
读取对象所有属性
hgetall student
示例:
127.0.0.1:6379> hmset student name "zhangsan" age 20
OK
127.0.0.1:6379> hmget student name
1) "zhangsan"
127.0.0.1:6379> hgetall student
1) "name"
2) "zhangsan"
3) "age"
4) "20"
list
list采用链表结构保存多个数据,是有序的、可重复的。
添加列表
lpush students zhangsan
lpush students lisi
lpush students wangwu
读取列表,0和2是开始和结束位置
lrange students 0 2
示例:
127.0.0.1:6379> lpush students zhangsan
(integer) 1
127.0.0.1:6379> lpush students lisi
(integer) 2
127.0.0.1:6379> lpush students wangwu
(integer) 3
127.0.0.1:6379> lrange students 0 2
1) "wangwu"
2) "lisi"
3) "zhangsan"
set
set是无序的、不可重复的集合。
添加数据
sadd students zhangsan
sadd students lisi
sadd students wangwu
读取数据
smembers students
示例:
127.0.0.1:6379> del students
(integer) 1
127.0.0.1:6379> sadd students zhangsan
(integer) 1
127.0.0.1:6379> sadd students lisi
(integer) 1
127.0.0.1:6379> sadd students wangwu
(integer) 1
127.0.0.1:6379> smembers students
1) "wangwu"
2) "zhangsan"
3) "lisi"
zset
zset是有序的、不可重复的集合。
添加数据,要添加一个score数字,按score排序
zadd key score value
读取数据
zrangebyscore key start end
zrange key start end
示例:
127.0.0.1:6379> del students
(integer) 1
127.0.0.1:6379> zadd students 1 zhangsan
(integer) 1
127.0.0.1:6379> zadd students 2 lisi
(integer) 1
127.0.0.1:6379> zadd students 3 wangwu
(integer) 1
127.0.0.1:6379> zrangebyscore students 0 1000
1) "zhangsan"
2) "lisi"
3) "wangwu"
雪崩:在高并发下,大量缓存key在同一时间内失效,大量请求直接落在数据库上,导致数据库宕机。
穿透:redis缓存和数据库中没有相关数据(例用户直接携带id<=0的参数不断发起请求),redis中没有这样的数据,无法进行拦截,直接被穿透到数据库,导致数据库压力过大宕机。
缓存穿透:指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。
解决方案:最简单粗暴的方法如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们就把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。
击穿:某一个热点key,在不停地扛着高并发,当这个热点key在失效的一瞬间,持续的高并发访问就击破缓存直接访问数据库,导致数据库宕机。
问题 | 原因 | 解决方案 |
---|---|---|
雪崩,大量请求在Redis中查询不到数据,直接访问数据库,导致数据库宕机 | 1. Redis服务器宕机或重启 2. 热点数据同时过期 | 1. 搭建Redis集群 2. 将热点数据的过期时间设置为随机值,避免同时过期 |
击穿,大量线程并发访问Redis,同时穿过Redis,直接访问数据库导致数据库宕机 | 1. 线程的高并发,前面线程还没有将数据保存到Redis中,其它线程就进入Redis进行查询 | 1. 通过锁机制,对Redis操作进行同步 DCL 双检锁 |
穿透,大量并发查询数据库中没有的数据,Redis中没有,请求直接打到数据库,导致宕机 | 1. 数据库中没有,Redis中没有保存,直接查询数据库 | 1. 将数据库中没有的数据,在Redis保存空数据,给空数据设置超时 2. 使用布隆过滤器,过滤掉不存在的数据 |
哨兵架构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ayLzFokV-1660044523034)(F:/%E6%A1%8C%E9%9D%A2%E6%96%87%E4%BB%B6/Java%E7%AC%AC%E5%9B%9B%E9%98%B6%E6%AE%B5/20220803-Redis%E8%A1%A5%E5%85%85-DAY01/01%E8%AF%BE%E5%A0%82%E8%B5%84%E6%96%99/Redis%E8%A1%A5%E5%85%85.assets/image-20220803093243175.png)]
集群架构
将数据的key进行hash运算,获得保存数据的位置,该位置可能是集群中任意一台服务器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AqocMpre-1660044523038)(F:/%E6%A1%8C%E9%9D%A2%E6%96%87%E4%BB%B6/Java%E7%AC%AC%E5%9B%9B%E9%98%B6%E6%AE%B5/20220803-Redis%E8%A1%A5%E5%85%85-DAY01/01%E8%AF%BE%E5%A0%82%E8%B5%84%E6%96%99/Redis%E8%A1%A5%E5%85%85.assets/image-20220803093415036.png)]
布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。
特点:判断存在的数据不一定存在,判断不存在的数据一定不存在
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H6OAvx45-1660044523038)(F:/%E6%A1%8C%E9%9D%A2%E6%96%87%E4%BB%B6/Java%E7%AC%AC%E5%9B%9B%E9%98%B6%E6%AE%B5/20220803-Redis%E8%A1%A5%E5%85%85-DAY01/01%E8%AF%BE%E5%A0%82%E8%B5%84%E6%96%99/Redis%E8%A1%A5%E5%85%85.assets/image-20220803154826607.png)]
Redis数据保存在内存中,为避免关闭程序后数据的丢失,就需要将数据保存到磁盘文件上。
Redis持久化策略包含:快照(RDB),仅附加文件(AOF)
我们如何选择RDB和AOF呢?视业务场景而定:
允许少量数据丢失,性能要求高,选择RDB
只允许很少数据丢失,选择AOF
几乎不允许数据丢失,选择RDB + AOF
Redis中的数据太多可能导致内存溢出,Redis会根据情况淘汰一些数据。
Redis的内存上限:64位系统,上限就是内存上限;32位系统,最大是4G
配置最大内存:
max-memory 配置0就是无上限(默认)
配置淘汰策略
maxmemory-policy
值:
noevication (默认)不淘汰
allkeys-lru (推荐)使用LRU算法淘汰比较少使用的键
volatile-lru 在过期的键中淘汰较少使用的
allkeys-random 在所有键中随机淘汰
volatile-random 在过期键中随机淘汰
volatile-ttl 在过期键中淘汰存活时间短的键
LRU算法: Least Recently Used 最近最少使用算法,淘汰长期不用的缓存
编程式缓存使用复杂,代码侵入性高,推荐使用声明式缓存,通过注解来实现热点数据缓存。
1)在启动类上添加注解
//启动缓存
@EnableCaching
2)Redis的配置类
@Configuration
public class RedisConfig {
@Bean
public RedisCacheConfiguration provideRedisCacheConfiguration(){
//加载默认配置
RedisCacheConfiguration conf = RedisCacheConfiguration.defaultCacheConfig();
//返回Jackson序列化器
return conf.serializeValuesWith(
RedisSerializationContext.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer()));
}
}
3)缓存相关注解
注意:缓存的实体类必须实现序列化接口
堆:放new出来的一切对象
方法区:一切静态不变的数据(class信息、常量、静态成员变量)
程序计数器:存放线程执行的行号信息
虚拟机栈:存放局部变量
本地方法栈:调用本地方法时分配的局部变量的位置
垃圾收集器全称:Garbage Collection,下文简称GC,其实就是各种垃圾算法的一种实现。目前还没有符合所有场景的收集器出现。
GC分为四种:
1.串行垃圾回收器(Serial Garbage Collector)
2.并行垃圾回收器(Parallel Garbage Collector)
3.并发标记扫描垃圾回收器(CMS Garbage Collector)
4.G1垃圾回收器(G1 Garbage Collector)
GC 算法主要有四种:引用计数法、根搜索算法、复制算法、标记-清除算法
首先,我们编写好的Java代码,经过编译变成.class文件,然后类加载器把.class字节码文件加载到JVM中,接着执行我们的代码,最后将类卸载出JVM。而从类加载到虚拟机到卸载出虚拟机的这一整个生命周期总共可以分为7个步骤,分别为加载、验证、准备、解析、初始化、使用和卸载,其中验证、准备和解析又称为连接阶段。
分代回收器有两个分区:老生代和新生代,新生代默认的空间占比总空间的 1/3,老生代的默认占比是 2/3。
新生代使用的是复制算法,新生代里有 3 个分区:Eden、To Survivor、From Survivor,它们的默认占比是 8:1:1,它的执行流程如下:
1、把 Eden + From Survivor 存活的对象放入 To Survivor 区;
2、清空 Eden 和 From Survivor 分区;
3、From Survivor 和 To Survivor 分区交换,From Survivor 变 To Survivor,To Survivor 变 From Survivor。
每次在 From Survivor 到 To Survivor 移动时都存活的对象,年龄就 +1,当年龄到达 15(默认配置是 15)时,升级为老生代。大对象也会直接进入老生代。
老生代当空间占用到达某个值之后就会触发全局垃圾收回,一般使用标记整理的执行算法。以上这些循环往复就构成了整个分代垃圾回收的整体执行流程。
XX比X的稳定性更差,并且版本更新不会进行通知和说明。
-Xms
s为strating,表示堆内存起始大小
-Xmx
x为max,表示最大的堆内存
(一般来说-Xms和-Xmx的设置为相同大小,因为当heap自动扩容时,会发生内存抖动,影响程序的稳定性)
-Xmn
n为new,表示新生代大小
(-Xss:规定了每个线程虚拟机栈(堆栈)的大小)
-XX:SurvivorRator=8
表示堆内存中新生代、老年代和永久代的比为8: 1: 1
-XX:PretenureSizeThreshold=3145728
表示当创建(new)的对象大于3M的时候直接进入老年代
-XX:MaxTenuringThreshold=15
表示当对象的存活的年龄(minor gc一次加1)大于多少时,进入老年代
-XX:-DisableExplicirGC
表示是否(+表示是,-表示否)打开GC日志
【引用计数法】
系统为对象添加一个计数器,当有新的引用时加1,引用失效时减1。但此方法无法解决两个对象循环引用的问题。
【可达性分析法】
通过对象的引用链来判断该对象是否需要被回收。
通过一系列的GC Roots的对象作为起始点,从这些起节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是不可用的,就需要回收此对象。
Elasticsearch是一个分布式的RESTful风格的搜索和数据分析引擎,能够解决不断涌现出的各种用例。作为Elastic Stack的核心,它集中存储您的数据,帮助您发现意料之中以及意料之外的情况。
查询主要分为两种类型:精确匹配、全文检索匹配。
精确匹配,例如 term、exists、term set、 range、prefix、 ids、 wildcard、regexp、 fuzzy
等。
全文检索,例如match、match_phrase、multi_match、match_phrase_prefix、query_string 等
1、一般会把Elasticsearch单独部署,作为一个服务存在。你可以把他类比于MySQL数据库
2、新增数据的时候,插入到MySQL后,一般我们会监听MySQL 的binlog来发现数据存在变更,然后把变更的内容插入到Elasticsearch
3、搜索时有的时候可以直接返回es搜索的结果,有的时候要用Id再从MySQL查询相关缺失的数据。这是看场景的。学技术不能认为当前学的”新潮流技术“都能颠覆或完全覆盖掉其他的技术。需要有不同场景使用不同技术栈的思维
Mysql数据同步到ES中分为两种,分别是全量同步和增量同步
全量同步表示第一次建立好ES索引之后,将Mysql中所有数据一次性导入到ES中
增量同步表示Mysql中产生新的数据,这些新的数据包括三种情况,就是新插入Mysql中的数据,更新老的数据,删除的数据,这些数据的变动与新增都要同步到ES中
Elasticsearch使用了一种叫做倒排索引的结构,一种设计用来允许快速全文检索的。一个倒排索引包含出现在任何文档中的所有的唯一词的列表,以及对于每个词,他们在哪些文档中出现的列表。