notes 2023

文章目录

  • Java
    • jdk jre jvm三者之间的关系
    • 面向对象(oop)
    • 对象创建的过程
    • java的反射机制
    • Object类常用方法
    • public private protected default
    • String StringBuffer与Stringbuilder区别
    • sleep() 和 yield()区别
    • sleep()和wait()
    • 抽象类和接口有什么区别?(重点)
    • ==和equals
    • 方法的重写与重载有什么区别?
    • Java当中的多态操什么?
    • 框架是如何反射创建对象的呢?
    • 数组和链表有什么区别?(重点)
    • java当中的集合类或集合框架(重点)
    • 进程和线程有什么区别?
    • java当中如何实现线程呢?(服点)
    • 多线程并发或线程安全问题如何解决?(重点)
    • synchronized(内置锁)和 ReentrantLock(可重入锁)
    • AQS是啥原理,CAS操作是什么
    • ThreadLocal底层是怎么实现的?
    • 线程之间如何进行通信?
    • 悲观锁和乐观锁
    • 公平锁和非公平锁
    • 线程的使用场景:
    • 线程池
    • i++线程安全么
    • 数据存储的形式IO(input输入 output输出)
    • java当中IO流的分类
    • BIO,NIO,AIO有什么区别:
    • 手写一个单例 单例有三要素:(重点)
  • 数据库
    • 你是怎么设计数据库的?(重点)
    • 关系型缴据库
    • 数据库索引是什么?
    • 聚簇索引和非聚簇索引
    • Mysql支持以下约束
    • B树
    • B+树
    • B树和B+树的区别
    • 数据库调优(重点)
    • sql怎么调优,索引命中怎么知晓?(重点)
    • sql调优技巧
    • mysql 如何实现悲观锁和乐观锁
    • 索引的常用数据结构
    • 索引有什么优缺点?
    • indoob和myisam区别
    • 数据库的事务隔离级别
    • 脏、幻、不可重复读
  • JVM
    • jvm内存结构
    • 什么时候对象可以进行垃圾收集?
    • 垃圾收集算法?
    • 对象的引用类型
    • jvm的调优
    • 通过监控发现,cpu和内存使用率居高不下,该怎么排查问题
    • jdk1.8之后的新特性(重点)
    • Mysql主从如何切换的?
    • tomcat的工作原理
    • tomat调优
  • WEB
    • session和cookie的区别
    • 如果客户端禁止cookie能实现session还能用吗?
    • tcp和udp有什么区别?(重点)
    • http协议和https协议区别
    • 请详细描述下一次http请求到最后响应这之间的过程?浏览器输入一个网址按下回车之后的完整过程?(重点】
    • Java-web的核心
    • servlet的生命周期:
    • 协议有哪几种请求方式?
    • get和post请求有什么区别?
    • 请求的转发和重定向有什么区别
    • 同步与异步的区别
  • Spring
    • Spring特性 spring理解
    • jdk的动态代理和cglib动态代理有什么区别?
    • spring中创建对象的三种方式
    • spring中创建的对象的作用域
    • 你看过spring的源码么?说说IOC的源码?
    • spring当中的单例,是如何保障它线程安全的?
    • springmvc的原理,工作流程,源码
    • springmvc的常用注解有哪些?(重点)
    • springmvc的拦截器怎么写?
    • spring和springBoot
    • springboot自动装配本质
    • bean的作用域
    • spring单例模式如何保证线程安全
  • Mybatis
    • 在Mybatis中 Dao接口和XML文件的SQL如何建立关联?
    • Mybatis的四种分页方式
    • mybatis嵌套查询和嵌套结果
    • mybatis中#{}和${}区别
    • mybatis 的二级缓存
    • 如何使用Mybatis的二级领存,
    • mybatis 如何主键回填
  • redis
    • redis支持的键值数据类型有五种:
    • redis三种特殊数据类型
    • redis的哨兵机制
    • redis cluster集群原理
    • 你们在项目当中哪些地方用到redis?(重点)
    • 缓存的穿透、击穿和雪崩,你们是如何解决
    • redis是单线程执行的,为什么性能这么高?
    • redis缓存机制
    • Redis的应用场景
    • 持久化
    • RDB持久化
    • AOF持久化
  • RabbitMQ
    • rabbitMQ的消息发布模式
    • RabbitMQ如何保证消息不会丢失?
    • Rabbit保证消息幂等性的办法
    • RabbitMQ使用场景
    • 什么是Nginx?
    • 说说linux系统的常用shell命令
    • 权限管理Shiro

Java

jdk jre jvm三者之间的关系

java开发者工具包 java运行环境 java虚拟机
jdk(包含)>>jre>>(包含)ivm

面向对象(oop)

面向对象是对面向过程的封装,封装了类的属性和方法

对象创建的过程

对象创建之前,首先类被加载(先加载父类再加载子类),执行静态代码块,执行构造器((先构造父类再构造子类)),创建对象实例静态方法和类有关,直接使用类名,方法来进行调用

java的反射机制

当类加载器将类加载进jvm之后,jvm会创建每一个类的元数据对象(Class),java语言允许通过元数据对象动态的创建对象实例,这种机制就称为java的反射机,基本上所有框架的底层都用到了反射机制,spring、maybatis、servlet 都用到了。

Object类常用方法

1.getClass方法
获取运行时类型,返回值为Class对象
2.hashCode方法
返回该对象的哈希码值,是为了提高哈希表的性能(HashTable)
3.equals方法
判断两个对象是否相等,在Object源码中equals就是使用去判断,所以在Object中equals是等价于的,但是在String及某些类对equals进行了重写,实现不同的比较。
4.clone方法
主要是JAVA里除了8种基本类型传参数是值传递,其他的类对象传参数都是引用传递,我们有时候不希望在方法里将参数改变,这时就需要在类中复写clone方法。
如果在clone方法中调用super.clone()方法需要实现Cloneable接口,否则会抛出CloneNotSupportedException。
此方法只实现了一个浅层拷贝,对于基本类型字段成功拷贝,但是如果是嵌套对象,只做了赋值,也就是只把地址拷贝了,所以没有成功拷贝,需要自己重写clone方法进行深度拷贝。
5.toString方法
返回一个String字符串,用于描述当前对象的信息,可以重写返回对自己有用的信息,默认返回的是当前对象的类名+hashCode的16进制数字。
6.wait方法
多线程时用到的方法,作用是让当前线程进入等待状态,同时也会让当前线程释放它所持有的锁。直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,当前线程被唤醒
7.notify方法
多线程时用到的方法,唤醒该对象等待的某个线程
8.notifyAll方法
多线程时用到的方法,唤醒该对象等待的所有线程
9.finalize
​对象在被GC释放之前一定会调用finalize方法,对象被释放前最后的挣扎,因为无法确定该方法什么时候被调用,很少使用。

public private protected default

public:可以被所有其他类所访问。
private:只能被自己类访问和修改。
protected:自身,子类及同一个包中类可以访问。
default:同一包中的类可以访问,声明时没有加修饰符。

String StringBuffer与Stringbuilder区别

String 字符串常量
StringBuffer 字符串变量(线程安全)
StringBuilder 字符串变量(非线程安全)
1.只是StringBuffer 中的方法大都采用了 synchronized 关键字进行修饰,因此是线程安全的,而 StringBuilder 没有这个修饰,可以被认为是线程不安全的。
2.在单线程程序下,而StringBuffer则每次都需要判断锁,效率相对更低StringBuilder效率更快,因为它不需要加锁,不具备多线程安全

sleep() 和 yield()区别

1.sleep()方法给其他线程运行机会时,不考虑线程的优先级,因此会给低优先级的线程运行机会;yield()方法只会给相同优先级或更高优先级的线程运行机会
2.线程执行sleep()方法后转入阻塞(blocked)状态;执行yield()方法后装入就绪(ready)状态
3.sleep()方法声明抛出InterruptedException;而yield()方法没有声明任何异常

sleep()和wait()

1、sleep是线程中的方法,但是wait是Object中的方法。
2、sleep方法不会释放lock,但是wait会释放,而且会加入到等待队列中。
3、sleep方法不依赖于同步器synchronized,但是wait需要依赖synchronized关键字。
4、sleep不需要被唤醒(休眠之后推出阻塞),但是wait需要(不指定时间需要被别人中断)。

抽象类和接口有什么区别?(重点)

相同点
都不能被实例化 ,接口的实现类或抽象类的子类都只有实现了接口或抽象类中的方法后才能实例化。

不同点
抽象类中的抽象方法的修饰符只能为public或者protected,默认为public接口中的方法默认使用public修饰
接口成员变量默认为public static final,必须赋初值,不能被修改。抽象类中成员变量默认default,可在子类中被重新定义,也可被重新赋值;
实现接口的关键字为implements,继承抽象类的关键字为extends。一个类可以实现多个接口,但一个类只能继承一个抽象类。所以,使用接口可以间接地实现多重继承。
接口强调特定功能的实现,而抽象类强调所属关系。
抽象类可以包含方法、构造方法,方法可以实现,但是构造方法不能用于实例化,主要用途是被子类调用。接口只有定义,不能有方法的实现,java 1.8中可以定义default方法体抽象类可以有构造方法,接口中不能有构造方法。
抽象类中可以有普通成员变量,接口中没有普通成员变量
抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的。
抽象类中可以包含静态方法,接口中不能包含静态方法
抽象类中的抽象方法的访问类型可以是public,protected,但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型。

==和equals

一般比较的是值是否相等
如果作用于基本数据类型的变量,则直接比较其存储的 值是否相等;
如果作用于引用类型的变量,则比较的是所指向的对象的地址
equals一般为比较内容是否相同
如果没有对equals方法进行重写(相当于“
”),则比较的是引用类型的变量所指向的对象的地址;
诸如String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容。

方法的重写与重载有什么区别?

重写是指子类继承父类,重写父类的方法:
重载是和在一个类中,可以有多个重名方法,方法名相同,参数类型不同。

Java当中的多态操什么?

多态是指可以创建父类对象的引用指向子类的对象,方法的调用只能调用父类的方法

框架是如何反射创建对象的呢?

框架的配置有xml形式,注解形式,xml可以被解析,解析的技术有dom4j。sax解析。三大框架使用的解析方式是也。

数组和链表有什么区别?(重点)

数组会在内存当中开辟一块连续的存储空间,需要指定长度。数组当中的每一个元素都有索引,查询的话,通过索引就可以直接定位到某一个元素,效率很高,但是进行删除的话,数组会进行移动,所以效率很低。
链表不需要连续的存储单元,链表中的上一个元素通过指针指向下一个元素。所以链表结构进行查询的话,头部尾部比较快,中间元素查找速度比较慢,但是删除的话,只需要删除前后指针,重新建立指针就可以了,所以删除的性能很高。

java当中的集合类或集合框架(重点)

1、List列表:有序的,可重复的;
2、Queue队列:有序,可重复的;
3、Set集合:不可重复;
4、Map映射:无序,键唯一,值不唯一。
collection接口是集合类的顶层接口,collections是工具类,collection有两个子接口,一个list接口,一个是set接口:
list接口有序可重复,set接口无序不可重复。
– list接口常用的实现类有:
arrayList 基于数组实现的
linkedList 是基于双向循环链表实现的
vector 基于数组实现的,但是是线程安全的 (synchrinized)
copyOnwriteArrayList 复制数组副本来实现的
– set 接口常用实现类有:
HashSet 基于hashMap来实现的,实现不可重复是通过hashcode方法和equals方法进行两次比较,先比较hashcode,再通过equals进行比较
Treeset 基于二叉树来实现的,可以对元素进行排序(排序规则默认是自然顺序,可以自定义比较器,实现自定义排序)
– map(key value结构的)接口的实现类有:
HashMap key不可重复,无序,可以为null(重点)
实现原理:基于数组和链表来实现的,当存入一组键值对的时候,先对key进行hash,然后映射到一个初始化长度为16的数组上,当不同的key产生hash碰撞的时候,value会通过链表结构来进行存储,jdk1.8之后对hashMap进行了改进,当链表长度达到临界值8,会通过红黑树来存储value,hashmap有两个参数,一个是初始化数组长度16,负载因子0.75,当满足扩容阙值的时候(当数组的12个元素被存满,并且有hash碳撞了),动态扩容,以2倍的增长方式进行扩容。
HashTable是线程安全的hashMap(synchronized机制)key不准许为null。
TreeMap 基于二叉树来实现的,可对key进行自然排序(自定义比较器,写比较规则)
ConcurrentHashMap是线程安全的,对整个hash桶采用分段策略,拆分成若干个段Segment,对每一个段上锁(ynchronized),极大的提高了并发修改的效率

## hashmap四种遍历方式
1.通过keyset遍历
2.通过entryset的Iterator
3.通过entryset遍历,大量数据时效率高
4.遍历所有value,不能遍历key

进程和线程有什么区别?

进程是操作系统资源分配的基本单位,线程是操作系统调度的基本单位。
进程是指应用程序在运行时的状态,进程会占用系统的cpu以及内存资源;
线程是指进程当中的执行流程。多个线程协间工作,会共同使用进程的资源才确保进程的功能得以卖现。
一个进程至少有一个线程,线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。
每个进程都有独立的地址空间,进程之间的切换会有较大的开销;线程可以看做轻量级的进程,同一个进程内的线程共享进程的地址空间
一个进程崩溃后,在保护模式下其他进程不会被影响,但是一个线程崩溃可能导致整个进程被操作系统杀掉,所以多进程要比多线程健壮

java当中如何实现线程呢?(服点)

1.继承Thread类
2.实现runnable接口,只是任务类,还需要手动创建线程对象
3.线程池机制来创建和管理线程ThreadPool
singleThreadPool 创建一个线程的线程池
fixedThreadPool 创建固定数量的线程池
cachedThreadPool 数量可缓存的线程池
ScheduledThreadPool 有定时功能的线程池

##线程的生命周期
new一个线程的时候处于新建状态:
调用线程,start方法的时候,等待cpu分配时间片进入可运行状态:当cpu分配到了时间片之后,线程开始运行,运行run方法当中的内容进入运行状态:
当线程遇到sleep、wait等方法的时候,线程会进入阻塞状态
当休眠时间到期,或者被notify了,线程又回到了可运行状态
线程运行结束之后,处于死亡状态,就会销毁,被jvm的gc回收。

多线程并发或线程安全问题如何解决?(重点)

1:通过volatile关键字修饰变量,可以实现线程之间的可见性、避免变量脏读的出现,底层是通过限制jvm指令的重排序来实现的,适用于一个线程修改,多个线程读的场景
2:通过synchronized锁(任意对象)来实现线程同步,自动锁的思想,底层实现原理:当有线程进入同步代码块之后,利用jvm的计数器将锁的标记置为1,当别的线程再想进入的时候,发现锁的标记为1,该线程就去锁池等待,当第一个线程出来之后,锁的标记会量为0,之后cpu随机分配一个线程再次进入同步代码块
3.通过lock锁的机制,进行手动lock,和unlock,但是这种很容易出现死锁。注意加锁以及解锁的顺序,就可以避免死锁
4.通过线程安全的集合类,可以解决并发问题,如
ConcurrentHashMap CopyonWriteArraylList
5:使用并发包(java.util.concurrent)下面的原子类,底层使用的是cas机制(乐观锁),可以解决并发问题如atomiclnteger 线程安全的原子整型类
6:使用线程池来创建和管理线程,也可以一定程度上解决并发问题
7:使用Threadlocal米修饰变量,可以解决并发问题

synchronized(内置锁)和 ReentrantLock(可重入锁)

synchronized 可用来修饰普通方法、静态方法和代码块,而 ReentrantLock 只能用在代码块上。
synchronized 会自动加锁和释放锁,而 ReentrantLock 需要手动加锁和释放锁
synchronized 属于非公平锁,而 ReentrantLock 既可以是公平锁也可以是非公平锁
synchronized 不能响应中断,也就是如果发生了死锁,使用 synchronized 会一直等待下去,而使用 ReentrantLock 可以响应中断并释放锁,从而解决死锁的问题
synchronized 是 JVM 层面通过监视器(Monitor)实现的,而 ReentrantLock 是通过 AQS(AbstractQueuedSynchronizer)程序级别的 API 实现

AQS是啥原理,CAS操作是什么

AQS核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态,如果被请求的共享资源被占用,就需要一套线程阻塞等待一级被唤醒是锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将获取不到锁的线程加入到队列中
CAS(比较并交换)的缩写,它通过比较内存中的值与预期值是否相等来判断共享变量的值是否被其他线程修改,然后根据比较结果来执行相应的操作。实现并发算法的常用技术 , 就是说我不用加锁 , 也能保证 ( 加锁会影响效率,可以考虑使用原子操作类 ) 原子性 , 当多个线程尝试使用 CAS 同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试 , 因此可以知道 , CAS只会允许一个线程的执行成功

ThreadLocal底层是怎么实现的?

多个线程会复制一份threadlocal变量的副本进行操作,互不影响,来保证线程安全的

线程之间如何进行通信?

1:使用synchronized锁的wait和notify进行等待和唤醒
2:使用lock锁的condition的await和signal进行等待唤醒

悲观锁和乐观锁

悲观锁的思想对线程并发持悲观态度,每次进行数据的读取时候都默认其他线程会对数据修改,所以需要进行加锁操作,当其他线程想要访问数据时,都需要阻塞挂起,使用互斥锁的机制(再任一时刻,只能由一个线程访问该对象)来解决并发问题。
乐观锁的思想是使用CAS机制,不上锁,但也能样决并发问题。
举例说明:
int count=100;
最后一次修改的时间戳或者版本号;
1:先查询一下变量的值以及它的最后一次修改的时间戳。
2:在本地线改变量的值
3,对变量进行修改的时候,先此对一下最后一次修改的时间戳是否发生变化了,如果投发生变化,修改变量的值,如果发送变化了,重试以上流程

公平锁和非公平锁

公平锁是指线程运行是有序的
非公平锁是指线程运行允许插队,具有随机性

线程的使用场景:

项目中使用线程池(注册完毕发邮件和短信)
tomcat本身就是通过线程的机制来处现用户的清求

线程池

线程池其实就是一种多线程处理形式,处理过程中可以将任务添加到队列中,然后在创建线程后自动启动这些任务。这里的线程就是实现了Runnable或Callable接口的实例对象;
优点:
1.线程和任务分离,提升线程重用性;
2.控制线程并发数量,降低服务器压力,统一管理所有线程;
3.提升系统响应速度,假如创建线程用的时间为T1,执行任务用的时间为T2,销毁线程用的时间为T3,那么使用线程池就免去了T1和T3的时间;

i++线程安全么

1、如果i是局部变量(在方法里定义的),那么是线程安全的。因为局部变量是线程私有的,别的线程访问不到,其实也可以说没有线程安不安全之说,因为别的线程对他造不成影响。
2、如果i是全局变量,则同一进程的不同线程都可能访问到该变量,i++这个操作并不是原子性的,因而是线程不安全的,会产生脏读。

数据存储的形式IO(input输入 output输出)

文件形式类似于湖泊
IO流的形式类似于河流
File file = new File(“D:\text.txt”)
FilelnputStream in = new FilelnputStream(“D:\text.txt”)

java当中IO流的分类

字节流 InputStream(抽象类)0utputStream FileInputStream FileOutputStream(实现类)
装饰者模式对传统的字节流进行了封装,增加了缓冲区功能
BufferedInputstream BufferedOutputStream(带有缓冲区的字节流)
字符流Reader writer
FileReader FileWriter BufferedReader BufferedWriter

BIO,NIO,AIO有什么区别:

BIO同步阻塞的IO,客户端发起连接之后,直到响应,这当中一直是阻塞状态
NIO同步非阻塞,服务器实现模式一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理
AIO异步的IO

手写一个单例 单例有三要素:(重点)

public class SingleTon{
	//私有的静态的成员变量
	private static SingleTon singleTon=null//私有的构造方法
	private SingleTon(){
	}
	//公共的静态的获取实例的方法(线程安全机制)
	public synchronized static SingleTon getInstance(){
		if(singleton==null){
			singleTon=new SingleTon();
	}
	return singleTon;
}

尽量不要使用全局变量

数据库

你是怎么设计数据库的?(重点)

设计数据库首先要遵循三大范式要求:原子性、依赖性、关联性
1.原子性是指数据库表的每一列都是不可分割的原子数据项
2.依赖性是指实体的属性完全依赖于主关键字。
3.关联性是指任何非主属性不依赖于其它非主属性
对于数据库设计来说,不仅仅要考虑范式要求,为了节省查询效率,允许适当的有一些冗余字段。设计数据库的时候,要预估单表的蜂值,因为以mysql为例,当单表的数据超过千万条。查询速度就特别缓慢了,这里就需要对数据库进行分库分表(数据分片),为了提高查询和写入性能,可以采取主从结构

关系型缴据库

关系型数据是面向对象设计,每一张表都是用来表述一类事物的,每一列都是描述事物属性的,每一行都是一条记录,一个对象
mysql oracle sqlServer DB2

数据库索引是什么?

索引是数据库提供的利于快速查询的机制,索引类似于书签目录。当查商条件那一列做了索引之后,那么数据库会去硬盘索引文件当中找到满是条件记录的物理位置,直接就可以定位以及获取记录。

a:  Mysql的底层数据结构有了解过么?
b:  有,是B+树。
a:  为什么是B+树呢,刚才说到HashMap用的哈希表,红黑树查询效率都挺高的,为什么Mysql不用呢?
b:  哈希表的一次查询是很快,但是范围查询就很搓了;至于红黑树的话,由于二叉树的特性,数据量太大的情况下,树会很高,由于数据是存储在磁盘上的,这样与磁盘的IO会很频繁,是不可以接受的。所以选择B+树。
a:  那树高的问题B树也可以解决,为什么非要是B+树。
b:  因为B+树非叶子节点是不存储数据的,B树是存储数据的,而且B+树的叶子结点上有所有的数据,而B树的数据是散落在树的各个层级里面,且,B+树的子节点还有指针相连,是一个双向链表、这样,虽然B树的节点数比B+树少,但是应付一些全表扫描的场景上,B+树更有优势;并且,在范围查询的场景中,我们找到了一个值,B+树会更方便顺序取出剩下的所有值。
a:  Mysql中的B+树大概会有几层,查询的时间是怎么样的?
b:  B+树的高度在数据库中大概只有2到4层,磁盘的IO1秒至少完成100次,所以查询时间应该在0.02s到0.04s。

聚簇索引和非聚簇索引

聚簇索引:以InnoDB为存储引擎的表,表中的数据都会有一个主键,即使不创建主键,系统也会帮你创建一个隐式的主键
因为InnoDB把数据存放在B+树中的,而B+树的键值就是主键,在B+树的叶子节点中,存储了表中的所有数据,这种以主键作为B+树索引的键值而构建的B+树索引,我们称之为聚簇索引
非聚簇索引:以主键以外的列值所谓键值构建的B+树索引,我们称之为非聚簇索引
区别:非聚簇索引叶子结点不存储表中的数据,而存储该对应的主键,想要查找数据,我们还需要根据主键再去聚集索引中进行查找,这个再根据聚集索引查找数据的过程,我们称为回表```

Mysql支持以下约束

主键约束 :primary key
唯一性约束:unique key
外键约束:foreign key
非空约束:not null
默认值约束:default

B树

B树是一种多路平衡查找树,指的是子树高度相同即所有叶子节点均在同一层,平衡因子等于0,除了根节点外,其余每个节点都至少有m/2个分叉(m是他的阶,也是节点的最大分叉树,可以理解为每个节点最多有m棵子树)
1.所有节点中有分叉最多的例如有3个分叉成为3阶b树
2.每个节点中包含多个元素,成为关键字,当某个节点有m棵子树时,则一点有m-1个关键字
3.左小右大,由于一个节点会有多个关键字,所以有多个范围
467e2631a9754344b0125371c8a4eb2d
722988da411c4b82b7c67d9c98e82e51
优点:查询中,磁盘IO效率很低,而树结构在数据量较大时,采用链式存储,使数据分散在磁盘的各个角落,要不停进行寻道读取磁盘IO,读入内存后查询速度大大提升,同时减少IO次数
缺点:部分关键字在非终端节点中,部分关键字在终端节点,没办法惊醒范围查询,同时每个节点包含了关键字的存储信息,占用了一部分空间,注定会影响每个节点的关键字个数,没有把磁盘IO优化到最优

B+树

​1.B+树的非叶子节点不包含关键字的存储地址
2.所有叶子节点通过指针链相连,且叶子节点本身按关键字的大小从小到大顺序排列。
3.另外通常B+树有两个头指针,一个指向根节点一个指向关键字最小的叶子节点。

B树和B+树的区别

1,关键字的数量不同;B+树中分支结点有m个关键字,其叶子结点也有m个,其关键字只是起到了一个索引的作用,但是B树虽然也有m个子结点,但是其只拥有m-1个关键字。
2,存储的位置不同;B+树中的数据都存储在叶子结点上,也就是其所有叶子结点的数据组合起来就是完整的数据,但是B树的数据存储在每一个结点中,并不仅仅存储在叶子结点上。
3,分支结点的构造不同;B+树的分支结点仅仅存储着关键字信息和儿子的指针(这里的指针指的是磁盘块的偏移量),也就是说内部结点仅仅包含着索引信息。
4,查询不同;B树在找到具体的数值以后,则结束,而B+树则需要通过索引找到叶子结点中的数据才结束,也就是说B+树的搜索过程中走了一条从根结点到叶子结点的路径。

数据库调优(重点)

1.数据库结构优化:设计数据库的时候,要预估单表的峰值,因为以Mysql为例,当单表的数据超过千万条,查询速度就特别缓慢了,这里就需要对数据底进合分库分表(数据分片),为了提高查询和写入性能。mysql可以采取主从结构,主数据库采用innodb,从数据库采用innodb引擎,主从之间通过监听主数据库的binlog日志来完成数据一致,从服务器有两个线程,一个是IO线程,一个是
sql线程,IO线程负责读取主mysql服务器的binlog日志,把读取到的日志放入到中继日志中,sql线程负责读取中继日志,并转为具体的操作,实现数据的一致性。
2.SQL优化,本质是通过创建索引,使用explain关键字,查询索引命中情况,对sql进行调优,explain当中有多种参数,比如:索引类型,命中率,执行时间
3.使用cache层,预先对经常常用的数根库数据进行缓存,以减少数据库的压力,提高数据库性能

sql怎么调优,索引命中怎么知晓?(重点)

使用explain关键字查看sql的执行计划,可以看到该sql的索引命中情况、索引命中类型,命中率等关键信息,可以通过这种方式对sql语句进行调优,对关键查询的条件创建索引并且要注意以下sql的用法,尽量不适用like,尽量避免在某一列上进行运算,不适用in,not in,使用exist not exist等等代替

sql调优技巧

1.避免使用select*
2.用union all代替union(排重的过程需要遍历、排序和比较,它更耗时,更消耗cpu资源)
3.小表驱动大表(用小表的数据集驱动大表的数据集 in/exist)
4.批量插入 (每次远程请求数据库,是会消耗一定性能,批量插入需要远程请求一次数据库,sql性能会得到提升,数据量越多,提升越大)
5.多用limit(避免全表扫描,提高查询效率,)
6.in中值太多(容易导致接口超时,代码中添加限制,或者分批多线程查询)
7.增量查询(增加查询条件,按id和时间升序,每次查询后记录查询的最大id和时间等,给同步下一批数据的时候用,提升单次查询效率)
8. 高效的分页(随着页码的增大,查询效率越低下,使用where条件或between优化分页,between要在唯一索引上分页,不然会出现每页大小不一致的问题)
9.用连接查询代替子查询(子查询时,需要创建临时表,查询完毕后,需要再删除这些临时表,有一些额外的性能消耗)
10.join的表不宜过多(如果join太多,mysql在选择索引的时候会非常复杂,很容易选错索引)
11.join时要注意(用left join关联查询时,左边要用小表,右边可以用大表。如果能用inner join的地方,尽量少用left join)
12.控制索引的数量(表中新增数据时,需要同时为它创建索引,而索引是需要额外的存储空间的,而且还会有一定的性能消耗,单表控制在5个以内)
13.选择合理的字段类型(能用数字类型,就不用字符串,因为字符的处理往往比数字要慢,长度固定的字符串字段,用char类型。长度可变的字符串字段,用varchar类型。金额字段用decimal,避免精度丢失问题。)
14.提升group by的效率(先缩小数据的范围之后,再分组)
15.索引优化(可以使用explain命令,查看mysql的执行计划)

mysql 如何实现悲观锁和乐观锁

1:mysql提供了表锁和行锁的机制,这就是悲观镇的体现select * from user where id=7 for update
2:mysq没有提供乐观锁的机制,需要自己手动实现,
更新user表当中id=1的username的值,每一条记录都有一个最后一次修改时间,进行更新操作之前,先把上一次的最后一次修改时间查询出,执行sql语句
update user set username=“新值”where id=1 and updateTime=上次查询的那个时间

索引的常用数据结构

hash索引 hash 均匀分布
B树索引 b树 分布有序
比如我对A列分别建立Hash索引和B树索引,当我频繁对A列的数据进行修改的时候,hash索引性能高,但是如果按照范围进行查找的话,B树索引的性能高(where A>100 and A<1000)

索引有什么优缺点?

索引可以提高查询的效率,但是对记录进行增删改的时候,由于数据库需要维护索引文件,所以说如果频繁修改记录的话,影响数据库性能。我们在项目当中创建索引的时候,只是针对于那些频紧查询的字段创建索引。通常情况下创建索引的列不会超过6个

indoob和myisam区别

innodb支持事务和外键约束,可以有效的管理和使用大量数据
myisam不支持事务和外键约束,适合存储小型数据集
Innodb不支持全文索引,而myism支持全文索引
innodb叶子节点存储的是整个数据所有的数据,myisam索引和数据是分开存储的,叶子节点存储的是数据所在的地址,不是数据
innodb支持行锁增删改操作快,myisam支持表锁查询快

数据库的事务隔离级别

1.读未提交:允许读取未提交的数据
2.读已提交:允许读取并发事务提交的数据,防止脏读,会幻读以及不可重复读
3.可重复读:对同一字段的多次读取都是一致的,除非是被事务本身修改,可能会幻读
4.可串行化:所有事务依次执行,可防止所有问题,效率偏低

脏、幻、不可重复读

1.脏读:当一个事务读取并修改了数据,还未提交,另一个事务也访问了该数据,并使用数据了数据,这可能是不正确的
2.幻读:一个事务读取了几条数据,接着另一个事务插入了其他数据,随后第一个事务会发现多了几条数据
3.不可重复读:一个事务多次读取数据,这是另一个事务进来修改了其中的数据,第一个事务发现几次读取的数据不同

JVM

jvm内存结构

Jvm内存区域主要分为三部分;堆、栈、非堆区域
堆主要存放的是对象的实例,栈当中存放线程运行时的临时变量,非堆区效又包含永久代、方法区、常量池等,jk1.8之后,取消了永久代。使用操作系统本地内存来存放class信息,常量池移到了堆内存中
新生代主要存放的是新创建的对象,新生代主要是采用复制算法进行垃圾回收minorGC,老年代存放的是在新生代阶段被多次gc回收还没有回收掉的对象,老年代也会进行垃圾回收gc(mjorGC),采用的是标记-整理算法,老年代进行GC的时候会造成一个应用程序线程中断的效果,会影响到应用程序的性能。
jvm提供了很多种垃圾回收策略,垃圾回收器CMS、G1(采用多线程进行GC)

什么时候对象可以进行垃圾收集?

引用计数算法(refcrence-counting):每个对象有一个引用计数器,当对象被引用一次则计数器加1,当对象引用失效一次则计数器减1,对于计数器为0的对象意味着是垃圾对象,可以被GC回收
可达性算法(GC Roots Tracing):从GC Roots作为起点开始搜索,那么整个连通图中的对象便都是活对象,对于GC Roots无法到达的对象便成了垃圾回收的对象,随时可被GC回收。

垃圾收集算法?

1.复制算法
2.标记清除算法
3.标记整理算法
4.分代管理算法(新生代使用复制算法,老年代使用标记整理算法)

对象的引用类型

1,强引用(Object obj = new Object();)
2:软引用
3:弱引用
4:虚引用

jvm的调优

通常我们在项目当中,需要对jvm进行调优,以防止内存溢出的现象。主要通过调整jvm的启动参数,包括最小堆内存xms、最大堆内存xmx、栈内存xss,永久代permSize.由于jdk1.8中永久代被弃用了,使用操作系统本地内存,所以不需要调节永久代大小了,还包括使用并行的垃圾收集器,比如我们用的cms、G1垃圾收集器,通常情况下,我们将最小堆内存和最大堆内存设置的一样,防止内存回落造成GC。在生产环境,我们通过使用Jconsole JvisualVm对jvm可以进行监控。

通过监控发现,cpu和内存使用率居高不下,该怎么排查问题

1.可以通过linux top命令查看机器的cpu和内存使用情况然后通过jstack获取java进程的线程使用情况,然后查看日志,排查问题
2.使用jvm监控工具Jconsole JvisualVM远程连接到服务器,分析线程和堆栈内存,排查问题,最有可能的问题是:代码死循环,线程死锁、jvm内存溢出等

jdk1.8之后的新特性(重点)

1:支持函数式编程Lambda表达式
2:方法引用
3:集合的strean处理
jdk的内存结构发生重大变化
永久代弃用,class信息使用本地内存存储
常量池移动到堆中
堆内存,栈内存、永久代内存、方法区内存
垃圾收集器选择?CMS G1内存、方法区内存
tomcat 的bin目录下面的catalina.bat catalina.sh
eclipse installerJres
jvm的内存线程线程监控
jconson
jvisuawm,Linux 相关命令,jstack去查看该jvm的内存使用情况、线程情况等

Mysql主从如何切换的?

1.使用springaop,当时我们在项目当中利用spring 的aop来完成数据源的动态切换的,首先我们准备两个数据源,一个连接主服务器,一个连接从服务器,然后我们编写一个类、里面实例化一个threadlocal,用来保存当前线程的数据源,我们还需要编写一个类,此类继承spring提供的abstractreeingdatasource,重写里面的一个方法,我们还需要一个切面类,因为service层有事务的支持,所以切入点指向 service层,对于增删改的时候我们使用主mysql数据源,对于查询使用从数据源。
2.使用mycat做为代理中间件。

tomcat的工作原理

tomcat的核心组件包括server(服务器)service(服务)connector(连接器)Executor(执行器)engine(servlet引擎).
server 当中有一个catalina服务,调用conector(连接器)组件接收用户的连接,conector(连接器)底层是通过ServerSocket监听8080端口号接收客户端的tcp连接的,接收到连接之后,会调用Executor(执行器)创建或捞取线程执行业务逻辑处理,最后再调用servlet引擎完成请求以及响应。

tomat调优

1.tomcat默认的线程创建方式,是一次请求,创建一个新的线程来处理优化方案,可以配置线程池来创建和管理线程,可以设置线程数量

2.tomeat 默认的连接处理方式是BIO,阻塞的IO,容易造成客户端堵塞
优化方案:使用http11Nio协议来进行连接的处理
3.因为tomcat是基于jvm的,所以应该对jvm的启动参数进行调优(堆栈内存、垃圾收集器)

WEB

session和cookie的区别

1.对象不同:cookie针对每个网站,其他网站无法访问,存储在客户端,每次打开网站先去找cookie没有的话再去服务器请求。session是针对用户的,存储在服务端
2.存储大小不同:cookie不超过3kb,session不限制大小,太大服务器可以选择清理
3.生命周期不同:cookie关掉网页就没了,从创建开始30分钟后消失。session如果30分钟没有访问就被销毁
4.存储位置不同:cookie在客户端,session在服务端
5.数据类型不同:都已key-value存储,key是相同的,cookie的value是字符串类型,session是object类型
6.安全性不同:cookie不安全

如果客户端禁止cookie能实现session还能用吗?

可以,使用session时,服务端会生成一个唯一id存储在cookie中,如果禁用cookie,可以使用url重写,将id作为参数放入url中

##OSI七层模型/TCP四层模型
OSI
应用层 表示层 会话层 传输层 网络层 数据链路层 物理层
TCP
应用层 传输层 网络层 数据链路层

tcp和udp有什么区别?(重点)

tcp是面向连接的,建立连接前需要进行三次握手,断开连接前需要进行四次挥手,可靠的不会造成数据丢失的,可以间单理解成打电话,效率低。
udp协议是面向无连接,不可靠有可能造成数据丢失,可以理解成发电报,效率高。

http协议和https协议区别

一、传输信息安全性不同
1、http协议:是超文本传输协议,信息是明文传输。如果攻击者截取了Web浏览器和网站服务器之间的传输报文,就可以直接读懂其中的信息。
2、https协议:是具有安全性的ssl加密传输协议,为浏览器和服务器之间的通信加密,确保数据传输的安全。
二、连接方式不同
1、http协议:http的连接很简单,是无状态的。
2、https协议:是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议。
三、端口不同
1、http协议:使用的端口是80。
2、https协议:使用的端口是443.
四、证书申请方式不同
1、http协议:免费申请。
2、https协议:需要到ca申请证书,一般免费证书很少,需要交费。

请详细描述下一次http请求到最后响应这之间的过程?浏览器输入一个网址按下回车之后的完整过程?(重点】

客户端发起http请求,因为http协议里面封装的tcp协议,会首先经过dns解析,找到目标服务器,发起tcp连接进行三次握手,之后连接进入tomcat,tomcat会对连接进行处理,处理完毕之后响应一个html文档给客户端,客户端通过浏览器进行可视化,tcp断开连接的时候进行四次挥手
http/1.1 长连接 通过一个tcp连接,可以向服务端发送若干个http请求

##请简述一下tcp协议三次握手或四次挥手的过程?
1:客户端发送syn包
2:服务端接收到syn包之后,回复syn+ack
3:客户端接收到服务端的syn+ack之后,会回复一个ack
客户端向服务端发送断开连接的请求(客户端请求断开连接,客户端不再发送消息)
服务端发送消息,确认收到请求(服务端收到请求但服务端可能存在消息未发送,示意客户端收到消息,但不断开)
服务端向客户端发送断开连接请求(服务端消息发送完毕,请求断开连接)
客户端发送收到请求(客户端收到请求,发送给服务端,确认断开)(连接关闭)

Java-web的核心

socket 是网络编程的根本,线程是程序运行的根本。Tomcat底层通过ServerSocket监听8080端口号,能收到tcp连接之后,会调用线程执行代码。

servlet的生命周期:

servlet的对象是tomcat容器通过加载web.xml解析之后通过反射机制创建的,创建完之后会惊醒初始化init,然后service提供服务,容器关闭时销毁servlet时单例存在tomcat的内存中,servlet是面向方法编程的思想,doGet,doPost包括sprintmvc也是面向方法。

协议有哪几种请求方式?

set post put delete
get和post请求是最常用的请求方式

get和post请求有什么区别?

get请求是通过请求行url来传输参数的,安全性比较低,传输参数的大小不超过2KB效率快。post请求是通过请求正文来提交数据的,安全性比较高太统输数据的大小不受限制,效率低。

请求的转发和重定向有什么区别

转发只访问服务器一次,重定向访问服务器两次
转发页面的URL不会改变,重定向地址会改变
转发只能转发到自己的web应用内,重定向可以重定义到任意资源路径。

同步与异步的区别

同步请求会使客户端处于阻塞状态,直至服务端响应完毕,基本上所有的请求都是同步请求,类似于打电话
比如服务器一接收客户端请求,马上响应,这样客户端可以在最短的时间内得到结果,但是如果多个客户端,或者一个客户端发出的请求很频繁,服务器无法同步处理,就会造成涌塞。
异步请求不会使客户端处于阻塞,把请求发出去,然后服务端响应之后会进行异步回调把数据传送给客户端,类似于寄信
服务器接收到客户端请求后并不是立即处理,而是等待服务器比较空闲的时候加以处理,可以避免涌塞。
ajax请求就是异步请求,用来局部刷新,隐形请求

Spring

Spring特性 spring理解

spring 有两大核心。IOC和AOP
IOC即控制反化,通常我们在项目当中需要手动new去创建对象,运种方式不利于对对象的管理。现在我们将对象的创建权力反转给spring容器,这就是控制反转,spring容器在实例化对象的时候,会根据对象之间的以来关系,自动完成属性注入工作,这就是依赖注入
AOP即面向切面编程,底层是通过动态代理的机制来实现的、支持jdk和cglib两种。默认通过jdk动态代理。通常我们在项目当中,一些公共功能的实现可以通过aop来进行解耦和统一实现,比如事务管理,日志,权限等等
我们在项目当中的事务管理是这样配置的:
1:声明spring的事务管理器 transactionManager
2:配置一下事务增强的传播特性 tx:advice tx:nethod
对于增删改开头的方法,使用事务进行管理,对于查询开头的方法,只读模式
3:配置切面,通常我们切面设置在service 实现类这一层 pointcut

jdk的动态代理和cglib动态代理有什么区别?

JDK 动态代理:
基于接口来创建被代理对象的代理实例。当对象要被代理时,它必须实现一个或多个接口并依赖JDK库。JDK动态代理利用反射机制生成一个包含被代理对象的所有接口的代理类,并覆盖接口中的所有方法,可以对目标对象进行代理。
优点:无需引用第三方库,在JRE运行环境中就可以运行,生成代理对象更加简单、快捷;缺点:仅支持基于接口进行代理,无法对类进行代理,所以它的作用有限。
Cglib 代理:
基于继承的方式对被代理类生成子类,从而添加代理逻辑。因为它是继承了被代理类,所以它会受到final类、private、static等不可继承属性的影响。
优点:Cglib支持对类进行代理,即使没有接口,也可通过设置回调接口间接地实现。性能比JDK动态代理更高,能够代理那些没有实现任何接口的目标对象。
JDK和Cglib区别:
jdk 的动态代理要求业务类必须得实现业务接口,底层是通过生成业务接口的动态代理实现类来完成功能增强
cglib 不需要业务类实现接口,底层是通过衍生出当前业务类的子类对象来完成功能增强Cglib性能更强
jdk的动态代理要求业务类必须得实现业务接口,底层是通过生成业务接口的动态代理实现类来完成功能增强
cglib不需要业务类实现接口,底层是通过衍生出当前业务类的子类对象来完成功能增强
Cglib在生成代理类的过程中,采用动态生成字节码的方式,在被代理类加载之前就完成了代理类的创建并缓存到内存中,以后每次调用时,都直接使用缓存的代理类。在大多数情况下,Cglib代理比JDK动态代理更适合于大规模的方法拦截和增强等场景。

spring中创建对象的三种方式

  1. 使用无参数构造器创建
  2. 使用静态工厂方法创建
  3. 使用实例化对象工厂方法创建

spring中创建的对象的作用域

作用域有singleton和prototype,默认为前者,设置延迟加载只对singleton有效。

  1. 当scope为singleton时
    可以看出作用域为singleton时,对象只创建了一次,并且两者相等,虽然使用getBean方法两次,但是却只有一个对象,似乎对象不是getBean方法获得,这跟后面要说的延迟加载有关。
  2. 当scope为prototype时
    当作用域为prototype时,创建了两个对象,并且两者不相等,说明使用一个getBean方法,就创建了一个新的对象。
    延迟加载:默认情况下容器启动之后,会将作用域为singleton的bean创建好,设置延迟加载容器启动之后,对作用域为singleton的bean不再创建,直到调用getBean方法才会创建,设置延迟加载需在配置文件中设置lazy-init属性。

你看过spring的源码么?说说IOC的源码?

平常看过一些博客,了解一部分sping的源码,spring的ioc有两大核心接口,beanFactory 和applicationContext,beanfactory是通过工厂模式来生产bean,默认生产的是单例bean。
applicationcontext是beanFactory的子接口。,增强了BeanFactory的功能

spring当中的单例,是如何保障它线程安全的?

springmvc 是通过ThreadLocal来保障单例线程安全的。

springmvc的原理,工作流程,源码

首先请求到达核心控制器 dispatcherServlet,dispatcherServlet会判断请求后级是否满足格式要求,如果满足格式要求,会查询handlaerMapping查找对应的handler,调用handlerAdapter进行参数绑定等适配工作,之后调用具体的handler进行业务逻辑处理,返回一个modelandView,对象给dispatcherservlet,dispatcherServlet 调用viewResolver进行视图解析渲染,然后返回给客户端。

springmvc的常用注解有哪些?(重点)

Controller requestMappingy requestBody responseBodye Pathvariable RequestParam

springmvc的拦截器怎么写?

定义一个类,实现了handlerlnterceptor 接口,重写preHandle、postHandle、afterCompletion三个方法,之后在springmvc的配置文件当中使用mvc:interceptors里面可以配置多个interceptor。

spring和springBoot

spring:
Spring框架为开发Java应用程序提供了全面的基础架构支持。它包含一些很好的功能,如依赖注入和开箱即用的模块,如:Spring JDBC 、Spring MVC 、Spring Security、 Spring AOP 、Spring ORM 、Spring Test,这些模块缩短应用程序的开发时间,提高了应用开发的效率。例如,在Java Web开发的早期阶段,我们需要编写大量的代码来将记录插入到数据源中。但是通过使用Spring JDBC模块的JDBCTemplate,我们可以将这操作简化为只需配置几行代码。
springboot:
SpringBoot是Spring框架的扩展,是一个在spring基础上进行简化配置和开发流程的web整合的轻量级框架,可以更快更高效地开发生态系统,使开发,测试和部署更加方便。SpringBoot不是Spring官方的框架模式,而是一个团队在Spring4.0版本上二次开发并开源公布出来的。

springboot自动装配本质

SpringBoot自动装配的本质就是通过Spring去读取META-INF/spring.factories中保存的配置类文件然后加载bean定义的过程。
如果是标了@Configuration注解,就是批量加载了里面的bean定义
如何实现 “自动”:通过配置文件获取对应的批量配置类,然后通过配置类批量加载bean定义,只要有写好的配置文件spring.factories就实现了自动。

bean的作用域

1.singleton单例 在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,bean作用域范围的默认值。
2.prototype原型 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean()。
3.request请求 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于web的Spring WebApplicationContext环境。
4.session会话 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean。该作用域仅适用于web的Spring WebApplicationContext环境。
5.application全局 限定一个Bean的作用域为ServletContext的生命周期。该作用域仅适用于web的Spring WebApplicationContext环境。

spring单例模式如何保证线程安全

1.@Scope(“prototype”)

2.threadlocal

Mybatis

mybatis 是一个半自动化轻量级的orm框架,程序员可以手写sql语句,使用起来比较灵活。

在Mybatis中 Dao接口和XML文件的SQL如何建立关联?

针对有两个XML文件和这个Dao建立关系是否会冲突的问题:不管有几个XML和Dao建立关系,只要保证namespace+id唯一即可。

Mybatis的四种分页方式

1、借助数组进行分页
2、借助Sql语句进行分页
3、拦截器分页
4、RowBounds实现分页

mybatis嵌套查询和嵌套结果

使用嵌套结果来查询:相当于使用一条sql语句关联多张表查询出所有结果,然后根据映射给订单类属性赋值
使用嵌套查询:查询订单数据时新用一条sql来查。参数是column属性的值,如果多参数的话:使用
column=“{property1 = column1,property2 = column2…}”

mybatis中#{}和${}区别

#{} 占位符 为参数占位符?即sql预编译
KaTeX parse error: Expected 'EOF', got '#' at position 29: …换,即sql拼接 变量替换后,#̲{} 对应的变量自动加上单引号…{} 对应的变量不会加上单引号 ‘’
#{} 能防止sql 注入
${} 不能防止sql 注入

mybatis 的二级缓存

mybatis是自动开启一级缓存的,sqlsession级别
mybatis的二级缓存需要手动开启的,mapper级别(application应用),通常目当中,对于字典表(大量查前,很少修改)的数据。,可以使用mybatis的二级缓存机制,提高查的效率。

如何使用Mybatis的二级领存,

1:mybatis的核心配置文件当中需要手动开启二级领存
2:在指定mapper文件中,是否启用二级缓存,以及二级缓存的实现类(实现cache接口)
3:如果mapper启动二级缓存,那么该mapper对应的实体类,必须实现序列化接口。mybatis的二级缓存通常是使用ehcache,但是咱们使用redis来充当二级缓存容器

mybatis 如何主键回填

insert标签里面,加入select last_insert_id()标签行查询以及结果集自动映射。

redis

redis支持的键值数据类型有五种:

1.字符串类型string 存放字符串或二进制数据
2.散列类型hash 存放对象
3.列表类型list 队列支持push以及poll操作
4.集合类型set 无序可去重复
5.有序集合类型zset 通过元素绑定分数来实现有序

redis三种特殊数据类型

Geospatial:地理位置
Hyperloglog:基数
Bitmap: 位存储

redis的哨兵机制

redis主从之间可以通过哨兵进行检查,如果主节点出现故障,会自动切换从节点为主

redis cluster集群原理

redis cluster集群默认16384个hash槽,集群搭建成功之后,需要给每一个主节点,分配hash槽。当外部数据插入的时候,会对key进行crcl6(查表法)然后对16384取模这样就计算出哪个节点对该数据进行管理。我们在项目当中采用的3主3从的结构,主从之间通过哨兵,出现故障自动切换。

你们在项目当中哪些地方用到redis?(重点)

1:项目当中一些字典表数据,通过redis进行缓存提高查询性能,比如品牌表,省市县表等,我们使用 spring-cache 整合redis实现分布式缓存
2:利用redis的过期策略,短信验证码存入redis,两分钟之内有效
3:整个项目架构采用redis充当session容器,实现分布式环境下的session共享,Spring-session整合redis
4: 利用redis的list队列,基于push pop的原子性,实现商品抢购的秒杀场景
5:购物车功能的使用redis来存储登录用户的购物车信息

缓存的穿透、击穿和雪崩,你们是如何解决

穿透
1.每次请求时,可以在redis层,做一个bitmap,先去bitmap中查询一下是否存在该条件,进行过滤
2.每次请求,查询数据库就算不存在我也将查询条件和null在redis中进行缓存,设置一个很短的存活时间.
击穿
1.分布式互斥锁,只允许一个线程重建缓存,其他线程等待重建缓存的线程执行完,重新从缓存获取数据即可
2.设置永远不过期
雪崩
对大量数据设置过期时间时,哪怕业务需要同时失效,我也不设置同一个时间,分别对每一组key value 设置失效时间,让每一组key value的失效时间间隔个几毫秒

redis是单线程执行的,为什么性能这么高?

redis通过C语言实现了多路复用chanel select轮询的机制,保证了高性能(类似于NiO)
1.redis是基于内存的,内存的读写速度非常快;
2.redis是单线程的,省去了很多上下文切换线程的时间;
3.redis使用多路复用技术,可以处理并发的连接;

redis缓存机制

1.AOF:redis处理的每一条命令都会记录在aof文件中,可以看作是命令日志文件
rewrite:aof中记录了所有的命令会导致aof文件很大并且存放了很多冗余命令,通过rewrite,所有冗余的指令会被删除,达到压缩文件的效果
2.RDB:redis数据快照,将内存中所有的数据都记录到磁盘中,当redis实例故障重启后,从磁盘读取文件,恢复数据,快照文件成为rdb文件,默认保存在当前的运行目录
缺点:执行间隔时间长,两次写入之间有数据丢失风险,费时
3.AOF+RDB:某一时刻先删除备份文件,然后再备份rdb快照,之后两次rdb之间的数据由aof保存。

Redis的应用场景

内容缓存(数据查询、短连接、新间内容、商品内容等等)。(成多使用)
分布式集群架中的session分离。任务队列,(秒杀、抢购、12306等等)
数整过期处理(可以精确到毫秒)

持久化

Redis的高性能是由于其将所有数据都存储在了内存中,为了使Redis在重启之后仍能保证数据不丢失,需要将数据从内存中同步到硬盘中,这一过程就是持久化。
Redis支持两种方式的持久化,一是RDB方式,一种是AOF方式。可以单独使用其中一种或将二者结合使用。

RDB持久化

RDB方式的持久化是通过快照完成的,当符合一定条件时Redis会自动将内存中的数据进行快照并持久化到硬盘。
RDB是Redis默认采用的持久化方式,在redis.conf配置文件中默认有此下配置:
save 900 1
save 开头的一行就是持久化配置,可以配置多个条件(每行配置一个条件),每个条件之间是“或”的关系

AOF持久化

默认情况下Redis 没有开启AOF,(append-only file)方式的持久化,可以通过appendonly 参数开启:apenidonly:yes
开启AOF持久化后每执行一条会更改Redis中的数据的命令,Redis就会将该命令写入硬盘中的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的,默认的文件名是appendonly.aof,可以通过appendfilename 参数修改:appendfilename appendonly.aof ke品考B)

RabbitMQ

MQ成为消息队列,用于服务之间同步调用解耦,实现数据的最终一致性rabbitMQ是使用erlang语言开发的,天然支持高并发提供了消息的确认机制,保证了消息的可靠性
特点:消费者不需要去保证提供者是否存在,实现了消费者与生产者之间的高度解耦

rabbitMQ的消息发布模式

basic 点对点 round 一对多 exchange 发布订阅

RabbitMQ如何保证消息不会丢失?

消息丢失很有可能在生产者、mq服务、消费者任意一个环节出现,生产者可以使用channel事务机制、confirm机制,前者能保证消息传递的可靠性,但是吞吐量会下降,这时候可以使用confim机制,mq收到消息之后会进行ack回调通知,即采用持久化对消息进行硬盘存储,可以保证mq异常关机之后,数据不会丢失,消费者关闭rabbitMQ提供的自动ACK消息确认机,改为消费者处理完消息之后,手动ACK(基于tcp的ack事务包)

Rabbit保证消息幂等性的办法

1.生成者不重复发送消息到MQ
mq内部可以为每条消息生成一个全局唯一、与业务无关的消息id,当mq接收到消息时,会先根据该id判断消息是否重复发送,mq再决定是否接收该消息。
2.消费者不重复消费
消费者怎么保证不重复消费的关键在于消费者端做控制,因为MQ不能保证不重复发送消息,所以应该在消费者端控制:即使MQ重复发送了消息,消费者拿到了消息之后,要判断是否已经消费过,如果已经消费,直接丢弃。所以根据实际业务情况,有下面几种方式:
– 如果从MQ拿到数据是要存到数据库,那么可以根据数据创建唯一约束,这样的话,同样的数据从MQ发送过来之后,当插入数据库的时候,会报违反唯一约束,不会插入成功的。(或者可以先查一次,是否在数据库中已经保存了,如果能查到,那就直接丢弃就好了)。
–让生产者发送消息时,每条消息加一个全局的唯一id,然后消费时,将该id保存到redis里面。消费时先去redis里面查一下有么有,没有再消费。(其实原理跟第一点差不多)。
–如果拿到的数据是直接放到redis的set中的话,那就不用考虑了,因为set集合就是自动有去重的。

RabbitMQ使用场景

1.用户订单,库存处理。【服务间解耦】
使用MQ前:系统正常时,用户下单,订单系统调用库存系统进行删减操作,操作成功,将成返回消息,提醒下单成功。系统异常时,库存系统将无法访问,导致订单删减操作无法执行,最终导致下单失败。
使用MQ后:订单系统和库存系统之间不在互相影响,独立运行,达到了应用解耦的目的。订单系统只需要将下单消息写入MQ,就可以直接执行下一步操作。这时即使库存系统出现异常也不会影响订单系统的操作,且下单的库存删减记录,将会被永久保存到MQ中,直到库存系统恢复正常,从MQ中订阅下单消息,进行消费成功为止。
2.用户注册,发送手机短信,邮件。【实现异步通信】
使用MQ前:整个操作流程,全部在主线程完成。点击用户注册 --》 入库添加用户 --》发送邮件 --》发送短信。每一步都需要等待上一步完成后才能执行。且每一步操作的响应时间不固定,如果请求过多,会导致主线程请求耗时很长,响应慢,甚至会导致死机的情况出现,严重影响了用户的体验。
使用MQ后:主线程只需要处理耗时较低的入库操作,然后把需要处理的消息写进MQ消息队列中,然后由不同的独立的邮件系统和发短信系统,同时订阅消息队列中的消息进行消费。这样通过消息队列作为一个中间人去保存和传递消息,不仅仅耗时低消耗的资源也很少且单个服务器能够承受的并发请求将更多。
3.商品秒杀和抢购。【流量削峰】
流量削峰是消息队列中常用的场景 一般在秒杀或团购活动中使用广泛。
使用MQ前:对于秒杀、抢购活动,用户访问所产生的流量会很大,甚至会在同一时间段出现上万上亿条请求,这股瞬间的流量暴涨,我们的应用系统配置是无法承受的,会导致系统直接崩溃死机。
例如:A系统平时每秒请求100个,系统稳定运行; 但是晚上8点有秒杀活动 ,每秒并发增至1万条 ,系统最大处理每秒1000条 于是导致系统崩溃。
使用MQ后:我们在大量用户进行秒杀请求时,将那个巨大的流量请求拒在系统业务处理的上层,并将其转移至MQ中,而不是直接涌入我们的接口。在这里MQ消息队列起到了缓存作用。
例如:100万用户在高峰期,每秒请求5000个,将这5000个请求写入MQ系统每秒只能处理2000请求,因为MySQL只能处理2000个请求 ; 系统每秒拉取2000个请求 不超过自己的处理能力即可。

什么是Nginx?

Nginx是一个web服务器和反向代理服务器,Nginx配置文件主要分成四部分:main(全局设置)、server(主机设置)、upstream(上游服务器设置,主要为反向代理、负载均衡相关配置)和location(URL.配特定位置后的设置)
main 部分设置的指令将影响其它所有部分的设置;
server部分的指令主要用于指定虚拟主机域名、IP和端口
upstream的指令用于设置一系列的后端服务器
设置反向代理发后端服务器的负载均衡
location部分用于匹配网页位置
server继承main,location继承server
upstream既不会继承指令也不会被继承

说说linux系统的常用shell命令

tar -zxvf 用于解压 .tar.gz 后缀结尾的
tar -xvf 用于解压 .tar 后缀结尾的
yum install mysql 安装软件
wget 资源路径 下载资源
scp 复制资源到远程服务器
ps -ef|grep redis 查找redis进程以及端口号
tail-f catalina.out 监听tomcat的控制台日志
chmod 777 shell.sh 对某文件授权(读写执行)
free 或top或vmstat 这三个命令是查看系统内存使用情况
df或du 这两个命令是查看系统文件硬盘使用情况Find或whereis查找文件

权限管理Shiro

shiro是apache旗下的权限控制框架,shiro有四大核心
认证器(authenticator)、授权器(Authorizer)、会话管理器(sessionManager)、密码管理器(Cryptography)
我们在项目当中使用shiro的时候,需要配置shiroFillter,指定的登录url、非法访问的url、权限过滤规则等等,认证和授权通过自定义Realm,重写登录认证方法以及授权方法,shiro不仅仅提供url级别的权限控制,我们还通过shiro标签实现按钮级别的权限管理,session管理这一块,我们使用shiro整合redis-cluster,实现分布式环境下的session分离,手动开发sessionDaom,重写session的增删改查方法

你可能感兴趣的:(java)