JavaSE
Java基本数据类型大小
一个字节等于8位 1byte = 8bit。int :4个字节 32位 (-231~231-1)。口诀:1248,4812
- byte:1
- short:2
- int:4
- long:8
- float:4
- double:8
- boolean:1
- char:2
JAVA中&&和||两种符号
- &&可以用作逻辑与的运算符,表示逻辑与(and),当运算符两边的表达式的结果都为true时,整个运算结果才为true,否则,只要有一方为false,则结果为false。
- &&还具有短路的功能,即如果第一个表达式为false,则不再计算第二个表达式,例如,对于if(str != null && !str.equals(“”))表达式,当str为null时,后面的表达式不会执行,所以不会出现NullPointerException
- ||可以作逻辑或运算符,表示逻辑或(or),当运算符有一边为true时,整个运算结果为true
抽象类不能创建对象,那么抽象类中是否有构造器
- 抽象类中一定有构造器。构造器的作用:给子类初始化对象的时候要先super调用父类的构造器
抽象类是否可以被final修饰
- 不能被final修饰,因为抽象类设计的初衷就是给子类继承用的。要是被final修饰了这个抽象类了,就不存在继承了,就没有子类
- 抽象类可以被其他类继承:一个类继承一个抽象类,那么这个类可以变成抽象类,一般子类不会加abstract修饰,一般会让子类重写父类中的抽象方法,子类继承抽象类,就必须重写全部的抽象方法,子类如果没有重写父类全部的抽象方法,那么子类也可以变成一个抽象类
抽象方法可否被static修饰
- 不可以:抽象方法是让子类去重写完善功能的,而static修饰的方法不能被重写
HashCode()、equals()区别
都是Object类中的方法:
- 如果类中不重写此方法
- hashcode():属于是本地方法,返回的是对象的地址值
- equals():用来比较两个对象的地址值是否相等
- 如果重写此方法
- hashcode():放回的是根据对象的成员变量,计算出的一个整数
- equals():比较的是两个对象的成员信息是否相同
类中重写hashCode()、equals()比肩两个对象是否相等,如何提高效率
- 两个对象通过equals()比较是相等的,那么hashCode()肯定想等,两个对象通过hashCode()比较相等,但equals()不一定相等
- 先将两个对象进行hashCode()比较,hashCode不相等则equals一定不相等,如果相等再进行equals比较
Error和Exception的区别
- Error:通常是比较严重的错误,JVM无法解决的,常见的栈溢出StackOverflowError、内存溢出OutOfMemoryError
- Exception:一般错误,程序员可以手动处理,常见的空指针异常NullPointException,数组越界ArrayIndexOutOfBoundsException
Java对异常默认的处理方式
- Java默认处理方式,是将问题抛出给上一级,抛出之前,Java会根据错误产生的异常类,创建出该类的对象,底层通过thorw关键字讲异常抛出给上级,不断的向上抛出,直到抛给JVM,JVM拿到问题之后,讲错误原因和所在位置打印在控制台(method->main->jvm>控制台)
throw与throws
- throw:将异常对象抛给调用者
- throws:仅仅是声明作用,告诉调用者,此方法存在异常
- 问题可以自己解决的:try-catch,不会影响后面代码继续执行
- 问题自己解决不了的:throws抛出,通过throw关键字,将异常对象抛出给调用者,如果使用new throw抛出异常对象,则方法上必须进行throws声明
- 如果抛出的异常对象是RuntimeException,则方法上无需throws声明
String、StringBuffer、StringBuilder
可变性 | 线程安全 | |
---|---|---|
String | 不可变 | 安全 |
StringBuilder | 可变 | 不安全 |
StringBuffer | 可变 | 安全,内部使用syvchronized进行同步 |
Java集合
LinkedList与ArrayList区别
- LinkedList
- 基于双向链表,无需连续内存
- 随机访问慢,因为要沿着链表遍历
- 头尾插入删除性能高
- 占用内存多
- ArrayList
- 基于数组,需要连续内存
- 随机访问快,根据下标访问
- 尾部插入、删除性能可以,其他部分插入、删除都会移动数据,性能低
- 可以利用CPU缓存,局部性原理
HashMap和Hashtable的区别
- 都是一个键对应一个值,键不能重复,值可以
- HashMap是JDK1.2出现,Hashtable是JDK1.0出现
- HashMap线程不同步,Hashtable线程同步
- HashMap容量是2的n次幂,hash分散性不好,需要二次哈希补偿,Hashtable没有采用这一设计
- HashMap可以存储null键null值,Hashtable不能存储null键null值
JavaEE
请求转发与重定向区别
- 重定向是浏览器发送请求并收到响应以后再次向一个新地址发请求;请求转发是服务器收到请求后为了完成响应转到另一个资源
- 重定向中有两次请求对象,不共享数据;请求转发只产生一次请求对象且在组件间共享数据
- 重定向后地址栏改变;请求转发不变
- 重定向的新地址可以是任意地址;请求转发必须是同一个应用内的资源
获取servlet的转发和响应重定向的方式
转发的方法:
- 通过
HttpServletRequest
的getRequestDispatcher()方法获得 - 通过
ServletCOntext
的getRequestDispatcher()方法获得
重定向的方法:
HttpServletResponse
的sendRedirect()方法
XML与JSON区别
- JSON数据体积更小,传输速度快
- jJSON跟js交互更方便
- JSON数据可读性比xml低
cookie、session、token的区别
- cookie: 是存储在浏览器端的一小段文本数据(大小不超过4kb),cookie里的内容会在请求头里随着http请求一起发送到服务器端
- session是存储在服务器端的一组数据,用来存储用户会话的数据,有的网址是采用session机制来验证用户身份的,通常会把session ID存储在cookie中
- token通常用来代表一小段字符串,token可以存储在cookie里,也可以存储在服务器的内存里
有一种特殊的token :JSON WEB TOKEN(都叫它Jwt)
JWT
在网络应用中传递一些小批量的安全数据时使用,这个token的特点是紧凑并且安全,特别适用于分布式站点的单点登录
jwt结构主要分为三部分:header头部、payload负载、signature签名
主要应用场景:身份认证:
- 传统方法在服务器端存储一个session,给客户端返回一个cookie。把SessionID存储在cookie中,使用JWT就是当用户登陆系统之后,后台会返回一个jwt给用户(浏览器端)。用户只需要本地保存这个token
- web应用通常使用cookie或者是local storage来存储
- APP应用使用app自己的存储机制来存储,用户请求后台资源时,每次都要带上这个token,后台会对这个token进行验证
token在项目中的应用?token校验过程
token多用于单点登录:
- 客户端使用用户名密码请求登录
- 服务端收到请求,去验证用户名密码
- 验证成功后,服务端会签发一个token,再把这个token发送给客户端,客户端收到token以后把它存储起来,放在Cookie或者Local-Storage里,如果是单点登录应该存放到本地,比如Redis
- 客户端每次向服务器请求资源的时候需要带着服务端签发的token,服务端收到请求,对token进行解析验证
单点登录和跨域单点登录的区别?
有多个应用系统,如果在同一域名下这就是普通的单点登录,比如:username.greedy.com
和 password.greedy.com
。同一域名下它们会共享本地的token
如果不在同一域名下,这时就需要用跨域单点登录的解决方案,最有名的规范就是JWT
Servlet生命周期
一般情况,servlet是在被请求的时候才去创建的,它提供了生命周期的方法:实例化,初始化,服务和销毁。比如我们可以在初始化方法设置一些初始数据等。使用servlet时,一般都是继承httpServlet,然后分别实现doGet或者dePost方法,但是在这里面要注意的是,servlet并不是线程安全的,多线程单实例执行的,当并发访问同一个资源的话,就有可能引发线程安全问题,不要写全局变量
JUC
创建线程的几种方式
- 继承Thread类
- Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新的线程,并执行run()方法。这种方式实现多线程很简单,通过自己创建的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法
- 优点:代码简单
- 缺点:该类无法继承别的类
- 实现Runnable接口
- Java中的类属于单继承,如果自己的类已经extend另一个类,就无法直接extends Thread,但是一个类继承一个类的同时,是可以实现多个接口的
- 优点:继承其它类,统一实现该接口的实例可以共享资源
- 缺点:代码复杂
- 实现Callable接口
- 实现Runnable和实现Callable接口的方式基本相同,不过Callable接口中的call()方法有返回值,Runnable接口中的run()方法无返回值
- 线程池方式
- 线程池就是一个容纳多个线程的容器,其中的线程可以重复使用,省去了频繁的创建线程对象的操作,反复创建线程是非常消耗资源的
- 优点:实现自动化装配,易于管理,循环利用资源
Lock接口比synchronized块优势是什么
- 能够显示的获取锁
lock()
和释放锁unlock()
- 可以方便的实现公平锁
公平锁与非公平锁
- 公平锁:表示线程获取锁的顺序是按线程加锁的顺序来进行的分配的,先来先得,先进先出
- 非公平锁:一个获取锁的抢占机制,是随机拿到锁的,不一定先来的就能拿到锁,可能会导致一些线程一直拿不到锁
计算机网络
IP地址、MAC地址与DNS
IP地址: 就好比每个人的身份证一样,可以通过IP地址精准找到你的设备
有一种特殊的IP地址:内网IP地址。由于IP地址有限,IP地址不够用,于是每个局域网就对外共用同一个IP地址,内部的设备就用内网的IP地址,内网和外网采用路由器进行连接和转发消息,内网IP有三个保留的IP地址段:10,192,172
MAC地址: 也叫物理地址,是硬件厂家直接烧在网卡上的。理论上MAC地址是唯一的,但是MAC地址可以通过程序修改,可能会重复
IP地址与MAC地址在计算机里都是以二进制的方式进行表示的,IP地址是32位的,MAC地址是32位的
DNS: 由于IP地址是一堆数字不容易被记住,所以有了域名(域名就是相当于IP地址的一个别名),DNS就是把域名转换成IP地址的
从浏览器输入URL到页面展现的过程
第一步:专业说法:DNS 查询分级缓存策略(DNS Query)
找缓存顺序:浏览器 —> 操作系统 —> 本地服务商 —> 根服务器
- 先查询浏览器的本地缓存(通常在内存中)
- 本地没找到,查找操作系统的 host 文件,该文件在 Linux 中在 /etc/hosts 里
- 上述还没找到,DNS会查询本地服务提供商(ISP)
- ISP没找到,请求指向ROOT根服务器,返回顶级域名服务器地址
- 浏览器发送请求给顶级域名服务器,返回权威域名服务器地址
- 浏览器发送 Lookup 请求给权威域名服务器,找到具体DNS记录,返回给浏览器
第二步:向IP地址发起连接请求,进行TCP三次握手四次挥手
三次握手
四次挥手
第三步:页面展现
首先解析HTML文件创建DOM树,之后解析CSS形成CSS对象模型,将CSS模型跟DOM树合并成渲染树
什么是CDN
CDN中文名称:内容分发网络,用来做内容分发的一套网络体系,用来提升文件下载速度的一种机制,让用户在离自己最近的CDN服务器进行下载,减少路由次数,提升用户体验。(有点像在京东购物,会在距离你下单地址最近的仓库给你发货的意思)
TCP、UDP、HTTP协议
- TCP协议提供安全可靠的网络传输服务,它是一种面向连接的服务,类似于打电话,必须先拨号,双方建立一个传输信息的通道进行传输
- UDP协议是一种数据报协议,它传输的数据书分组报文,它是无连接的,不需要和目标通信才建立连接,UDP类似于写信,所以UDP传输不保证安全可靠。但适合大数据量的传输
- HTTP协议是超文本传输协议,是一种相对于TCP来说更细致的协议,TCP和UDP协议规范的是网络设备之间的通信规范,HTTP是在TCP协议的基础上针对用户服务的协议,用户服务具体体现在应用程序之间的交互,比如我们的JavaWeb中的客户端与服务端体系就要用HTTP协议来规范通信
TCP/IP四层模型
- 应用层:为应用程序提供交互服务。在互联网中的应用层协议很多,如域名系统DNS、HTTP协
议、SMTP协议等。 - 传输层:负责向两台主机进程之间的通信提供数据传输服务。传输层的协议主要有传输控制协议
TCP和用户数据协议UDP。 - 网络层:选择合适的路由和交换结点,确保数据及时传送。主要包括IP协议。
- 数据链路层:在两个相邻节点之间传送数据时,数据链路层将网络层交下来的 IP 数据报组装成
帧,在两个相邻节点间的链路上传送帧。
HTTP状态码
类别 | 原因 |
---|---|
1XX | 服务器收到请求,需要客户端继续发送请求 |
2XX | 请求正常处理完毕 |
3XX | 重定向,需要进一步操作 |
4XX | 请求无效,客服端错误,服务端无法处理请求 |
5XX | 服务器后端出错 |
详细:
- 100:告诉客户端继续发送请求
- 200:表示请求响应成功
- 201:请求成功,服务器创建了新资源
- 202:请求已经被处理还未做出响应
- 301:请求的网页被永久移动到了新位置
客户端收到301请求后,通常用户会向新地址发起GET请求
- 308:永久重定向
客户端收到308请求后,延用旧的method(POST/GET/PUT)到新地址
- 400:请求无效,常见的情况是请求参数有误,HTTP头构建错误
- 403:禁止访问
- 404:访问不到资源
- 500:服务器后端错误
- 505: 服务器不支持请求中所使用的 HTTP 协议版本
POST和GET的区别
- GET请求参数通过URL传递,POST的参数放在请求体中
- GET产生一个TCP数据包;POST产生两个TCP数据包。对于GET方式的请求,浏览器会把请求头和请求体一并发送出去;而对于POST,浏览器先发送请求头,服务器响应100 continue,浏览器再发送请求体
- GET请求会被浏览器主动缓存,而POST不会,除非手动设置
- GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留
- GET请求参数不安全,提交数据量小,POST提交数据比较安全提交的,数据量无限制
HTTP 与 HTTPS 的区别
区别 | HTTP | HTTPS |
---|---|---|
协议 | 运行在 TCP 之上,明文传输,客户端与服务器端都无法验证对方的身份 | 身披 SSL( Secure Socket Layer )外壳的 HTTP,运行于 SSL 上,SSL 运行于 TCP 之 上, 是添加了加密和认证机制的 HTTP。 |
端口 | 80 | 443 |
资源消耗 | 较少 | 由于加解密处理,会消耗更多的CPU和内存资源 |
开销 | 无需证书 | 需要证书,而证书一般需要向认证机构购买 |
加密机制 | 无 | 共享密钥加密和公开密钥加密并用的混合加密机制 |
安全性 | 弱 | 由于加密机制,安全性强 |
数据库
索引失效
- 模糊查询:使用like关键字的时候,以%号开头索引就会失效
- 数据类型错误,索引也会失效
- 对索引的字段使用内部函数,索引也会失效。这种情况应该建立基于函数的索引
- null,索引不存储空值,如果不限制索引列not null,数据库会认为索引列有可能存在空值,所以不会按照索引值进行计算
- 对索引列进行加减乘除运算,会导致索引失效
- 最左原则,在复合索引中,索引列的顺序非常重要,如果不是按照索引列最左列,开始进行查找,则无法使用索引
- 如果数据库预计使用全表扫描比使用索引更快,数据库不会使用索引
数据隔离级别
- 脏读:一个事务读到另一个事务还没有提交的数据
- 不可重复读:是指在对于数据库中的某行记录,一个事务范围内多次查询却返回了不同的数据值,这
是由于在查询期间,另一个事务update数据并提交了。 - 幻读:一个事务读到了另一个事务已经提交了insert的数据,导致在当前事务中多次查询的结果不一致
MySQL数据库为我们提供的四种隔离级别:(级别从低到高,级别越高越安全,效率越低)
- Read committed (读已提交):一个事务只能看见已经提交事务所做的改变。可避免脏读的发生。
- Read uncommitted (读未提交):Oracle默认事务隔离级别所有事务都可以看到其他未提交事务的执行结果。
- Repeatable read (可重复读):MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行,解决了不可重复读的问题。
- Serializable (串行化):通过强制事务排序(当前事务没结束,其它事务就得等着),使之不可能相互冲突,从而解决幻读问题。
JVM
类加载器
- JDK自带三个类加载器:启动类加载器、扩展类加载器、应用类加载器
- 假设有这样一段代码:String s = “abc”;
- 代码开始执行前,会将所有需要的类全部加载到JVM中,通过类加载器加载,看到上述代码类加载器会找String.class文件,找到就加载。
- 首先通过"启动类加载器"加载:启动类加载器专门加载:“D:\JAVA\JDK\jdk1.8.0_251\jre\lib\rt.jar”,rt.jar中都是JDK最核心的类库
- 如果启动类加载器加载不到的时候,会通过"扩展类加载器"加载:扩展类加载器专门加载:“D:\JAVA\JDK\jdk1.8.0_251\jre\lib\ext*.jar”
- 如果扩展类加载器加载不到的时候,会通过"应用类加载器"加载:应用类加载器专门加载:classpath中的jar包(class文件)
类的实例化顺序
- 父类中的静态代码块,当前类的静态代码块
- 父类的普通代码块
- 父类的构造函数
- 当前类普通代码块
- 当前类的构造函数
类加载机制
- 加载:将class文件读入内存,并为之创建java.lang.Class对象
- 验证:检验被加载的类是否有正确的内部结构,并和其他类协调一致
- 准备:负责为类的类变量(static)分配内存,并设置默认初始化值
- 解析:将常量池的符号引用解析为直接引用
- 初始化:静态代码块、static 修饰的变量赋值、static final 修饰的引用类型变量赋值(static final 修饰的基本类型变量赋值,在链接阶段就已完成),初始化是懒惰执行
Java中内存分配
- 栈内存:存储的是局部变量,局部变量指的是方法中的变量
- 堆内存:new关键字出来的对象,有程序员手动释放,程序员不释放,最终会由OS回收
- 静态区:全局变量和静态变量的存储是放在一起的,初始化的全局变量和静态变量在一块区域
- 常量池:字符串存储的区域v
常量池与运行时常量池
- 常量池:就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量
等信息 - 运行时常量池:常量池是 *.class 文件中的,当该类被加载,它的常量池信息就会放入运行时常量
池,并把里面的符号地址变为真实地址
SpringBoot
常用注解
- @ConditionalOnClass,classpath 下存在某个 class 时,条件才成立
- @ConditionalOnMissingBean,beanFactory 内不存在某个 bean 时,条件才成立
- @ConditionalOnProperty,配置文件中存在某个 property(键、值)时,条件才成立
- @ConfigurationProperties,会将当前 bean 的属性与配置文件中的键值进行绑定
SpringBoot自动配置原理
@SpringBootApplication
是组合注解,由@ConponentScan、@EableAutoConfiguration、@SpringBootConfiguration和其它注解组成
- @SpringBootConfiguration与其它@Configuration注解功能一样,只不过@SpringBootConfiguration在全局唯一
- @ConponentScan中的excluderFilter用来在进行组件扫描的时候进行排除,也会排除自动配置类
- @EnableAutoConfiguration也是一个组合注解
- @AutoConfigurationPackage-用来记住扫描的起始包(就是以该包为起点扫描其子孙包)
- @Import(AutoConfigurationImportSelector.class)用来加载
META-INF/spring.fanctories
中的自动配置类
AutoConfigurationImportSelector.class
会通过SpringFactoriesLoader.class
扫描"META-INF/spring.factories"
为什么不用@Import直接引入自动配置类
- 让主配置类(程序员写的类)和自动配置类变成了强耦合,主配置类不应该知道有哪些自动配置类
- 直接用
@Import(自动配置类.class)
,引入的配置解析优先级高,自动配置类的解析应该在主配置类(程序员写的类)没提供时作为默认配置
AutoConfigurationImportSelector.class实现了DeferredImportSelector接口,让自动配置类解析晚于主配置类
Spring
常用注解
- @Order:控制bean的执行顺序,数字越小优先级越高
- @Configuration
- 配置类相当于一个工厂,标注@Bean注解的方法相当于工厂方法
- @Bean不支持方法重载,如果有多个重载方法,仅有一个能入选为工厂方法,重载的工厂方法参数越多的优先级越高
- @Configuration默认会为标注的类生成代理,其目的是保证@Bean方法相互调用的时候,仍能保证其单例
- @Configuration中如果有BeanFanctory后处理器,则实例工厂方法会导致MyConfig提前创建,造成其依赖注入失败,解决方法是改用静态工厂方法或直接为 @Bean 的方法参数依赖注入, 针对 Mapper 扫描可以改用注解方式
- @Import
- 四种用法
- 引入单个bean
- 引入一个配置类
- 通过Selector引入多个类
- 通过beanDefinition注册器
- 解析规则
- 同一配置类中,@Import优先级大于@Bean,就是@Import的类先解析再解析@Bean的类
- 同名定义的,默认后面的解析会覆盖掉前面的解析
- 不允许覆盖的情况下, 如何能够让主配置类(程序员自己写的配置类)的配置优先?
- 采用DeferredImportSelector,使主配置类实现DeferredImportSelector,因为它最后工作,可以理解为先解析@Bean,再@Import
- @Lazy
- 加在类上,表示此类延迟实例化、初始化
- 加在方法参数上,此参数会以代理方式注入
- 四种用法
SpringMVC
常用注解
- @RequestMapping,可以派生多个注解如 @GetMapping 等
- @RequestBody:处理请求体中的JSON数据,把JSON数据转换为Java对象
- @ResponseBody:把Java对象转换为JSON数据,写入到响应体,组合 @Controller => @RestController
- @ResponseStatus:控制响应状态码
- @ControllerAdvice,一般用于统一处理异常。组合 @ResponseBody => @RestControllerAdvice
- @PathVariable:映射 URL 绑定的占位符
Mybatis
Mybatis中动态SQL语句是什么意思?
动态SQL:
- SQL语句主体结构,在编译时无法确定,只有等到程序运行起来,在执行过程中才能确定
- Mybatis中用于实现动态SQL的主要元素有:if、where、foreach
静态SQL:
- 在应用程序运行前进行编译的,编译结果会存储在数据库内部,当程序运行时,数据库将直接执行编译好的SQL语句,降低开销
值得注意一点:
如select * from user where age > ?
这种语句是静态SQL,虽然个别参数的值不知道,但整个SQL的结构已经确定,数据库是可以将它编译的。动态SQL最明显的例子就是多条件查询,其中查询的条件每次并不确定有哪些
mybatis中的#和$的区别
- #将传入的数据都当成一个字符串,会对自动传入的数据加一个双引号。如:where username=#{username},如果传入的值是111,那么解析成sql时的值为where username=“111”, 如果传入的值是id,则解析成的sql为where username=“id”.
- 将传入的数据直接显示生成在 s q l 中。如: w h e r e u s e r n a m e = 将传入的数据直接显示生成在sql中。如:where username= 将传入的数据直接显示生成在sql中。如:whereusername={username},如果传入的值是111,那么解析成sql时的值为where username=111;如果传入的值是;drop table user;,则解析成的sql为:select id, username, password, role from user where username=;drop table user;
- #方式能够很大程度防止sql注入,$方式无法防止Sql注入。
- $方式一般用于传入数据库对象,例如传入表名.
- 一般能用#的就别用 ,若不得不使用“ ,若不得不使用“ ,若不得不使用“{xxx}”这样的参数,要手工地做好过滤工作,来防止sql注入攻击。
- 在MyBatis中,“ x x x ”这样格式的参数会直接参与 S Q L 编译,从而不能避免注入攻击。但涉及到动态表名和列名时,只能使用“ {xxx}”这样格式的参数会直接参与SQL编译,从而不能避免注入攻击。但涉及到动态表名和列名时,只能使用“ xxx”这样格式的参数会直接参与SQL编译,从而不能避免注入攻击。但涉及到动态表名和列名时,只能使用“{xxx}”这样的参数格式。所以,这样的参数需要我们在代码中手工进行处理来防止注入。
- 【结论】在编写MyBatis的映射语句时,尽量采用“#{xxx}”这样的格式。若不得不使用“${xxx}”这样的参数,要手工地做好过滤工作,来防止SQL注入攻击。
Redis
Redis为什么这么快
- 完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于 HashMap,HashMap 的优势就是查找和操作的时间复杂度都是 O(1);
- 数据结构简单,对数据操作也简单,Redis 中的数据结构是专门进行设计的;
- 采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
- 使用多路 I/O 复用模型,非阻塞 IO;
- 使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis 直接自己构建了 VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;
缓存与数据库同步怎么做的
缓存同步其实就是当缓存信息发生了变化,也就是后台对数据进行了增
删
改
操作后,数据库中发生了变化的数据删除相应的缓存即可。当页面再次请求数据时,缓存不能命中,会从数据库中查询并且添加到缓存中,即实现了缓存同步
缓存穿透
缓存穿透是查不到,与缓存击穿有明显的区别。缓存击穿是查到的太多了!
redis缓存中没有该条查询结果,会去数据库中查询,查询一次没有就查询两次,如果这个时候秒杀系统(也可能是有人采用洪水攻击)大量给数据库发送请求,数据库可能会招架不住导致崩溃 ┗( T﹏T )┛
布隆过滤器解决缓存穿透
布隆过滤器是一种数据结构,对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃,从而避免了对底层存储层的查询压力。相比于传统的 List、Set、Map 等数据结构,它更高效、占用空间更少,但是缺点是其返回的结果是概率性的,而不是确切的
缓存空对象
当存储层不命中后,即使返回的空对象也能将其缓存起来,同时会设置一个过期时间,之后再访问这个数据将会从缓存中获取,保护了后端数据源。如果空值能够被缓存起来,这就意味着缓存需要更多的空间存储更多的空键
即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间的窗口不一致,这对于需要保持一致性的业务造成影响
缓存击穿
简述:
缓存击穿与缓存穿透不一样,缓存击穿是一个key非常热点,一直在扛着高并发。俗话说就是:对着一个点猛攻!
当这个Key在过期的那一瞬间,由于缓存过期会同时访问数据库来查询最新数据,并回写缓存,这会导致数据库瞬间压力过大
解决方案:
- 设置热点数据永不过期(不太可能实现,因为存着存着的它会满啊,满了自己删除一些Key)
- 加锁,使用分布式锁,保证对于每个Key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可,把压力给了分布式锁
缓存雪崩
简述:
是指在某一时间段内,缓存集体过期失效,或者是Redis宕机,就是上图中缓存层没有啦
但最致命的是缓存服务的某个节点宕机,对数据库造成的压力不可预知,很有可能瞬间就把数据库搞垮
解决方案:
- Redis高可用:多加几台Redis
- 限流降级:在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量
- 数据预热:就是提前把可能会被大量访问的Key们,加载到缓存中,给它们设置不同的过期时间
哨兵模式
简述:
哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例
哨兵两个作用:
- 发送命令,让每个被监控的Redis服务器返回其运行状态
- 当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机
一个哨兵监控的时候可能会有偷懒的时候 ,这个时候就需要多个哨兵,而且让哨兵之间互相监督不许偷懒
文字叙述一下故障转移过程:
假设哨兵A发现主机挂掉啦,不会马上进行故障转移,因为这仅仅是它自己主观认为主机挂掉了,这个现象网上称为主观下线。等到发现主机挂掉到达一定数量的其他哨兵,它们会集合起来开会,最后决定由一个哨兵去进行故障转移。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从机切换为主机,这个过程称为客观下线。这样对于外部而言,一切都是透明的
DRB与AOF
RDB可以看作为某一时刻Redis的快照,比较适合灾难恢复,可以看作快照
优点:主进程不进行任何IO操作,确保了极高的性能。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是很敏感,那么RDB方式要比AOF方式更加高效
缺点:最后一次持久化的时候如果宕机,数据可能丢失。fork进程的时候,会占用一定的内存空间
RDB触发机制
- save的规则满足的情况下,会自动触发RDB规则
- 执行flushall命令,也会触发RDB规则
- 退出Redis,也会产生RDB文件
恢复RDB文件
只需要将RDB文件放在redis启动目录,redis启动的时候会自动检查dump.rdb恢复其中的数据
AOF是将所有命令都记录下来,恢复的时候把文件全部再执行一遍
AOF默认是关闭的,需要在配置文件中开启AOF。Redis支持AOF和RDB同时生效,如果同时存在,AOF优先级高于RDB(Redis重新启动时会使用AOF进行数据恢复)
监听执行的命令,如果发现执行了修改数据的操作,同时直接同步到数据库文件中
优点:相对RDB数据更加安全
缺点:相同数据集AOF要大于RDB,相对RDB可能会慢一些
机制
- save的规则满足的情况下,会自动触发RDB规则
- 执行flushall命令,也会触发RDB规则
- 退出Redis,也会产生RDB文件
恢复RDB文件
只需要将RDB文件放在redis启动目录,redis启动的时候会自动检查dump.rdb恢复其中的数据
AOF是将所有命令都记录下来,恢复的时候把文件全部再执行一遍
AOF默认是关闭的,需要在配置文件中开启AOF。Redis支持AOF和RDB同时生效,如果同时存在,AOF优先级高于RDB(Redis重新启动时会使用AOF进行数据恢复)
监听执行的命令,如果发现执行了修改数据的操作,同时直接同步到数据库文件中
优点:相对RDB数据更加安全
缺点:相同数据集AOF要大于RDB,相对RDB可能会慢一些
到此这篇Java初级面试常见面试题_Java初级面试常见面试题的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!版权声明:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权、违法违规、事实不符,请将相关资料发送至xkadmin@xkablog.com进行投诉反馈,一经查实,立即处理!
转载请注明出处,原文链接:https://www.xkablog.com/javal-cj/6700.html