Java学习笔记_java八股文
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
10.18日已更新错误已修改~~~有错误的地方欢迎大家留言
目录
11.String、StringBuffer、StringBuilder的区别
23.MySQL 删除自增 id随后重启 MySQL 服务再插入数据自增 id 会从几开始
九.其他RabitMQ、数据结构与算法、nginx、git、jwt登录等...
一、Java基础篇
1.接口和抽象类的区别
相似点
1接口和抽象类都不能被实例化
2实现接口或继承抽象类的普通子类都必须实现这些抽象方法
不同点
1抽象类可以包含普通方法和代码块接口里只能包含抽象方法静态方法和默认方法
2抽象类可以有构造方法而接口没有
3抽象类中的成员变量可以是各种类型的接口的成员变量只能是 public static final 类型的并且必须赋值
2.重载和重写的区别
重载发生在同一个类中方法名相同、参数列表、返回类型、权限修饰符可以不同
重写发生在子类中方法名相、参数列表、返回类型都相同权限修饰符要大于父类方法声明异常范围要小于父类方法但是final和private修饰的方法不可重写
3.==和equals的区别
==比较基本类型比较的是值==比较引用类型比较的是内存地址
equlas是Object类的方法本质上与==一样但是有些类重写了equals方法比如String的equals被重写后比较的是字符值另外重写了equlas后也必须重写hashcode()方法
4.异常处理机制
1使用try、catch、finaly捕获异常finaly中的代码一定会执行捕获异常后程序会继续执行
2使用throws声明该方法可能会抛出的异常类型出现异常后程序终止
5.HashMap原理
1.HashMap在Jdk1.8以后是基于数组+链表+红黑树来实现的特点是key不能重复可以为null线程不安全
2.HashMap的扩容机制
HashMap的默认容量为16默认的负载因子为0.75当HashMap中元素个数超过容量乘以负载因子的个数时就创建一个大小为前一次两倍的新数组再将原来数组中的数据复制到新数组中。当数组长度到达64且链表长度大于8时链表转为红黑树
3.HashMap存取原理
1计算key的hash值然后进行二次hash根据二次hash结果找到对应的索引位置
2如果这个位置有值先进性equals比较若结果为true则取代该元素若结果为false就使用高低位平移法将节点插入链表JDK8以前使用头插法但是头插法在并发扩容时可能会造成环形链表或数据丢失而高低位平移发会发生数据覆盖的情况
6.想要线程安全的HashMap怎么办
1使用ConcurrentHashMap
2使用HashTable
3Collections.synchronizedHashMap()方法
7.ConcurrentHashMap原如何保证的线程安全
JDK1.7:使用分段锁将一个Map分为了16个段每个段都是一个小的hashmap每次操作只对其中一个段加锁
JDK1.8:采用CAS+Synchronized保证线程安全每次插入数据时判断在当前数组下标是否是第一次插入是就通过CAS方式插入然后判断f.hash是否=-1是的话就说明其他线程正在进行扩容当前线程也会参与扩容删除方法用了synchronized修饰保证并发下移除元素安全
8.HashTable与HashMap的区别
1HashTable的每个方法都用synchronized修饰因此是线程安全的但同时读写效率很低
2HashTable的Key不允许为null
3HashTable只对key进行一次hashHashMap进行了两次Hash
4HashTable底层使用的数组加链表
9.ArrayList和LinkedList的区别
ArratList的底层使用动态数组默认容量为10当元素数量到达容量时生成一个新的数组大小为前一次的1.5倍然后将原来的数组copy过来因为数组在内存中是连续的地址所以ArrayList查找数据更快由于扩容机制添加数据效率更低
LinkedList的底层使用链表在内存中是离散的没有扩容机制LinkedList在查找数据时需要从头遍历所以查找慢但是添加数据效率更高
10.如何保证ArrayList的线程安全
1使用collentions.synchronizedList方法为ArrayList加锁
2使用VectorVector底层与Arraylist相同但是每个方法都由synchronized修饰速度很慢
3使用juc下的CopyOnWriterArrayList该类实现了读操作不加锁写操作时为list创建一个副本期间其它线程读取的都是原本list写操作都在副本中进行写入完成后再将指针指向副本。
11.String、StringBuffer、StringBuilder的区别
String 由 char[] 数组构成使用了 final 修饰对 String 进行改变时每次都会新生成一个 String 对象然后把指针指向新的引用对象。
StringBuffer可变并且线程安全
StringBuiler可变但线程不安全。
操作少量字符数据用 String单线程操作大量数据用 StringBuilder多线程操作大量数据用 StringBuffer。
12.hashCode和equals
hashCode()和equals()都是Obkect类的方法hashCode()默认是通过地址来计算hash码但是可能被重写过用内容来计算hash码equals()默认通过地址判断两个对象是否相等但是可能被重写用内容来比较两个对象
所以两个对象相等他们的hashCode和equals一定相等但是hashCode相等的两个对象未必相等
如果重写equals()必须重写hashCode()比如在HashMap中key如果是String类型String如果只重写了equals而没有重写hashcode的话则两个equals()比较为true的key因为hashcode不同导致两个key没有出现在一个索引上就会出现map中存在两个相同的key
13.面向对象和面向过程的区别
面向对象有封装、继承、多态性的特性所以相比面向过程易维护、易复用、易扩展但是因为类调用时要实例化所以开销大性能比面向过程低
4.深拷贝和浅拷贝
浅拷贝:浅拷贝只复制某个对象的引用而不复制对象本身新旧对象还是共享同一块内存
深拷贝:深拷贝会创造一个一摸一样的对象新对象和原对象不共享内存修改新对象不会改变原对对象。
15.多态的作用
多态的实现要有继承、重写父类引用指向子类对象。它的好处是可以消除类型之间的耦合关系增加类的可扩充性和灵活性。
16.什么是反射
反射是通过获取类的class对象然后动态的获取到这个类的内部结构动态的去操作类的属性和方法。
应用场景有要操作权限不够的类属性和方法时、实现自定义注解时、动态加载第三方jar包时、按需加载类节省编译和初始化时间
获取class对象的方法有class.forName(类路径)类.class()对象的getClass
17.Java创建对象得五种方式?
(1)new关键字 (2)Class.newInstance (3)Constructor.newInstance
(4)Clone方法 (5)反序列化
二.Java多线程篇
1.进程和线程的区别进程间如何通信
进程系统运行的基本单位进程在运行过程中都是相互独立但是线程之间运行可以相互影响。
线程独立运行的最小单位一个进程包含多个线程且它们共享同一进程内的系统资源
进程间通过管道、 共享内存、信号量机制、消息队列通信
2. 什么是线程上下文切换
当一个线程被剥夺cpu使用权时切换到另外一个线程执行
3.什么是死锁
死锁指多个线程在执行过程中因争夺资源造成的一种相互等待的僵局
4.死锁的必要条件
互斥条件同一资源同时只能由一个线程读取
不可抢占条件不能强行剥夺线程占有的资源
请求和保持条件请求其他资源的同时对自己手中的资源保持不放
循环等待条件在相互等待资源的过程中形成一个闭环
想要预防死锁只需要破坏其中一个条件即可比如使用定时锁、尽量让线程用相同的加锁顺序还可以用银行家算法可以预防死锁
5.Synchrpnized和lock的区别
1synchronized是关键字lock是一个类
2 synchronized在发生异常时会自动释放锁lock需要手动释放锁
3synchronized是可重入锁、非公平锁、不可中断锁lock的ReentrantLock是可重入锁可中断锁可以是公平锁也可以是非公平锁
4synchronized是JVM层次通过监视器实现的Lock是通过AQS实现的
6.什么是AQS锁?
AQS是一个抽象类可以用来构造锁和同步类如ReentrantLockSemaphoreCountDownLatchCyclicBarrier。
AQS的原理是AQS内部有三个核心组件一个是state代表加锁状态初始值为0一个是获取到锁的线程还有一个阻塞队列。当有线程想获取锁时会以CAS的形式将state变为1CAS成功后便将加锁线程设为自己。当其他线程来竞争锁时会判断state是不是0不是0再判断加锁线程是不是自己不是的话就把自己放入阻塞队列。这个阻塞队列是用双向链表实现的
可重入锁的原理就是每次加锁时判断一下加锁线程是不是自己是的话state+1释放锁的时候就将state-1。当state减到0的时候就去唤醒阻塞队列的第一个线程。
7.为什么AQS使用的双向链表
因为有一些线程可能发生中断 而发生中断时候就需要在同步阻塞队列中删除掉这个时候用双向链表方便删除掉中间的节点
8.有哪些常见的AQS锁
AQS分为独占锁和共享锁
ReentrantLock独占锁可重入可中断可以是公平锁也可以是非公平锁非公平锁就是会通过两次CAS去抢占锁公平锁会按队列顺序排队
Semaphore信号量:设定一个信号量当调用acquire()时判断是否还有信号有就获取一个信号量没有就阻塞等待其他线程释放信号量当调用release()时释放一个信号量唤醒阻塞线程。
应用场景允许多个线程访问某个临界资源时如上下车买卖票
CountDownLatch倒计数器:给计数器设置一个初始值当调用CountDown()时计数器减一当调用await() 时判断计数器是否归0不为0就阻塞直到计数器为0。
应用场景启动一个服务时主线程需要等待多个组件加载完毕之后再继续执行
CyclicBarrier循环栅栏:给计数器设置一个目标值,当调用await() 时会计数+1并判断计数器是否达到目标值未达到就阻塞直到计数器达到目标值
应用场景多线程计算数据最后合并计算结果的应用场景
9.sleep()和wait()的区别
(1)wait()是Object的方法sleep()是Thread类的方法
(2)wait()会释放锁sleep()不会释放锁
(3)wait()要在同步方法或者同步代码块中执行sleep()没有限制
(4)wait()要调用notify()或notifyall()唤醒,sleep()自动唤醒
10.yield()和join()区别
yield()调用后线程进入就绪状态
A线程中调用B线程的join() ,则B执行完前A进入阻塞状态
11.线程池七大参数
核心线程数线程池中的基本线程数量
最大线程数当阻塞队列满了之后逐一启动
最大线程的存活时间当阻塞队列的任务执行完后最大线长的回收时间
最大线程的存活时间单位
阻塞队列当核心线程满后后面来的任务都进入阻塞队列
线程工厂用于生产线程
任务拒绝策略阻塞队列满后拒绝任务有四种策略1抛异常2丢弃任务不抛异常3打回任务4尝试与最老的线程竞争
12.Java内存模型
JMMJava内存模型 屏蔽了各种硬件和操作系统的内存访问差异实现让Java程序在各平台下都能达到一致的内存访问效果它定义了JVM如何将程序中的变量在主存中读取
具体定义为所有变量都存在主存中主存是线程共享区域每个线程都有自己独有的工作内存线程想要操作变量必须从主从中copy变量到自己的工作区每个线程的工作内存是相互隔离的
由于主存与工作内存之间有读写延迟且读写不是原子性操作所以会有线程安全问题
13.保证并发安全的三大特性
原子性一次或多次操作在执行期间不被其他线程影响
可见性当一个线程在工作内存修改了变量其他线程能立刻知道
有序性JVM对指令的优化会让指令执行顺序改变有序性是禁止指令重排
14.volatile
保证变量的可见性和有序性不保证原子性。使用了 volatile 修饰变量后在变量修改后会立即同步到主存中每次用这个变量前会从主存刷新。
单例模式双重校验锁变量为什么使用 volatile 修饰 禁止 JVM 指令重排序new Object()分为三个步骤为实例对象分配内存用构造器初始化成员变量将实例对象引用指向分配的内存实例对象在分配内存后实才不为null。如果分配内存后还未初始化就先将实例对象指向了内存那么此时最外层的if会判断实例对象已经不等于null就直接将实例对象返回。而此时初始化还没有完成。
15.线程使用方式
(1)继承 Tread 类
(2)实现 Runnable 接口
(3)实现 Callable 接口带有返回值
(4)线程池创建线程
16.ThreadLocal原理
原理是为每个线程创建变量副本不同线程之间不可见保证线程安全。每个线程内部都维护了一个Mapkey为threadLocal实例value为要保存的副本。
但是使用ThreadLocal会存在内存泄露问题因为key为弱引用而value为强引用每次gc时key都会回收而value不会被回收。所以为了解决内存泄漏问题可以在每次使用完后删除value或者使用static修饰ThreadLocal可以随时获取value
17.什么是CAS锁
CAS锁可以保证原子性思想是更新内存时会判断内存值是否被别人修改过如果没有就直接更新。如果被修改就重新获取值直到更新完成为止。这样的缺点是
1只能支持一个变量的原子操作不能保证整个代码块的原子操作
2CAS频繁失败导致CPU开销大
3ABS问题:线程1和线程2同时去修改一个变量将值从A改为B但线程1突然阻塞此时线程2将A改为B,然后线程3又将B改成A,此时线程1将A又改为B,这个过程线程2是不知道的这就是ABA问题可以通过版本号或时间戳解决
18.Synchronized锁原理和优化
Synchronize是通过对象头的markwordk来表明监视器的监视器本质是依赖操作系统的互斥锁实现的。操作系统实现线程切换要从用户态切换为核心态成本很高此时这种锁叫重量级锁在JDK1.6以后引入了偏向锁、轻量级锁、重量级锁
偏向锁当一段代码没有别的线程访问此时线程去访问会直接获取偏向锁
轻量级锁当锁是偏向锁时有另外一个线程来访问会升级为轻量级锁。线程会通过CAS方式获取锁不会阻塞提高性能
重量级锁轻量级锁自旋一段时间后线程还没有获取到锁会升级为重量级锁重量级锁时来竞争锁的所有线程都会阻塞性能降低
注意锁只能升级不能降级
19.如何根据 CPU 核心数设计线程池线程数量
IO 密集型线程中十分消耗Io的线程数*2
CPU密集型 cpu线程数量
20.AtomicInteger的使用场景
AtomicInteger是一个提供原子操作的Integer类使用CAS+volatile实来现线程安全的数值操作。
因为volatile禁止了jvm的排序优化,所以它不适合在并发量小的时候使用只适合在一些高并发程序中使用
三.JVM篇
1.JVM运行时数据区内存结构
线程私有区
1虚拟机栈每次调用方法都会在虚拟机栈中产生一个栈帧每个栈帧中都有方法的参数、局部变量、方法出口等信息方法执行完毕后释放栈帧
2本地方法栈为native修饰的本地方法提供的空间在HotSpot中与虚拟机合二为一
3程序计数器保存指令执行的地址方便线程切回后能继续执行代码
线程共享区
4堆内存Jvm进行垃圾回收的主要区域存放对象信息分为新生代和老年代内存比例为12新生代的Eden区内存不够时时发生MinorGC,老年代内存不够时发生FullGC
5方法区存放类信息、静态变量、常量、运行时常量池等信息。JDK1.8之前用持久代实现JDK1.8后用元空间实现元空间使用的是本地内存而非在JVM内存结构中
2.什么情况下会内存溢出
堆内存溢出1当对象一直创建而不被回收时2加载的类越来越多时3)虚拟机栈的线程越来越多时
栈溢出方法调用次数过多一般是递归不当造成
3.JVM有哪些垃圾回收算法
1标记清除算法 标记不需要回收的对象然后清除没有标记的对象会造成许多内存碎片。
2复制算法 将内存分为两块只使用一块进行垃圾回收时先将存活的对象复制到另一块区域然后清空之前的区域。用在新生代
3标记整理算法 与标记清除算法类似但是在标记之后将存活对象向一端移动然后清除边界外的垃圾对象。用在老年代
4.GC如何判断对象可以被回收
1引用计数法已淘汰为每个对象添加引用计数器引用为0时判定可以回收会有两个对象相互引用无法回收的问题
2可达性分析法从GCRoot开始往下搜索搜索过的路径称为引用链若一个对象GCRoot没有任何的引用链则判定可以回收
GCRoot有虚拟机栈中引用的对象方法区中静态变量引用的对象本地方法栈中引用的对象
5.典型垃圾回收器
CMS:以最小的停顿时间为目标、只运行在老年代的垃圾回收器使用标记-清除算法可以并发收集。
G1 JDK1.9以后的默认垃圾回收器注重响应速度支持并发采用标记整理+复制算法回收内存使用可达性分析法来判断对象是否可以被回收。
6.类加载器和双亲委派机制
从父类加载器到子类加载器分别为
BootStrapClassLoader 加载路径为JAVA_HOME/jre/lib
ExtensionClassLoader 加载路径为JAVA_HOME/jre/lib/ext
ApplicationClassLoader 加载路径为classpath
还有一个自定义类加载器
当一个类加载器收到类加载请求时会先把这个请求交给父类加载器处理若父类加载器找不到该类再由自己去寻找。该机制可以避免类被重复加载还可以避免系统级别的类被篡改
7.JVM中有哪些引用
强引用new的对象。哪怕内存溢出也不会回收
软引用只有内存不足时才会回收
弱引用每次垃圾回收都会回收
虚引用必须配合引用队列使用一般用于追踪垃圾回收动作
8.类加载过程
1加载 把字节码通过二进制的方式转化到方法区中的运行数据区
2连接
验证验证字节码文件的正确性。
准备正式为类变量在方法区中分配内存并设置初始值final类型的变量在编译时已经赋值了
解析将常量池中的符号引用如类的全限定名解析为直接引用类在实际内存中的地址
3初始化 执行类构造器不是常规的构造方法为静态变量赋初值并初始化静态代码块。
9.JVM类初始化顺序
父类静态代码块和静态成员变量->子类静态代码块和静态成员变量->父类代码块和普通成员变量->父类构造方法->子类代码块和普成员变量->子类构造方法
10..对象的创建过程
1检查类是否已被加载没有加载就先加载类
2为对象在堆中分配内存使用CAS方式分配防止在为A分配内存时执行当前地址的指针还没有来得及修改对象B就拿来分配内存。
3初始化将对象中的属性都分配0值或null
4设置对象头
5为属性赋值和执行构造方法
11.对象头中有哪些信息
对象头中有两部分一部分是MarkWork,存储对象运行时的数据如对象的hashcode、GC分代年龄、GC标记、锁的状态、获取到锁的线程ID等另外一部分是表明对象所属类如果是数组还有一个部分存放数组长度
12.JVM内存参数
-Xmx[]:堆空间最大内存
-Xms[]:堆空间最小内存一般设置成跟堆空间最大内存一样的
-Xmn[]:新生代的最大内存
-xx:[survivorRatio=3]:eden区与from+to区的比例为31默认为41
-xx[use 垃圾回收器名称]指定垃圾回收器
-xss:设置单个线程栈大小
一般设堆空间为最大可用物理地址的百分之80
13.GC的回收机制和原理
GC的目的实现内存的自动释放使用可达性分析法判断对象是否可回收采用了分代回收思想
将堆分为新生代、老年代新生代中采用复制算法老年代采用整理算法当新生代内存不足时会发生minorGC,老年代不足时会发送fullGC
四.Mysql篇
1.MyIsAm和InnoDB的区别
InnoDB有三大特性分别是事务、外键、行级锁这些都是MyIsAm不支持的
另外InnoDB是聚簇索引MyIAm是非聚簇索引
InnoDB不支持全文索引MyIAm支持
InnoDB支持自增和MVCC模式的读写MyIAm不支持
MyIsAM的访问速度一般InnoDB快差异在于innodb的mvcc、行锁会比较消耗性能还可能有回表的过程先去辅助索引中查询数据找到数据对应的key之后再通过key回表到聚簇索引树查找数据
2.mysql事务特性
原子性一个事务内的操作统一成功或失败
一致性事务前后的数据总量不变
隔离性事务与事务之间相互不影响
持久性事务一旦提交发生的改变不可逆
3.事务靠什么保证
原子性由undolog日志保证他记录了需要回滚的日志信息回滚时撤销已执行的sql
一致性由其他三大特性共同保证是事务的目的
隔离性由MVCC保证
持久性由redolog日志和内存保证mysql修改数据时内存和redolog会记录操作宕机时可恢复
4.事务的隔离级别
在高并发情况下并发事务会产生脏读、不可重复读、幻读问题这时需要用隔离级别来控制
读未提交 允许一个事务读取另一个事务已提交的数据可能出现不可重复读幻读。
读提交 只允许事务读取另一个事务没有提交的数据可能出现不可重复读幻读。
可重复读 确保同一字段多次读取结果一致可能出现欢幻读。
可串行化 所有事务逐次执行没有并发问日
Inno DB 默认隔离级别为可重复读级别分为快照度和当前读并且通过间隙锁解决了幻读问题。
5.什么是快照读和当前读
*快照读读取的是当前数据的可见版本可能是会过期数据不加锁的select就是快照都
*当前读读取的是数据的最新版本并且当前读返回的记录都会上锁保证其他事务不会并发修改这条记录。如update、insert、delete、select for undate排他锁、select lockin share mode共享锁 都是当前读
6.MVCC是什么
MVCC是多版本并发控制为每次事务生成一个新版本数据每个事务都由自己的版本从而不加锁就决绝读写冲突这种读叫做快照读。只在读已提交和可重复读中生效。
实现原理由四个东西保证他们是
undolog日志记录了数据历史版本
readView:事务进行快照读时动态生成产生的视图记录了当前系统中活跃的事务id控制哪个历史版本对当前事务可见
隐藏字段DB_TRC_ID 最近修改记录的事务ID
隐藏字段DB_Roll_PTR 回滚指针配合undolog指向数据的上一个版本
7.MySQL有哪些索引
主键索引一张表只能有一个主键索引主键索引列不能有空值和重复值
唯一索引唯一索引不能有相同值但允许为空
普通索引允许出现重复值
组合索引对多个字段建立一个联合索引减少索引开销遵循最左匹配原则
全文索引myisam引擎支持通过建立倒排索引提升检索效率广泛用于搜索引擎
8.聚簇索引和非聚簇索引的区别
聚簇索引聚簇索引的叶子节点存放的是主键值和数据行辅助索引在聚簇索引上创建的其它索引的叶子节点存放的是主键值或指向数据行的指针。
优点根据索引可以直接获取值所以他获取数据更快对于主键的排序查找和范围查找效率更高
缺点如果主键值很大的话辅助索引也会变得很大如果用uuid作为主键数据存储会很稀疏修改主键或乱序插入会让数据行移动导致页分裂所以一般我们定义主键时尽量让主键值小并且定义为自增和不可修改。
非聚簇索引辅助索引叶子节点存放的是数据行地址先根据索引找到数据地址再根据地址去找数据
他们都是b+数结构
9.MySQL如何做慢SQL优化
开启慢查询日志 SET GLOBAL long_query_time=阈值;超过阈值的sql就会记录到慢查询日志当中或查看执行计划explain+SQL。慢查询优化如下
1分析sql语句是否加载了不需要的数据列
2分析sql执行计划字段有没有索引索引是否失效是否用对索引
3 使用复杂查询时尽量使用关联查询来代替子查询并且最好使用内连接
4orderby查找时使用索引进行排序否则的话需要进行回表然后在排序缓冲区中进行排序。
5groupby查询时同样要建立联合索引避免使用到临时表
6分页查询时如果偏移量太大比如要查询一百万条数据后的十条记录可以使用主键+子查询的方式避免进行全表扫描
7使用count函数时直接使用count的话count(*)的效率最高也可以额外创建一张表去统计不同表中的数据行数但维护麻烦
count(*)或count(唯一索引)或count(数字):表中总记录数count(字段)不会统计null
(8) 在写update语句时where条件要添加使用索引否则会锁会从行锁升级为表锁
9表中数据是否太大是不是要分库分表
10.为什么要用内连接而不用外连接
用外连接的话连接顺序是固定死的比如left join他必须先对左表进行全表扫描然后一条条到右表去匹配而内连接的话mysql会自己根据查询优化器去判断用哪个表做驱动。
子查询的话同样也会对驱动表进行全表扫描所以尽量用小表做驱动表。
11.MySQL整个查询的过程
1客户端向 MySQL 服务器发送一条查询请求
2服务器首先检查查询缓存如果命中缓存则返回存储在缓存中的结果。否则进入下一阶段
3服务器进行 SQL 解析、预处理、再由优化器生成对应的执行计划
4MySQL 根据执行计划调用存储引擎的 API 来执行查询
5将结果返回给客户端同时缓存查询结果
注意只有在8.0之前才有查询缓存8.0之后查询缓存被去掉了
12.执行计划中有哪些字段
我们想看一个sql的执行计划使用的语句是explain+SQL表中的字段包括
id:select查询的优先级id越大优先级越高子查询的id一般会更大
select_type:查询的类型,是普通查询还是联合查询还是子查询常见类型有simple不包含子查询primary标记复杂查询中最外层的查询union(标记primart只后子查询)
table者一行的数据是数哪张表的
type:扫描类型效率从底到高为
ALL全表扫描)>index(全索引扫描我们的需要的数据在索引中可以获取)>range(范围索引扫描)>ref(使用非唯一索引列进行了关联查询)> eq_ref (使用唯一索引进行关联查询)>const(查询结果只有一行体现在使用唯一索引进行单表查询)>system(表中只有一行数据)
possible_keys可能的:当前查询语句可能用到的索引可能为null(如果用了索引但是为null有可能是表数据太少innodb认为全表扫描更快)
key:实际使用到的索引
ref编号:显示索引的哪一行被使用了
rows:估算大概多少行数据被查找了
excess额外的:mysql如何查询额外信息常见的有
Using filesort:排序时使用外部排序而不是索引排序需要回表查询数据然后在排序缓冲区中进行排序
backward index scan:排序时使用了索引排序但如果是按照降序排序的话就会使用反向扫描索引
Using index:表示使用覆盖索引覆盖索引如果key中显示有索引select后面查询的字段都可以从通过这个索引获取
Using temporary:查询时要建立一个临时表存放数据
13.哪些情况索引会失效
1where条件中有or除非所有查询条件都有索引否则失效
2like查询用%开头索引失效
3索引列参与计算索引失效
4违背最左匹配原则索引失效
5索引字段发生类型转换索引失效
6mysql觉得全表扫描更快时数据少索引失效
14.B和B+数的区别为什么使用B+数
二叉树索引字段有序极端情况会变成链表形式
AVL数树的高度不可控
B数控制了树的高度但是索引值和data都分布在每个具体的节点当中若要进行范围查询要进行多次回溯IO开销大
B+树非叶子节点只存储索引值叶子节点再存储索引+具体数据从小到大用链表连接在一起范围查询可直接遍历不需要回溯7
15.MySQL有哪些锁
基于粒度
*表级锁对整张表加锁粒度大并发小
*行级锁对行加锁粒度小并发大
*间隙锁间隙锁锁住表的一个区间间隙锁之间不会冲突只在可重复读下才生效解决了幻读
基于属性
*共享锁又称读锁一个事务为表加了读锁其它事务只能加读锁不能加写锁
*排他锁又称写锁一个事务加写锁之后其他事务不能再加任何锁避免脏读问题
16.Mysql内连接、左连接、右连接的区别
内连接取量表交集部分左连接取左表全部右表匹部分右连接取右表全部坐表匹部分
17.sql执行顺序
我单独写了一篇文章 http://t.csdn.cn/6a5Y3
18.如何设计数据库
1抽取实体如用户信息商品信息评论
2分析其中属性如用户信息姓名、性别...
3分析表与表之间的关联关系
然后可以参考三大范式进行设计设计主键时主键要尽量小并且定义为自增和不可修改。
19.where和having的区别
where是约束声明having是过滤声明where早于having执行并且where不可以使用聚合函数having可以
20.三大范式
第一范式每个列都不可以再拆分。
第二范式在第一范式的基础上非主键列完全依赖于主键而不能是依赖于主键的一部分。
第三范式在第二范式的基础上非主键列只依赖于主键不依赖于其他非主键。
21.char和varchar的区别
char是不可变的最大长度为255varchar是可变的字符串最大长度为2^16
22.InnoDB 什么情况下会产生死锁
事务1已经获取数据A的写锁想要去获取数据B的写锁然后事务2获取了B的写锁想要去获取A的写锁相互等待形成死锁。
mysql解决死锁的机制有两个1.等待 直到超时 2.发起死锁检测主动回滚一条事务
死锁检测的原理是构建一个以事务为顶点、 锁为边的有向图 判断有向图是否存在环 存在即有死锁。
我们平时尽量减少事务操作的资源和隔离级别
23.MySQL 删除自增 id随后重启 MySQL 服务再插入数据自增 id 会从几开始
innodb 引擎
MySQL8.0前下次自增会取表中最大 id + 1。原理是最大id会记录在内存中重启之后会重新读取表中最大的id
MySQL8.0后仍从删除数据 id 后算起。原理是它将最大id记录在redolog里了
myisam
自增的 id 都从删除数据 id 后算起。原理是它将最大id记录到数据文件里了
24.MySQL插入百万级的数据如何优化
1一次sql插入多条数据可以减少写redolog日志和binlog日志的io次数sql是有长度限制的但可以调整
2保证数据按照索引进行有序插入
3可以分表后多线程插入
五.常用开发框架系列
1.什么是Spring
Spring是个轻量级的框架通过IOC达到松耦合的目的通过AOP可以分离应用业务逻辑和系统服务进行内聚性的开发不过配置各种组件时比较繁琐所以后面才出选了SpringBoot的框架。
2.IOC是什么
IOC是控制反转是一种思想把对象的创建和调用从程序员手中交由IOC容器管理降低对象之间的依赖关系。
创建一个bean的方式有xml方式、@Bean注解方式、@Componte方式
我们在对一个bean进行实例化后要对他的属性进行填充大多数我们都是使用 @Autowire直接的填充依赖注入的他是有限按照类型进行匹配。
3.AOP是什么
AOP是面向切面编程可以将那些与业务不相关但是很多业务都要调用的代码抽取出来思想就是不侵入原有代码的情况下对功能进行增强。
SpringAOP是基于动态代理实现的动态代理是有两种一种是jdk动态代理一种是cglib动态代理;
jdk动态代理是原理是利用反射来实现的需要调用反射包下的Proxy类的newProxyInstance方法来返回代理对象这个方法中有三个参数分别是用于加载代理类的类加载器被代理类实现的接口的class数组和一个用于增强方法的InvocaHandler实现类。
cglib动态代理原理是利用asm开源包来实现的是把被代理类的class文件加载进来通过修改它的字节码生成子类来处理
jdk动态代理要求被代理类必须有实现的接口生成的动态代理类会和代理类实现同样的接口cglib则生成的动态代理类会继承被代理类。Spring默认使用jdk动态代理当被代理的类没有接口时就使用cglib动态代理
4.如何定义一个全局异常处理类
想要定义一个全局异常处理类的话我们需要在这个类上添加@ContaollerAdvice注解然后定义一些用于捕捉不同异常类型的方法在这些方法上添加@ExceptionHandler(value = 异常类型.class)和@ResponseBody注解方法参数是HttpServletRequest和异常类型然后将异常消息进行处理。
如果我们需要自定义异常的话就写一个自定义异常类该类需要继承一个异常接口类属性包括final类型的连续id、错误码、错误信息再根据需求写构造方法
5.如何使用aop自定义日志
第一步创建一个切面类把它添加到ioc容器中并添加@Aspect注解
第二步 在切面类中写一个通知方法在方法上添加通知注解并通过切入点表达式来表示要对哪些方法进行日志打印然后方法参数为JoinPoint
第三步通过JoinPoint这个参数可以获取当前执行的方法名、方法参数等信息这样就可以根据需求在方法进入或结束时打印日志
6.循环依赖是什么怎么解决的
循环依赖就是在创建 A 实例的时候里面包含着 B 属性实例所以这个时候就需要去创建 B 实例而创 建 B 实例过程中也包含着 A 实例。 这样 A 实例还在创建的过程当中所以就导致 A 和 B 实例都创建不出来。
spring通过三级缓存来解决循环依赖
一级缓存缓存经过完整的生命周期的Bean
二级缓存 缓存未经过完整的生命周期的Bean
三级缓存缓存的是ObjectFactory其中存储了一个生成代理类的拉姆达表达式
我们在创建 A 的过程中先将 A 放入三级缓存 这时要创建BB要创建A就直接去三级缓存中查找并且判断需不需要进行 AOP 处理如果需要就执行拉姆达表达式得到代理对象不需要就取出原始对象。然后将取出的对象放入二级缓存中因为这个时候 A 还未经 过完整的生命周期所以不能放入一级缓存。这个时候其他需要依赖 A 对象的直接从二级缓存中去获取即可。当B创建完成A 继续执行生命周期当A完成了属性的注入后就可以放入一级缓存了
7.Bean 的作用域
1Singleton:一个IOC容器只有一个
2Prototype:每次调用getBean()都会生成一个新的对象
3request:每个http请求都会创建一个自己的bean
4session:同一个session共享一个实例
5application:整个serverContext只有一个bean
6webSocket:一个websocket只有一个bean
8.Bean 生命周期
实例化 Instantiation->属性赋值 Populate->初始化 Initialization->销毁 Destruction
在这四步的基础上面Spring 提供了一些拓展点
*Bean 自身的方法: 包括了 Bean 本身调用的方法和通过配置文件中的 init-method 和 destroy-method 指定的方法
*Bean 级生命周期接口方法:包括了 BeanNameAware、BeanFactoryAware、InitializingBean 和 DiposableBean 这些接口的方法
*容器级生命周期接口方法:包括了 InstantiationAwareBeanPostProcessor 和 BeanPostProcessor 这两个接口实现一般称它们的实现类为“后处理器”。
*工厂后处理器接口方法: 包括了 AspectJWeavingEnabler, ConfigurationClassPostProcessor, CustomAutowireConfigurer 等等非常有用的工厂后处理器接口的方法。工厂后处理器也是容器级的。在应用上下文装配配置文件之后立即调用。
9.Spring 事务原理
spring事务有编程式和声明式我们一般使用声明式在某个方法上增加@Transactional注解这个方法中的sql会统一成功或失败。
原理是
当一个方法加上@Transactional注解spring会基于这个类生成一个代理对象并将这个代理对象作为bean当使用这个bean中的方法时如果存在@Transactional注解就会将事务自动提交设为false然后执行方法执行过程没有异常则提交有异常则回滚、
10.spring事务失效场景
1事务方法所在的类没有加载到容器中
2事务方法不是public类型
3同一类中一个没有添加事务的方法调用另外以一个添加事务的方法事务不生效
4spring事务默认只回滚运行时异常可以用rollbackfor属性设置
5业务自己捕获了异常事务会认为程序正常秩序
11.spring事务的隔离级别
default:默认级别使用数据库自定义的隔离级别
其它四种隔离级别与mysql一样
12.spring事务的传播行为
1支持当前事务如果不存在则新启一个事务
2支持当前事务如果不存在则抛出异常
3支持当前事务如果不存在则以非事务方式执行
4不支持当前事务创建一个新事物
5不支持当前事务如果已存在事务就抛异常
6不支持当前事务始终以非事务方式执行
13.Spring IoC
12.spring用了哪些设计模式
BeanFactory用了工厂模式AOP用了动态代理模式RestTemplate用来模板方法模式SpringMVC中handlerAdaper用来适配器模式Spring里的监听器用了观察者模式
14.SpringMV工作原理
SpringMVC工作过程围绕着前端控制器DispatchServerlet几个重要组件有HandleMapping处理器映射器、HandleAdapter处理器适配器、ViewReslover试图解析器
工作流程
1DispatchServerlet接收用户请求将请求发送给HandleMapping
2HandleMapping根据请求url找到具体的handle和拦截器返回给DispatchServerlet
3DispatchServerlet调用HandleAdapter,HandleAdapter执行具体的controller并将controller返回的ModelAndView返回给DispatchServler
4DispatchServerlet将ModelAndView传给ViewReslover,ViewReslover解析后返回具体view
5DispatchServerlet根据view进行视图渲染返回给用户
15.springboot自动配置原理
启动类@SpringbootApplication注解下有三个关键注解
1@springbootConfiguration:表示启动类是一个自动配置类
2@CompontScan:扫描启动类所在包外的组件到容器中
3@EnableConfigutarion:最关键的一个注解他拥有两个子注解其中@AutoConfigurationpackageu会将启动类所在包下的所有组件到容器中@Import会导入一个自动配置文件选择器他会去加载META_INF目录下的spring.factories文件这个文件中存放很大自动配置类的全类名这些类会根据元注解的装配条件生效生效的类就会被实例化加载到ioc容器中
16.springboot常用注解
@RestController 修饰类该控制器会返回Json数据
@RequestMapping("/path") 修饰类该控制器的请求路径
@Autowired : 修饰属性按照类型进行依赖注入
@PathVariable : 修饰参数将路径值映射到参数上
@ResponseBody :修饰方法该方法会返回Json数据
@RequestBody需要使用Post提交方式 :修饰参数将Json数据封装到对应参数中
@Controller@Service@Compont: 将类注册到ioc容器
@Transaction开启事务
17.spring的bean是线程安全的吗
spring的默认bean作用域是单例的单例的bean不是线程安全的但是开发中大部分的bean都是无状态的不具备存储功能比如controller、service、dao他们不需要保证线程安全。
如果要保证线程安全可以将bean的作用域改为prototype比如像Model View。
另外还可以采用ThreadLocal来解决线程安全问题。ThreadLocal为每个线程保存一个副本变量每个线程只操作自己的副本变量。
18.springcloud主要解决什么问题
解决服务之间的通信、容灾、负载平衡、冗余问题能方便服务集中管理常用组件有注册中心、配置中心、远程调用。服务熔断、网关
19.CAP理论
C一致性这里指的强一致性也就是数据更新完访问任何节点看到的数据完全一致
A可用性就是任何没有发生故障的服务必须在规定时间内返回合正确结果
P容灾性当网络不稳定时节点之间无法通信造成分区这时要保证系统可以继续正常服务。提高容灾性的办法就是把数据分配到每一个节点当中所以P是分布式系统必须实现的然后需要在C和A中取舍
20.为什么不能同时保证一致性和可用性呢
当网络发生故障时如果要保障数据一致性那么节点相互间就只能阻塞等待数据真正同步时再返回就违背可用性了。如果要保证可用性节点要在有限时间内将结果返回无法等待其它节点的更新消息此时返回的数据可能就不是最新数据就违背了一致性了
21.熔断限流的理解
SprngCloud中用Hystrix组件来进行降级、熔断、限流
熔断是对于消费者来讲当对提供者请求时间过久时为了不影响性能就对链接进行熔断
限流是对于提供者来讲为了防止某个消费者流量太大导致其它更重要的消费者请求无法及时处理。限流可用通过拒绝服务、服务降级、消息队列延时处理、限流算法来实现
22.常用限流算法
计数器算法使用redis的setnx和过期机制实现
漏桶算法一般使用消息队列来实现系统以恒定速度处理队列中的请求当队列满的时候开始拒绝请求。
令牌桶算法计数器算法和漏桶算法都无法解决突然的大并发令牌桶算法是预先往桶中放入一定数量token然后用恒定速度放入token直到桶满为止所有请求都必须拿到token才能访问系统
六.Redis系列
1.redis为什么快
1完全基于内存操作
2数据结构简单对数据操作简单
3redis执行命令是单线程的避免了上下文切换带来的性能问题也不用考虑锁的问题
(4) 采用了非阻塞的io多路复用机制使用了单线程来处理并发的连接;内部采用的epoll+自己实现的事件分离器
其实Redis不是完全多线程的在核心的网络模型中是多线程的用来处理并发连接但是数据的操作都是单线程。Redis坚持单线程是因为Redis是的性能瓶颈是网络延迟而不是CPU多线程对数据读取不会带来性能提升。
2.redis持久化机制
1快照持久化RDB
redis的默认持久化机制通过父进程fork一个子进程子进程将redis的数据快照写入一个临时文件等待持久化完毕后替换上一次的rdb文件。整个过程主进程不进行任何的io操作。持久化策略可以通过save配置单位时间内执行多少次操作触发持久化。所以RDB的优点是保证redis性能最大化恢复速度数据较快缺点是可能会丢失两次持久化之间的数据
2追加持久化AOF
以日志形式记录每一次的写入和删除操作策略有每秒同步、每次操作同步、不同步优点是数据完整性高缺点是运行效率低恢复时间长
3.Redis如何实现key的过期删除
采用的定期过期+惰性过期
定期删除 Redis 每隔一段时间从设置过期时间的 key 集合中随机抽取一些 key 检查是否过期如果已经过期做删除处理。
惰性删除 Redis 在 key 被访问的时候检查 key 是否过期如果过期则删除。
4.Redis数据类型
String 常用命令 set,get,decr,incr,mget等
Hash 常用命令 hget,hset,hgetall 等
List 常用命令 lpush,rpush,lpop,rpop,lrange 等
Set 常用命令 sadd,spop,smembers,sunion 等
SortSet 常用命令 zadd,zrange,zrem,zcard 等
5.Redis缓存穿透如何解决
缓存穿透是指频繁请求客户端和缓存中都不存在的数据缓存永远不生效请求都到达了数据库。
解决方案
1在接口上做基础校验比如id<=0就拦截
2缓存空对象找不到的数据也缓存起来并设置过期时间可能会造成短期不一致
3布隆过滤器在客户端和缓存之间添加一个过滤器拦截掉一定不存在的数据请求
6.Redis如何解决缓存击穿
缓存击穿是值一个key非常热点key在某一瞬间失效导致大量请求到达数据库
解决方案
1设置热点数据永不过期
2给缓存重建的业务加上互斥锁缺点是性能低
7.Redis如何解决缓存雪崩
缓存雪崩是值某一时间Key同时失效或redis宕机导致大量请求到达数据库
解决方案
1搭建集群保证高可用
2进行数据预热给不同的key设置随机的过期时间
3给缓存业务添加限流降级通过加锁或队列控制操作redis的线程数量
4给业务添加多级缓存
8.Redis分布式锁的实现原理
原理是使用setnx+setex命令来实现但是会有一系列问题
1任务时常超过缓存时间锁自动释放。可以使用Redision看门狗解决
2加锁和释放锁的不是同一线程。可以在Value中存入uuid删除时进行验证。但是要注意验证锁和删除锁也不是一个原子性操作可以用lua脚本使之成为原子性操作
3不可重入。可以使用Redision解决实现机制类似AQS,计数
4redis集群下主节点宕机导致锁丢失。使用红锁解决
9.Redis集群方案
(1)主从模式个master节点多个slave节点master节点宕机slave自动变成主节点
(2)哨兵模式在主从集群基础上添加哨兵节点或哨兵集群用于监控master节点健康状态通过投票机制选择slave成为主节点
(3)分片集群主从模式和哨兵模式解决了并发读的问题但没有解决并发写的问题因此有了分片集群。分片集群有多个master节点并且不同master保存不同的数据master之间通过ping相互监测健康状态。客户端请求任意一个节点都会转发到正确节点因为每个master都被映射到0-16384个插槽上集群的key是根据key的hash值与插槽绑定
10.Redis集群主从同步原理
主从同步第一次是全量同步slave第一次请求master节点会根据replid判断是否是第一次同步是的话master会生成RDB发送给slave。
后续为增量同步在发送RDB期间会产生一个缓存区间记录发送RDB期间产生的新的命令,slave节点在加载完后会持续读取缓存区间中的数据
11.Redis缓存一致性解决方案
Redis缓存一致性解决方案主要思考的是删除缓存和更新数据库的先后顺序
先删除缓存后更新数据库存在的问题是可能会数据不一致一般使用延时双删来解决即先删除缓存再更新数据库休眠X秒后再次淘汰缓存。第二次删除可能导致吞吐率降低可以考虑进行异步删除。
先更新数据库后删除缓存存在的问题是会可能会更新失败可以采用延时删除。但由于读比写快发生这一情况概率较小。
但是无论哪种策略都可能存在删除失败的问题解决方案是用中间件canal订阅binlog日志提取需要删除的key然后另写一段非业务代码去获取key并尝试删除若删除失败就把删除失败的key发送到消息队列然后进行删除重试。
12.Redis内存淘汰策略
当内存不足时按设定好的策略进行淘汰策略有(1)淘汰最久没使用的2淘汰一段时间内最少使用的3淘汰快要过期的
七.计算机网络系列
1.TCP/IP模型
2.浏览器输入地址后做了什么
3.TCP三次握手
4.为什么TCP不能两次握手
假设是两次握手若客户端发起的连接请求阻塞在网络中会造成该报文的重传这时服务收到连接请求后会立刻进入连接状态当双方传输完数据结束连接后第一次阻塞的请求突然又到达了服务端此时服务端又进入连接状态而客户端不会响应服务端的连接确认报文
5.TCP四次挥手
6.为什么要进入时间等待状态
若客户端发送确认释放包后直接关闭而服务端因为某种原因没有收到客户端的确认释放包就会一直发送确认请求而客户端永远不会再响应该请求。
7.TCP 滑动窗口
TCP 流量控制主要使用滑动窗口协议滑动窗口是接受数据端使用的窗口大小用来告诉发送端接收端的缓存大小以此可以控制发送端发送数据的大小从而达到流量控制的目的。如果TCP发送方收到接收方的零窗口通知后会启动持续计时器。计时器超时后向接收方发送零窗口探测报文如果响应仍为0就重新计时不为0就打破死锁
8.TCP拥塞控制
发送方会维护一个拥塞窗口大小的状态变量大小取决于网络的拥塞程度。发送方的发送窗口大小是取接收方接收窗口和拥塞窗口中较小的一个
拥塞控制有四种算法
慢开始从小到大主键发送窗口每收到一个确认报文窗口大小指数增长
拥塞避免当窗口大小到达一定阈值时转为拥塞避免每收到一个确认报文窗口大小+1。若此时网络超时就把阈值调小一半重新慢开始
快重传要求接收方收到请求后要立即回复
快恢复发送方连续收到多个确认时就把拥塞避免阈值减小然后直接开始拥塞避免
9.TCP超时重传
发送方在发送按数据后一定时间内没有收到接收方响应报文就会重新发送刚刚的报文接收到收到报文后会对该报文的序列号进行检验已存在就抛弃
10.TCP可靠传输的实现
TCP是靠滑动窗口协议和连续ARQ协议配合流量控制和拥塞控制来保证的可靠传输。
ARQ是停止等待协议和自动重传请求它规定TCP要为每一次传输的包编号每发送一个包要等待对方确认后才能发送下一个分组若一段时间对方没有确认就重新发送刚刚的报文。接收方会对数据包排序把有序数据传给应用层返回缺失的第一个ACK确认序列号给发送方,接收到收到报文后会对该报文的序列号进行检验重复就丢弃。
流量控制是.....拥塞窗口上......上面已经说了
11.TCP报头有哪些信息
12.状态码
1xx:请求正在处理
2xx:请求成功处理
3xx:请求重定向 301永久重定向 302临时重定向 304使用本地缓存
4xx:客户端错误 400请求格式错误 403没有访问权限 415请求体过大
5xx:服务端错误
13.http与https的区别
单独写了一篇文章请移步 http://t.csdn.cn/iuF6O
14.socket通信流程
1服务端创建socket并调用bind()方法绑定ip和端口号
2服务端调用listen()方法建立监听此时服务的scoket还没有打开
3客户端创建socket并调用connect()方法像服务端请求连接
4服务端socket监听到客户端请求后被动打开调用accept()方法接收客户端连接请求当accept()方法接收到客户端connect()方法返回的响应成功的信息后连接成功
5客户端向socket写入请求信息服务端读取信息
6客户端调用close()结束链接服务端监听到释放连接请求后也结束链接
八.linux系列
1.linux常用命令
ifconfig:查看网络接口详情
ping查看与某主机是否能联通
ps -ef|grep 进程名称查看进程号
lost -i 端口 查看端口占用情况
top:查看系统负载情况包括系统时间、系统所有进程状态、cpu情况
free:查看内存占用情况
kill:正常杀死进程发出的信号可能会被阻塞
kill -9:强制杀死进程发送的是exit命令不会被阻塞
2.linux的io模型
IO是对磁盘或网络数据的读写用户进程读取一次IO请求分为两个阶段等待数据到达内核缓冲区和将内核缓冲区数据拷贝到用户空间当用户去内核中拷贝数据时要从用户态转为核心态
5中io模型:
(1)同步阻塞IO模型
用户进程发起io调用后会被阻塞等待内核缓冲区数据准备完毕时就被唤醒将内核数据复制到用户进程。这两个阶段都是阻塞的
(2)同步非阻塞IO模型
用户进程发起IO调用后若内核缓冲区数据还未准备好进程会继续干别的事每隔一段时间就去看看内核数据是否准备好。不过将内核数据复制到用户进程这个阶段依旧是阻塞的
(3)IO多路复用模型
linux中把一切都看成文件每个文件都有一个文件描述符FD来关联 IO多路复用模型就是复用单个进程同时监测多个文件描述符当某个文件描述符可读或可写就去通知用户进程。
(4)信号IO模型
用户进程发起IO调用后会向内核注册一个信号处理函数然后继续干别的事当内核数据准备就绪时就通知用户进程来进行拷贝。
5异步非阻塞模型
前面四种全是同步的。进程在发起IO调用后会直接返回结果。待内核数据准备好时由内核将数据复制给用户进程。两个阶段都是非阻塞的
4.IO多路复用详解
linux中把一切都看成文件每个文件都有一个文件描述符FD来关联 IO多路复用模型就是复用单个进程同时监测多个文件描述符当某个文件描述符可读或可写就去通知用户进程。IO多路复用有三种方式
1select采用数组结构监测的fd有限默认为1024当有文件描述符就绪时需要遍历整个FD数组来查看是哪个文件描述符就绪了效率较低每次调用select时都需要把整个文件描述符数组从用户态拷贝到内核态中来回拷贝当fd很多时开销会很大
2poll采用链表结构监测的文件描述符没有上限其它的根select差不多
3epoll采用红黑树结构监测的fd没有上限它有三个方法epoll_create() 用于创建一个epoll实例epoll实例中有一颗红黑树记录监测的fd一个链表记录就绪的fd;epoll_ctl() 用于往epoll实例中增删要监测的文件描述符并设置回调函数当文件描述符就绪时触发回调函数将文件描述符添加到就绪链表当中epoll_wait() 用于见擦汗就绪列表并返回就绪列表的长度然后将就绪列表的拷贝到用户空间缓冲区中。
所以epoll的优点是当有文件描述符就绪时只把已就绪的文件描述符写给用户空间不需要每次都遍历FD集合每个FD只有在调新增的时候和就绪的时候才会在用户空间和内核空间之间拷贝一次。
5.epoll的LT和ET模式
LT默认水平触发当FD有数据可读的时候那么每次 epoll_wait都会去通知用户来操作直到读完
ET边缘触发当FD有数据可读的时候它只会通知用户一次直到下次再有数据流入才会再通知所以在ET模式下一定要把缓冲区的数据一次读完
九.场景题
1.Java如何实现统计在线人数的功能
博主写了另外一篇文章http://t.csdn.cn/Q1S4h
2.电商网站可以分成哪些模块或订单模块要完成哪些功能
用户模块用户账户、会员等级、收货信息、订单模块订单编号、类型信息、状态信息、时间信息等、商品模块店铺信息、数量、价格等、支付模块支付方式、支付时间、支付单号等、物流模块物流公司、物流单号、物流状态等
十.其他RabitMQ、数据结构与算法、nginx、git、jwt登录等...
1.RabbitMQ如何保证消息不丢失
发送端
1创建一个消息状态表开启RabbitMQ的confirm机制当收到MQ回传的ACK以更新消息状态
2开启定时任务隔断时间重新发送状态表中超时的任务多次投递失败进行报警。
消费端
1为了避免消息重复消费要增加一张消息处理表消费者拿到消息时判断消息处理表中当前消息是否已经存在已经存在就抛弃不存在就进行消费并放入记录表消费和放入记录表要放在一个事务当中。
2开启手动ack模式在消费者处理完业务后才返回ACK避免消息还没有处理完就ACK。
具体可以看博主另外一篇文章(152条消息) RabbitMQ消息丢失的场景如何保证消息不丢失详细讲解一文看懂_十八岁讨厌Java的博客-CSDN博客
2.RabbitMQ如何保证消费顺序
RabbitMQ消费顺序乱了是因为消费者集群拿到消息后对消息处理速度不同导致的比如可能将增删改变成了增改删。
解决方法为RabbitMQ创建多个Queue,每个消费者只监听其中一个Queue,同一类型的消息都放在一个queue中同一个 queue 的消息是一定会保证有序的。
3.设计模式六大原则
1单一职责原则一个类或者一个方法只负责一项职责尽量做到类只有一个行为引起变化
2里氏替换原则子类可以扩展父类的功能但不能改变原有父类的功能
3依赖倒置原则高层模块不应该依赖底层模块两者都应该依赖接口或抽象类
4接口隔离原则建立单一接口尽量细化接口
5迪米特原则只关心其它对象能提供哪些方法不关心过多内部细节
6开闭原则对于拓展是开放对于修改是封闭的
4.设计模式分类
创建型模式主要是描述对象的创建代表有单例、原型模式、工厂方法、抽象工厂、建造者模式
结构型模式主要描述如何将类或对象按某种布局构成更大的结构代表有代理、适配器、装饰
行为型模式描述类或对象之间如何相互协作共同完成单个对象无法完成的任务代表有模板方法模式、策略模式、观察者模式、备忘录模式
5.排序算法的时间复杂度
交换排序冒泡排序n^2,稳定,快速排序(nlogn不稳定)
选择排序直接选择排序n^2,不稳定堆排序(nlogn,不稳定s)
插入排序直接插入排序n^2,稳定希尔排序(N^1.25不稳定)
归并排序(nlogn稳定)
6.大量数据排名采用什么数据结构
当数据很大时并且有序程度低时堆排序最快;当数据很大时并且有序程度高时快速排序最快
7.二叉树和堆之间联系或区别
堆是一种特殊的二叉树所有父结点都比子结点要小的完全二叉树我们称为最小堆所有父结点都比子结点要大这样的完全二叉树称为最大堆。
8.平衡二叉树不平衡如何调整
按照不平衡的情况有四种调整方法分别是LR、RL、LL、RR调整
当不平衡的子树以当前节点为第一个节点往下再数到第三个节点的路径是先左再右时使用LR调整LR是先将第二个节点旋转到第三个节点的左边将第一个节点移动到第三个节点的右边
RL与LR相反RR是把第一个节点移到第三个节点左边RR与LL相反
9.hash表冲突的解决方法
开放地址法有线性探测法和平方探测法当发生冲突时继续往后找
再哈希法构造多个哈希函数发生冲突后使用下一个函数
链地址法将hash值相同的记录用链表链接起来
建立公共溢出区将哈希表分为基础表和益处表两部分发生冲突的填入益处表
0.cookie和session的联系
因为http协议是无状态的无法识别两次请求是否来自同一个客户端于是就有了cookie和session的概念。
cookies:是存放在客户浏览器中每次http请求都会携带可以用来告知服务端两个请求是否来自同一浏览器cookied的单个数据大小和存储个数是有限制的不同浏览器限制不同cookie的安全性较低。
session当客户端第一次请求服务器时服务器为这个请求分配的一块区域的存储结构为ConcurrentHashMap服务器会将sessionId返回给客户端并存入cookie。相比cookie他的安全性更高但失效时间较短
接下来客户端每次请求带着cookie服务端会从中获取sessionId来进行匹配用这套机制就实现了服务器和客户端进行有记忆的对话。
11.Nginx反向代理是什么负载均衡算法有哪些
反向代理是用来代理服务器接收请求的然后将请求转发给内部网络的服务器并将从服务器上得到的结果返回给客户端此时代理服务器对外就表现为一个服务器。
负载均衡算法有轮询默认、带权轮询、ip_hash按ip哈希结果分配能解决session共享问题、url_hash(按访问的URL的哈希结果分配)、fair根据服务端响应时间分配响应时间短优先
博主因为白天在上班都是靠晚上更新最近几天内更新完毕先收藏转发起来哦~
秒杀项目相关问题
1.项目流程
用户点击下单按钮时进行三次判断先判断请求路径是否合法,因为做了动态URL再判断用户是否已经下单过就是看redis缓存中有没有用户下单信息最后判断库存这里进行了redis库存预减由于判断库存和预减库存不是原子性操作所以用lua脚本来执行这一段代码。然后从这里开始使用分布式锁锁id为用户id+商品id,防止一个用户发送多次请求让redis多次预减。
Redis扣减成功后进行异步下单直接将正在处理返回给前端将用户id和商品Id发送RabbitMQ中负责下单的业务会从消息队列中拿出消息去执行以下操作
1.减库存减库存时用where 库存>0防止超卖
2.订单表中生成记录订单表中的用户id和商品id添加了联合唯一索引防止超卖
减库存和增加订单放在一个事务内保证一致性
3.将用户id和订单id缓存到redis中用来最初对用户重复下单的判断
4.释放分布式锁根据value去判断锁是不是当前线程的判断和删除锁不是原子性操作所以封装到了lua脚本中
2.提升qps的操作
1页面动静分离静态页面缓存到redis
2分布式锁拦截不同用户的重复
3限流算法
4验证码限流
5rabbitMq流量削峰
6接口隐藏
微学院相关问题
1.如何用springSecurity做的认证授权
在数据库中有五张表分别是菜单表角色表用户表他们是多对多的关系所以还有角色菜单表角色用户表
登录后进入认证过滤器获取用户名和密码根据用户名查询用户具有的权限并把用户名和对应权限信息放到redisJWT生成token后放入cookie,每次调用接口时携带
然后执行授权过滤器,从header中获取token解析出用户名根据用户名从redis中获取权限列表然后springSecurity就能够判断当前请求是否有权限访问
2.前后端联调经常遇到的问题
1.请求方式不匹配
2.json、x-wwww-form-urlencoded混乱的错误
3.前后端参数不一致空指针异常数据类型不匹配
4.mp生成的分布式id是19位JavsScrip只会处理16位将id生成策略改为String类型
5.跨域问题跨域问题是在访问协议、ip地址、端口号这三个有任何一个不一样相互访问就会出现跨域可以通过Spring注解解决跨域的 @CrossOrigin也可以使用nginx反向代理、网关
6.maven加载项目时默认不会加载src-java文件夹的xml类型文件可以 将xml放到resources文件夹下也可以在yaml和pom中添加配置
实习中供应链项目的收获
1.熟悉了代码开发规范
1外部接口不可放到事务代码里
* 比如入库信息要推送到财务云系统当中但是推送的成功或失败不影响主体业务的进行这种情况调用财务云系统的接口时要进行异步处理避免请求出错导致事务时间过长影响主体业务流程
* 移库和出库时要对某局的数据库也进行操作某局数据库的成功或失败影响主体业务的进行这种情况需要先进行数据库操作再根据第三方接口返回成功还是失败进行提交事务或回滚事务
(2)不可在循环里操作数据库