导图社区 面经
这是一篇关于面经的思维导图,主要内容包括了基础,数据库,框架,编程4大件以及微服务,云原生,中间件,以及其他。
编辑于2023-02-08 09:07:33 广东面经
基础
基础
BIO NIO AIO
BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。 NIO:New IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。 AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。
==和equals的区别
基本类型:比较的是值是否相同; 引用类型:比较的是引用是否相同;
String、StringBuffer、StringBuilder
StringBuffer和StringBuilder都继承自抽象类AbstractStringBuilder。 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 存储数据的字符数组没有被final修饰,说明值可以改变,抽象类AbstractStringBuilder内部都提供了一个自动扩容机制,当发现长度不够的时候(初始默认长度是16),会自动进行扩容工作,扩展为原数组长度的2倍加2,创建一个新的数组,并将数组的数据复制到新数组,所以对于拼接字符串效率要比String要高。 线程安全性:StringBuffer由于很多方法都被 synchronized 修饰了所以线程安全,但是当多线程访问时,加锁和释放锁的过程很平凡,所以效率相比StringBuilder要低。StringBuilder相反执行效率高,但是线程不安全。所以单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。 执行速度:StringBuilder > StringBuffer > String。
泛型
Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。 泛型的本质是参数化类型,即给类型指定一个参数,然后在使用时再指定此参数具体的值,那样这个类型就可以在使用时决定了。这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。
序列化和反序列化
Serialization(序列化):将java对象以一连串的字节保存在磁盘文件中的过程,也可以说是保存java对象状态的过程。序列化可以将数据永久保存在磁盘上(通常保存在文件中)。也可以进行网络通信 deserialization(反序列化):将保存在磁盘文件中的java字节码重新转换成java对象称为反序列化。
枚举
枚举对应英文(enumeration,简写 enum) 枚举是一组常量的集合 枚举属于一种特殊的类,里面只包含一组有限的特定的对象 不需要提供 setXxxx() 方法,因为枚举对象值通常为只读 对枚举对象/属性使用 static+final 共同修饰
JDK和JRE有什么区别
JDK:Java Development Kit 的简称,Java 开发工具包,提供了 Java 的开发环境和运行环境。 JRE:Java Runtime Environment 的简称,Java 运行环境,为 Java 的运行提供了所需环境。 具体来说 JDK 其实包含了 JRE,同时还包含了编译 Java 源码的编译器 Javac,还包含了很多 Java 程序调试和分析的工具。简单来说:如果你需要运行 Java 程序,只需安装 JRE 就可以了,如果你需要编写 Java 程序,需要安装 JDK。
反射
什么是反射
反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。
实现反射有几种方式
通过new对象的方式实现反射机制
通过路径实现反射机制
通过类名实现反射机制
应用场景
反射是框架设计的灵魂,如模块化的开发,会通过反射去调用对应的字节码;动态代理设计模式也采用了反射机制。还有一些场景如下:jdbc连接数据库,spring的ioc和AOP,动态代理等
代理
动态代理
如何实现动态代理
JDK 原生动态代理和 cglib 动态代理。JDK 原生动态代理是基于接口实现的,而 cglib 是基于继承当前类的子类实现的
CGLB
CGLIB是针对类来实现代理的,它的原理是对指定目标类生成一个子类,并覆盖其中的方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
JDK动态代理
JDK动态代理只提供接口的代理,不支持类的代理。核心 InvocationHandler接口和Proxy类,InvocationHandler 通过invoke()方 法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接 着,Proxy利用 InvocationHandler动态创建一个符合某一接口的的实例, 生成目标类的代理对象。
静态代理
AspectJ是静态代理的增强,所谓静态代理,就是AOP框架会在编译阶段 生成AOP代理类,因此也称为编译时增强,他会在编译阶段将AspectJ(切面)织 入到Java字节码中,运行的时候就是增强之后的AOP对象。需要定义接口或者父类,被代理的对象和代理对象需要实现相同的接口或者相同的父类,会需要生成很多的代理类
静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ 的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而 Spring AOP则无需特定的编译器处理。
深拷贝和浅拷贝
浅拷贝:当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。 深拷贝:除了对象本身被复制外,对象所包含的所有成员变量也将复制。
集合
Collection
Collection和Conllections的区别
Collection 是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法,所有集合都是它的子类,比如 List、Set 等。 Collections 是一个包装类,包含了很多静态方法,不能被实例化,就像一个工具类,比如提供的排序方法:Collections. sort(list)。
List
ArrayList
ArrayList的内部是通过数组实现的,在每次向容器中增加元素的同时都会进行容量检查,当快溢出时,就会进行扩容操作,初始值是10,以后每次都扩容原容量的1.5倍
线程不同步,不适合插入和删除
LinkList
LinkedList 是用双向链表结构存储数据的,很适合数据的动态插入和删除,另外,他还提供了 List 接口中没有定义的方法get,remove,insert,专门用于操作表头和表尾元素。
随机访问和遍历速度比较慢。
Vector
Vector 与 ArrayList 一样,也是通过数组实现的,不同的是它支持线程的同步,
ArrayList和LinkList的区别
1 数据结构实现:ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现。 2 随机访问效率:ArrayList 比 LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。 3 增加和删除效率:在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为 ArrayList 增删操作要影响数组内的其他数据的下标。而LinkedList只需要断开引用即可 4 内存空间占用:LinkedList 比 ArrayList 更占内存,因为 LinkedList 多存储了两个引用,一个指向前一个元素,一个指向后一个元素。
ArrayList和Vector的区别
线程安全:Vector 使用了 Synchronized 来实现线程同步,是线程安全的,而 ArrayList 是非线程安全的。 性能:ArrayList 在性能方面要优于 Vector。 扩容:ArrayList 和 Vector 都会根据实际的需要动态的调整容量,只不过在 Vector 扩容每次会增加 1 倍,而 ArrayList 只会增加 50%
Array和ArrayList的区别
Array 可以存储基本数据类型和对象,ArrayList 只能存储对象。 Array 是指定固定大小的,而 ArrayList 大小是自动扩展的。 Array 内置方法没有 ArrayList 多,比如 addAll、removeAll、iteration 等方法只有 ArrayList 有。
Set
如果想要让两个不同的对象视为相等的,就必须覆盖 Object 的 hashCode 方法和 equals 方
HashSet
HashSet 是一个不允许存储重复元素的集合,它的继承了HashMap,HashSet 底层使用 HashMap 来保存所有元素,因此 HashSet 的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层 HashMap 的相关方法来完成,HashSet 不允许重复的值。
什么是Hash
是一种信息摘要算法,它还叫做哈希,或者散列。我们平时使用的MD5,SHA1都属于Hash算法,通过输入key进行Hash计算,就可以获取key的HashCode(),比如我们通过校验MD5来验证文件的完整性。
什么是HashCode
HashCode,它是一个本地方法,实质就是地址取样运算
什么是碰撞
如果出现了相同的hashCode就出现了碰撞
什么是Hash攻击
通过请求大量key不同,但是hashCode相同的数据,让HashMap不断发生碰撞
TreeSet
TreeSet()是使用二叉树的原理对新 add()的对象按照指定的顺序排序(升序、降序),每增 加一个对象都会进行排序,将对象插入的二叉树指定的位置。 2. Integer 和 String 对象都可以进行默认的 TreeSet 排序,而自定义类的对象是不可以的,自 己定义的类必须实现 Comparable 接口,并且覆写相应的 compareTo()函数,才可以正常使 用。 3. 在覆写 compare()函数时,要返回相应的值才能使 TreeSet 按照一定的规则来排序
Map
HashMap
你是怎么理解HashMap的
1. HashMap是基于哈希表实现的,基于hash算法,我们通过put存储,get获取,每一个元素是一个key-value对,无序, 2 HashMap是线程不安全的集合, 3 数据结构 JDK7之前是数组+链表的形式(数组的每个位置都存储一个单向链表),JDK8后是数组+链表+红黑树的形式(链表的数据达到一定的阙值(8)就会转换成红黑树)。 loadFactor:负载因子,默认为 0.75。初始值为16 当前数组容量,始终保持 2^n,可以扩容,扩容后数组大小为当前的 2
HashMap实现原理
HashMap 基于 Hash 算法实现的,我们通过 put(key,value)存储,get(key)来获取。当传入 key 时,HashMap 会根据 key. hashCode() 计算出 hash 值,根据 hash 值将 value 保存在 bucket 里。当计算出的 hash 值相同时,我们称之为 hash 冲突,HashMap 的做法是用链表和红黑树存储相同 hash 值的 value。当 hash 冲突的个数比较少时,使用链表否则使用红黑树。
负载因子(load factor)
下面说下加载因子,如果加载因子越大,对空间的利用更充分,但是查找效率会降低(链表长度会越来越长);如果加载因子太小,那么表中的数据将过于稀疏(很多空间还没用,就开始扩容了),对空间造成严重浪费。如果我们在构造方法中不指定,则系统默认加载因子为0.75,这是一个比较理想的值,一般情况下我们是无需修改的。
如果HashMap的大小超过了负载因子(load factor)定义的容量,怎么办?
默认的负载因子大小为0.75,也就是说,当一个map填满了75%的bucket时候,和其它集合类(如ArrayList等)一样,将会创建原来HashMap大小的两倍的bucket数组,来重新调整map的大小,并将原来的对象放入新的bucket数组中。这个过程叫作rehashing,因为它调用hash方法找到新的bucket位置
HashMap和HashTable有什么不同
HashMap是继承自AbstractMap类,而HashTable是继承自Dictionary类。 相同不过它们都实现了同时实现了map、Cloneable(可复制)、Serializable(可序列化)这三个接口。 1 Hashtable比HashMap多提供了elments() 和contains() 两个方法。 2 线程安全性不同:HashMap的方法都没有使用synchronized关键字修饰,都是非线程安全的,而Hashtable的方法几乎都是被synchronized关键字修饰的。 3 初始容量大小和每次扩充容量大小的不同:Hashtable默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。 4 计算hash值的方法不同:为了得到元素的位置,首先需要根据元素的 KEY计算出一个hash值,然后再用这个hash值来计算得到最终的位置。Hashtable直接使用对象的hashCode。hashCode是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值。然后再使用除留余数发来获得最终的位置。
HashMap和HashSet的区别
1.HashMap实现了Map接口,而HashSet实现了Set接口。 2.HashMap用于存储键值对,而HashSet用于存储对象。 3.HashMap不允许有重复的键,可以允许有重复的值。HashSet不允许有重复元素。 4.HashMap允许有一个键为空,多个值为空,HashSet允许有一个空值。 5.HashMap中使用put()将元素加入map中,而HashSet使用add()将元素放入set中。 6.HashMap比较快,因为其使用唯一的键来获取对象 1836
是使用HashMap还是用TreeMap
对于在 Map 中插入、删除、定位一个元素这类操作,HashMap 是最好的选择,因为相对而言 HashMap 的插入会更快,但如果你要对一个 key 集合进行有序的遍历,那 TreeMap 是更好的选择。
ConcurrentHashMap
你是怎么理解ConcurrentHashMap
ConcurrentHashMap 是一个 Segment 数组,每个Segment跟hashmap差不多,Segment 通过继承ReentrantLock 来进行加锁,所以每次需要加锁的操作锁住的是一个 segment,这样只要保证每个 Segment 是线程安全的,也就实现了全局的线程安全。
ConcurrentHashMap默认 有 16 个 Segments,所以理论上,这个时候,最多可以同时支持 16 个线程并发写,但是一旦初始化以后,它是不可以扩容的
jdk1.7 数组+单向链表;jdk1.8 数组+链表+红黑树+CAS+synchronized
ConcurrentHashMap可以替代HashTable
Hashtable是synchronized的,但是ConcurrentHashMap的Segment 通过继承ReentrantLock 来进行加锁的,同步性能更好,ConcurrentHashMap当然可以代替HashTable,但是HashTable提供更强的线程安全性。
HashTable
Hashtable 是遗留类,很多映射的常用功能与 HashMap 类似,不同的是它承自 Dictionary 类,并且是线程安全的是synchronized,但他的并发性不ConcurrentHashMap,因为 ConcurrentHashMap 引入了分段锁
并发
线程
线程创建的方式
继承 Thread 类
Thread 类本质上是实现了 Runnable 接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过 Thread 类的 start()实例方法。start()方法是一个 native 方法,它将启动一个新线程序,执行run方法
如果自己的类已经 extends 另一个类,就无法直接 extends Thread,此时,可以实现一个Runnable 接口
实现Callable接口通过FutureTask包装器来创建Thread线程
通过线程池创建线程,使用线程池接口ExecutorService结合Callable、Future实现有返回结果的多线程。
前面两种【无返回值】原因:通过重写run方法,run方法的返回值是void,所以没有办法返回结果。 后面两种【有返回值】原因:通过Callable接口,就要实现call方法,这个方法的返回值是Object,所以返回的结果可以放在Object对象中。
线程的生命周期
新建
当程序使用 new 关键字创建了一个线程之后,该线程就处于新建状态,此时仅由 JVM 为其分配内存,并初始化其成员变量的值
就绪
当线程对象调用了 start()方法之后,该线程处于就绪状态。Java 虚拟机会为其创建方法调用栈和程序计数器,等待调度运行
运行
如果处于就绪状态的线程获得了 CPU,开始执行 run()方法的线程执行体,则该线程处于运行状态。
阻塞
阻塞状态是指线程因为某种原因放弃了 cpu 使用权,也即让出了 cpu timeslice,暂时停止运行。 直到线程进入可运行(runnable)状态,才有机会再次获得 cpu timeslice 转到运行(running)状
等待阻塞
就绪阻塞
其他阻塞
线程死亡
正常结束 1. run()或 call()方法执行完成,线程正常结束
异常结束 2. 线程抛出一个未捕获的 Exception 或 Error。
调用 stop 3. 直接调用该线程的 stop()方法来结束该线程—该方法通常容易导致死锁,不推荐使用。
4.使用 interrupt()方法来中断线程
线程池
线程池的顶级接口是 Executor但真正的线程池接口是 ExecutorService。
线程池原理
线程池做的工作主要是控制运行的线程的数量,处理过程中将任务放入队列,然后在线程创建后 启动这些任务,如果线程数量超过了最大数量超出数量的线程排队等候,等其它线程执行完毕, 再从队列中取出任务来执行。他的主要特点为:线程复用;控制最大并发数;管理线程
线程池有哪些状态
RUNNING:这是最正常的状态,接受新的任务,处理等待队列中的任务。 SHUTDOWN:不接受新的任务提交,但是会继续处理等待队列中的任务。 STOP:不接受新的任务提交,不再处理等待队列中的任务,中断正在执行任务的线程。 TIDYING:所有的任务都销毁了,workCount 为 0,线程池的状态在转换为 TIDYING 状态时,会执行钩子方法 terminated()。 TERMINATED:terminated()方法结束后,线程池的状态就会变成这个。
线程池7大参数
(1) corePoolSize:核心线程数(当等待队列不满的时候,只会调用核心线程) (2) maximumPoolSize:最大线程数(当等待队列满了的时候,会激活更多的线程) (3) keepAliveTime:新激活的线程在没有任务的情况下的存活时间 (4) TimeUnit:keepAliveTime 的单位 (5) BlockingQueue:阻塞队列,当核心线程满了,新的请求会在队列中等待 (6) ThreadFactory:创建线程的工厂,使用默认的 Executors.defaultThreadFactory() 即可 (7) RejectedExecutionHandler:拒绝策略,当超出线程池的最大承载时的拒绝策略(有4种拒绝策略
线程的面试题
sleep和wait方法的区别
1.对于 sleep()方法,我们首先要知道该方法是属于 Thread 类中的。而 wait()方法,则是属于 Object 类中的 2.sleep()方法导致了程序暂停执行指定的时间,让出 cpu 该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态。 3. 在调用 sleep()方法的过程中,线程不会释放对象锁。而当调用 wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此 对象调用 notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。 4.类的不同:sleep() 来自 Thread,wait() 来自 Object。 释放锁:sleep() 不释放锁;wait() 释放锁。 5.用法不同:sleep() 时间到会自动恢复;wait() 可以使用 notify()/notifyAll()直接唤醒。
Start和run的区别
start()方法来启动线程,真正实现了多线程运行。这时无需等待 run 方法体代码执行完毕, 可以直接继续执行下面的代码。 2. 通过调用 Thread 类的 start()方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运 行。 3. 方法 run()称为线程体,它包含了要执行的这个线程的内容,线程就进入了运行状态,开始运 行 run 函数当中的代码。 Run 方法运行结束, 此线程终止。然后 CPU 再调度其它线程。
notfil和notifyall的区别
notifyAll()会唤醒所有的线程,notifyAll() 调用后,会将全部线程由等待池移到锁池,然后参与锁的竞争,竞争成功则继续执行,如果不成功则留在锁池等待锁被释放后再次参与竞争。 而 notify()只会唤醒一个线程,具体唤醒哪一个线程由虚拟机控制
线程死锁
当线程 A 持有独占锁a,并尝试去获取独占锁 b 的同时,线程 B 持有独占锁 b,并尝试获取独占锁 a 的情况下,就会发生 AB 两个线程由于互相持有对方需要的锁,而发生的阻塞现象,我们称为死锁。
如何避免死锁
尽量使用 tryLock(long timeout, TimeUnit unit)的方法(ReentrantLock、ReentrantReadWriteLock),设置超时时间,超时可以退出防止死锁。 尽量使用 Java. util. concurrent 并发类代替自己手写锁。 尽量降低锁的使用粒度,尽量不要几个功能用同一把锁。 尽量减少同步的代码块。
守护线程
守护线程是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。在 Java 中垃圾回收线程就是特殊的守护线程
锁
无锁状态、偏向锁、轻量级锁和重量级锁
锁可以从偏向锁升级到轻量级锁,再升级的重量级锁(但是锁的升级是单向的, 也就是说只能从低到高升级,不会出现锁的降级
乐观锁
乐观锁每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数,用版本号的方式,基本都是通过 CAS 操作实现的,
悲观锁
所以每次在读写数据的时候都会上锁,这样别人想读写这个数据就会 block 直到拿到锁。java中的悲观锁就是Synchronized,AQS框架下的锁则是先尝试cas乐观锁去获取锁,获取不到,才会转换为悲观锁,如 RetreenLock。
自旋锁
自旋锁原理非常简单,如果持有锁的线程能在很短时间内释放锁资源,那么那些等待竞争锁 的线程就不需要做内核态和用户态之间的切换进入阻塞挂起状态,它们只需要等一等(自旋), 等持有锁的线程释放锁后即可立即获取锁,这样就避免用户线程和内核的切换的消耗。
Synchronized 同步锁
java中的一个关键字,synchronized内置锁是一种对象锁(锁的是对象,而非引用),作用粒度是对象,可以用来实现对临界资源的同步互斥访问、是可重入的锁,独占式的悲观锁 Synchronized 作用范围 1. 作用于方法时,锁住的是对象的实例(this); 2. 当作用于静态方法时,锁住的是Class实例,1
ReentantLock
ReentantLock 继承接口 Lock 并实现了接口中定义的方法,他是一种可重入锁,除了能完 成 synchronized 所能完成的所有工作外,还提供了诸如可响应中断锁、可轮询锁请求、定时锁等 避免多线程死锁的方法。
公平锁于非公平锁
按随机、就近原则分配锁的机制则称为不公平锁,公平锁是指谁先提出的,就给谁分配非公平锁性能比公平锁高 5~10 倍,因为公平锁需要在多核的情况下维护一个队列 Java 中的 synchronized 是非公平锁,ReentrantLock 默认的 lock()方法采用的是非公平锁
可重入锁
可重入锁,也叫做递归锁,指的是同一线程 外层函数获得锁之后 ,内层递归函数仍然有获取该锁的代码,但不受影响。在 JAVA 环境下 ReentrantLock 和 synchronized 都是 可重入锁
ReadWriteLock 读写锁
如果你的代码只读数据,可以很多人同时读,但不能同时写,那就上读锁写锁 如果你的代码修改数据,只能有一个人在写,且不能同时读取,那就上读锁。
共享锁和独占锁
独占锁 独占锁模式下,每次只能有一个线程能持有锁,ReentrantLock 就是以独占方式实现的互斥锁。 共享锁 共享锁则允许多个线程同时获取锁,并发访问 共享资源,如:ReadWriteLock。共享锁则是一种乐观锁,它放宽了加锁策略,允许多个执行读操作的线程同时访问共享资源。
偏向锁
大多数情况下锁不仅不存在多线程竞争,而且总是由同一线 程多次获得。偏向锁的目的是在某个线程获得锁之后,消除这个线程锁重入(CAS)的开销,看起 来让这个线程得到了偏护。
分段锁
分段锁也并非一种实际的锁,而是一种思想
synchronized
synchronized原理
synchronized 是由一对 monitorenter/monitorexit 指令实现的,monitor 对象是同步的基本实现单元。在 Java 6 之前,monitor 的实现完全是依靠操作系统内部的互斥锁,因为需要进行用户态到内核态的切换,所以同步操作是一个无差别的重量级操作,性能也很低。但在 Java 6 的时候,Java 虚拟机 对此进行了大刀阔斧地改进,提供了三种不同的 monitor 实现,也就是常说的三种不同的锁:偏向锁(Biased Locking)、轻量级锁和重量级锁,大大改进了其性能。
synchronized锁升级
synchronized 锁升级原理:在锁对象的对象头里面有一个 threadid 字段,在第一次访问的时候 threadid 为空,jvm 让其持有偏向锁,并将 threadid 设置为其线程 id,再次进入的时候会先判断 threadid 是否与其线程 id 一致,如果一致则可以直接使用此对象,如果不一致,则升级偏向锁为轻量级锁,通过自旋循环一定次数来获取锁,执行一定次数之后,如果还没有正常获取到要使用的对象,此时就会把锁从轻量级升级为重量级锁,此过程就构成了 synchronized 锁的升级。 锁的升级的目的:锁升级是为了减低了锁带来的性能消耗。在 Java 6 之后优化 synchronized 的实现方式,使用了偏向锁升级为轻量级锁再升级到重量级锁的方式,从而减低了锁带来的性能消耗。
synchronized和volatile的区别
volatile 是变量修饰符;synchronized 是修饰类、方法、代码段。 volatile 仅能实现变量的修改可见性,不能保证原子性;而 synchronized 则可以保证变量的修改可见性和原子性。 volatile 不会造成线程的阻塞;synchronized 可能会造成线程的阻塞。
synchronized和lock的区别
synchronized 可以给类、方法、代码块加锁;而 lock 只能给代码块加锁。 synchronized 不需要手动获取锁和释放锁,使用简单,发生异常会自动释放锁,不会造成死锁;而 lock 需要自己加锁和释放锁,如果使用不当没有 unLock()去释放锁就会造成死锁。 通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到。
synchronized和ReentrantLock的区别
synchronized 早期的实现比较低效,对比 ReentrantLock,大多数场景性能都相差较大,但是在 Java 6 中对 synchronized 进行了非常多的改进。 主要区别如下: ReentrantLock 使用起来比较灵活,但是必须有释放锁的配合动作; ReentrantLock 必须手动获取与释放锁,而 synchronized 不需要手动释放和开启锁; ReentrantLock 只适用于代码块锁,而 synchronized 可用于修饰方法、代码块等。 ReentrantLock 标记的变量不会被编译器优化;synchronized 标记的变量可以被编译器优化。
volatile 关键字的作用
保证可见性; 2、防止指令重排; 3、但是不保证原子性;
面试题
并行和并发的区别
并行:多个处理器或多核处理器同时处理多个任务。 并发:多个任务在同一个 CPU 核上,按细分的时间片轮流(交替)执行,从逻辑上来看那些任务是同时执
java程序如何保证线程安全
方法一:使用安全类,比如 Java. util. concurrent 下的类。 方法二:使用自动锁 synchronized。 方法三:使用手动锁 Lock。
CAS
子主题
CAS(Compare And Swap/Set)比较并交换,CAS 算法的过程是这样:它包含 3 个参数 CAS(V,E,N)。V 表示要更新的变量(内存值),E 表示预期值(旧的),N 表示新值。当且仅当 V 值等于 E 值时,才会将 V 的值设为 N,如果 V 值和 E 值不同,则说明已经有其他线程做了更新,则当 前线程什么都不做。最后,CAS 返回当前 V 的真实值。 CAS 操作是抱着乐观的态度进行的(乐观锁),它总是认为自己可以成功完成操作。当多个线
ABA问题
一个线程把数据A变成了B,然后又重新变成了A,此时另一个线程读取该数据的时候,发现A没有变化,就误认为是原来的那个A,但是此时A的一些属性或状态已经发生过变化
AQS
AbstractQueuedSynchronizer 类如其名,抽象的队列式的同步器
JVM
简介
jvm是运行在操作系统之上的,于硬件没有直接的交换。jVM有一套字节码指令集,一组寄存器,栈,堆,一个垃圾回收,储存方法域。
java源文件通过编译器,变成字节码文件,字节码文件通过解释器变成机器码
jvm主要组成部分
类加载器(ClassLoader) 运行时数据区(Runtime Data Area) 执行引擎(Execution Engine) 本地库接口(Native Interface) 组件的作用:首先通过类加载器(ClassLoader)会把 Java 代码转换成字节码,运行时数据区(Runtime Data Area)再把字节码加载到内存中,而字节码文件只是 JVM 的一套指令集规范,并不能直接交个底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine),将字节码翻译成底层系统指令,再交由 CPU 去执行,而这个过程中需要调用其他语言的本地库接口(Native Interface)来实现整个程序的功能。
内存区域
1 Java堆是Java虚拟机所管理的内存中最大的一块,也是垃圾回收的主要区域。Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。存放对象实例,几乎所有的对象实例都在这里分配内存。 2 方法区与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。 3 程序计数器是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器,用来指示执行引擎下一条执行指令的地址。 4 Java虚拟机栈也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、返回方法地址等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。 5 本地方法栈(Native Method Stacks),本地方法栈(Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。
堆和栈的区别
堆的物理地址分配对对象是不连续的。因此性能慢些。在GC的时候也要考虑到不连续的分配,所以有各种算法。比如,标记-消除,复制,标记-压缩,分代(即新生代使用复制算法,老年代使用标记——压缩) 栈使用的是数据结构中的栈,先进后出的原则,物理地址分配是连续的。所以性能快。堆是线程共享的,和栈是线程私有的
运行时内存
JVM从GC的角度上来划分可以分为新生代和老年代
新生代一般占据三分之一的堆的空间,新生代又分为 Eden 区、ServivorFrom、ServivorTo 三个区 采用复制算法
老年代采用标记清除算法 主要存放应用程序中生命周期长的内存对象。
垃圾回收
如何判断对象是否可以回收
引用计数器:为每个对象创建一个引用计数,有对象引用时计数器 +1,引用被释放时计数 -1,当计数器为 0 时就可以被回收。它有一个缺点不能解决循环引用的问题; 可达性分析:从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是可以被回收的。
垃圾回收器的算法
标记清除算法
标记无用对象,然后进行清除回收。缺点:效率不高,无法清除垃圾碎片。
复制算法
按照容量划分二个大小相等的内存区域,当一块用完的时候将活着的对象复制到另一块上,然后再把已使用的内存空间一次清理掉。缺点:内存使用率不高,只有原来的一半。
标记整理算法
标记无用对象,让所有存活的对象都向一端移动,然后直接清除掉端边界以外的内存。
分代算法:根据对象存活周期的不同将内存划分为几块,一般是新生代和老年代,新生代基本采用复制算法,老年代采用标记整理算法。
垃圾收集器
Serial(英文连续)是最基本垃圾收集器,使用复制算法,单线程
ParNew 垃圾收集器其实是 Serial 收集器的多线程版本,也使用复制算法,但在垃圾收集过程也要停止所有线程
Parallel Scavenge 收集器也是一个新生代垃圾收集器,同样使用复制算法,它重点关注的是程序达到一个可控制的吞吐量
Serial Old 是 Serial 垃圾收集器年老代版本,它同样是个单线程的收集器,使用标记-整理算法
Parallel Old 收集器是Parallel Scavenge的年老代版本,使用多线程的标记-整理算法
(CMS)收集器是一种年老代垃圾收集器,其最主要目标是获取最短垃圾 回收停顿时间,和其他年老代使用标记-整理算法不同,它使用多线程的标记-清除算法
初始标记需要暂停所有线程
并发标记
重新标记,需要暂停所有线程
并发清除
G1标记整理算法 收集器避免全区域垃圾收集,它把堆内存划分为大小固定的几个独立区域,并且跟踪这些区域的垃圾收集进度,同时在后台维护一个优先级列表,每次根据所允许的收集时间,
新生代老年代垃圾收集
新生代回收器:Serial、ParNew、Parallel Scavenge 老年代回收器:Serial Old、Parallel Old、CMS 整堆回收器:G1 新生代垃圾回收器一般采用的是复制算法,复制算法的优点是效率高,缺点是内存利用率低;老年代回收器一般采用的是标记-整理的算法进行垃圾回收
垃圾回收机制
不需要显示的去释放一个对象的内存的,而是由虚拟机自行执行。在JVM中,有一个垃圾回收线程,它是低优先级的,在正常情况下是不会执行的,只有在虚拟机空闲或者当前堆内存不足时,才会触发执行,扫面那些没有被任何引用的对象,并将它们添加到要回收的集合中,进行回收
类加载
JVM 类加载机制分为五个部分:加载根据相应的路径查找到clss文件 验证:检查加载的 class 文件的正确性; 准备:给类中的静态变量分配内存空间; 解析:虚拟机将常量池中的符号引用替换成直接引用的过程。符号引用就理解为一个标示,而在直接引用直接指向内存中的地址; 初始化:对静态变量和静态代码块执行初始化工作 卸载:程序代码退出
双亲委派机制
当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,每一个层次类加载器都是如此,因此所有的加载请求都应该传送到启动类加载其中,
双亲委派模型
双亲委派模型:如果一个类加载器收到了类加载的请求,它首先不会自己去加载这个类,而是把这个请求委派给父类加载器去完成,每一层的类加载器都是如此,这样所有的加载请求都会被传送到顶层的启动类加载器中,只有当父加载无法完成加载请求(它的搜索范围中没找到所需的类)时,子加载器才会尝试去加载类。
如何打破双亲委派模型
实现类的动态加载的时候要打破双亲委派模型 自定义类加载器继承 ClassLoader ,然后重写 ClassLoader 中的 loadClass 方法,并采用 自己定义的类机制对类进行加载实现。
类加载器
什么是类加载器
对于任意一个类,都需要由加载它的类加载器和这个类本身统一确立在 JVM 中的唯一性,每一个类加载器,都有一个独立的类名称空间。类加载器就是根据指定全限定名称将 class 文件加载到 JVM 内存,然后再转化为 class 对象。
类加载器分类
启动类加载器(Bootstrap ClassLoader),是虚拟机自身的一部分,用来加载Java_HOME/lib/目录中的,或者被 -Xbootclasspath 参数所指定的路径中并且被虚拟机识别的类库; 其他类加载器: 扩展类加载器(Extension ClassLoader):负责加载libext目录或Java. ext. dirs系统变量指定的路径中的所有类库; 应用程序类加载器(Application ClassLoader)。负责加载用户类路径(classpath)上的指定类库,我们可以直接使用这个类加载器。一般情况,如果我们没有自定义类加载器默认就是用这个加载器。
javaweb
session和cookie有什么区别
session:是一种将会话状态保存在服务器端的技术。 Cookie :是在 HTTP 协议下, Web 服务器保存在用户浏览器(客户端)上的小文本文件,它可以包含有关用户的信息。无论何时用户链接到服务器,Web 站点都可以访问 Cookie 信息 。 存储位置不同:session 存储在服务器端;cookie 存储在浏览器端。 安全性不同:cookie 安全性一般,在浏览器存储,可以被伪造和修改。 容量和个数限制:cookie 有容量限制,每个站点下的 cookie 也有个数限制。 存储的多样性:session 可以存储在 Redis 中、数据库中、应用程序中;而 cookie 只能存储在浏览器中。
session工作原理
session 的工作原理是客户端登录完成之后,服务器会创建对应的 session,session 创建完之后,会把 session 的 id 发送给客户端,客户端再存储到浏览器中。这样客户端每次访问服务器时,都会带着 sessionid,服务器拿到 sessionid 之后,在内存找到与之对应的 session 这样就可以正常工作了
如何避免Sql注入
SQL 注入就是在用户输入的字符串中加入 SQL 语句,如果在设计不良的程序中忽略了检查,那么这些注入进去的 SQL 语句就会被数据库服务器误认为是正常的 SQL 语句而运行,攻击者就可以执行计划外的命令或访问未被授权的数据。
使用预处理 PreparedStatement。 使用正则表达式过滤掉字符中的特殊字符
参数化查询
限制数据库权限和特权
XSS攻击
XSS 攻击:即跨站脚本攻击,它是 Web 程序中常见的漏洞。原理是攻击者往 Web 页面里插入恶意的脚本代码(css 代码、Javascript 代码等),当用户浏览该页面时,嵌入其中的脚本代码会被执行,从而达到恶意攻击用户的目的,如盗取用户 cookie、破坏页面结构、重定向到其他网站等。 预防 XSS 的核心是必须对输入的数据做过滤处理
数据库
SQL
数据库三大范式
第一范式(1NF):强调的是列的原子性,即数据库表的每一列都是不可分割的原子数据项。 第二范式(2NF):要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性。(在1NF基础上消除非主属性对主键的部分函数依赖) 第三范式(3NF):任何非主属性不依赖于其它非主属性。(在2NF基础上消除传递依赖
MySQL
什么是MySQL
MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件之一。在Java企业级开发中非常常用,因为 MySQL 是开源免费的,并且方便扩展。
索引
1索引简介
锁引是满足某种特定查找算法的数据结构,而这些数据结构会以某种方式指向数据,从而实现高效查找数据。 具体来说 MySQL 中的索引,不同的数据引擎实现有所不同,但目前主流的数据库引擎的索引都是 B+ 树实现的,
3 索引分类
种类分类
聚集索引&二级索引
4 索引语法
1 创建索引
CREATE [ UNIQUE | FULLTEXT ] INDEX index_name ON table_name ( index_col_name,... ) ;
UNIQUE 创建唯一索引 FULLTEXT 创建全文索引 如果不加创建常规索引
2 查看索引
SHOW INDEX FROM table_name ;
3 删除索引
DROP INDEX index_name ON table_name ;
索引失效
对于复合索引,如果不使用前列,后续列也将无法使用,类电话簿。 like查询是以%开头 如果条件中有or,即使其中有部分条件带索引也不会使用 where 子句里对有索引列使用函数,用不上索引
事务
ACID
1 Atomicity(原子性):一个事务(transaction)中的所有操作,或者全部完成,或者全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被恢复(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。即,事务不可分割、不可约简。 2 Consistency(一致性):在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设约束、触发器、级联回滚等。 3 Isolation(隔离性):数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。 4 Durability(持久化):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
事务隔离级别
脏读,不可重复读,幻读
1. 脏读 :脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问 这个数据,然后使用了这个数据。 2. 不可重复读 :是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两 次读数据之间不同 3. 幻读 : 是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。 同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象 发生了幻觉一样
MySQL
Mysql排查
使用 show processlist 命令查看当前所有连接信息。 使用 explain 命令查询 SQL 语句执行计划。 开启慢查询日志,查看慢查询的 SQL
MySQL性能优化
为搜索字段创建索引。 避免使用 select *,列出需要查询的字段。 垂直分割分表。 选择正确的存储引擎。
大表如何优化
水平拆分
保持数据表结构不变,通过某种策略存储数据分片。这样每一片数据分散到不同的表或者库中,达到了分布式的目的。水平拆分可以支撑非常大的数据量。 水平拆分是指数据表行的拆分,表的行数超过200万行时,就会变慢,这时可以把一张的表的数据拆成多张表来存放
垂直拆分
简单来说垂直拆分是指数据表列的拆分,把一张列比较多的表拆分为多张表。
- 限定数据的范围:务必禁止不带任何限制数据范围条件的查询语句。比如:我们当用户在查询订单历史的时候,我们可以控制在一个月的范围内。; - 读/写分离:经典的数据库拆分方案,主库负责写,从库负责读; - 缓存:使用MySQL的缓存,另外对重量级、更新少的数据可以考虑使用应用级别的缓存; 还有就是通过分库分表的方式进行优化,主要有垂直分表和水平分表
数据库优化
优化原则:减少系统瓶颈,减少资源占用,增加系统的反应速度。 **数据库结构优化** 一个好的数据库设计方案对于数据库的性能往往会起到事半功倍的效果。 需要考虑数据冗余、查询和更新的速度、字段的数据类型是否合理等多方面的内容。 **将字段很多的表分解成多个表** 对于字段较多的表,如果有些字段的使用频率很低,可以将这些字段分离出来形成新表。 因为当一个表的数据量很大时,会由于使用频率低的字段的存在而变慢。 **增加中间表** 对于需要经常联合查询的表,可以建立中间表以提高查询效率。 通过建立中间表,将需要通过联合查询的数据插入到中间表中,然后将原来的联合查询改为对中间表的查询。 **增加冗余字段** 设计数据表时应尽量遵循范式理论的规约,尽可能的减少冗余字段,让数据库设计看起来精致、优雅。但是,合理的加入冗余字段可以提高查询 表的规范化程度越高,表和表之间的关系越多,需要连接查询的情况也就越多,性能也就越差。
SQL
SQL的生命周期
1. 应用服务器与数据库服务器建立一个连接 2. 数据库进程拿到请求sql 3. 解析并生成执行计划,执行 4. 读取数据到内存并进行逻辑处理 5. 通过步骤一的连接,发送结果到客户端 6. 关掉连接,释放资
SQL如何进行优化
(1) 慢SQL的定位和排查,我们可以通过慢查询日志和慢查询日志工具分析,得到有问题的SQL列表。 (2) 执行计划分析,针对慢SQL我们可以使用关键字explain来去查看当前sql的执行计划,可重点关注type,key,rows,filterd等字段,从而去定位该SQL执行慢的根本原因,再去有的放矢的进行优化。 (3) 使用show profile工具,这个工具是MySQL提供的可以用来分析当前会话中SQL语句资源消耗情况的工具,可以用于SQL调优的测量,在当前会话中,默认情况下,show profile是关闭状态,打开以后会保存,最近15次的运行结果,针对运行慢的SQL通过profile工具进行详细分析,可以得到SQL执行过程中所有资源的开销情况,比如io开销,cpu开销,内存开销。 (4)sql优化规则 SQL的查询一定要基于索引来进行数据扫描。 避免索引列上使用函数或者运算符。 Where字句中like%号尽量放置在右边。 使用索引扫描,联合索引中的列从左往后,命中越多越好 尽可能使用SQL语句用到的索引完成排序 查询有效的列信息即可,少用*代替列信息 永远要用小的结果集驱动大的结果集
NoSQL
Redis
Redis是什么
Redis 是一个使用 C 语言开发的高速缓存数据库。单线程的 redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。为了保证效率,数据都是缓存在内存中他提供了多种语言的客户端,使用很方便。redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave主从同步
Redisjava支持
jedis:提供了比较全面的 Redis 命令的支持。 Redisson:实现了分布式和可扩展的 Java 数据结构,与 jedis 相比 Redisson 的功能相对简单,不支持排序、事务、管道、分区等 Redis 特性。Jedis是Redis的Java实现的客户端,其API提供了比较全面的Redis命令的支持;Redisson实现了分布式和可扩展的Java数据结构,和Jedis相比,功能较为简单,不支持字符串操作,不支持排序、事务、管道、分区等Redis特性。Redisson的宗旨是促进使用者对Redis的关注分离,从而让使用者能够将精力更集中地放在处理业务逻辑上。
用来干什么
数据缓存功能 分布式锁的功能 支持数据持久化 支持事务 支持消息队列
使用场景
记录帖子点赞数、点击数、评论数; 缓存近期热帖; 缓存文章详情信息; 记录用户会话信息
Redis为什么这么快
1、完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于 HashMap,HashMap 的优势就是查找和操作的时间复杂度都是O(1); 2、数据结构简单,对数据操作也简单,Redis 中的数据结构是专门进行设计的; 3、采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗; 4、使用多路 I/O 复用模型,非阻塞 IO; 5、使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis 直接自己构建了 VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;
Redis为什么是单线程的
因为 cpu 不是 Redis 的瓶颈,Redis 的瓶颈最有可能是机器内存或者网络带宽。既然单线程容易实现,而且 cpu 又不会成为瓶颈,那就顺理成章地采用单线程的方案了。
为什么要使用Redis
高并发
直接操作缓存能够承受的请求是远远大于直接访问数据库的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里而不用经过数据库。
高性能
假如用户第一次访问数据库中的某些数据。这个过程会比较慢,因为是从硬盘上读取的。将该用户访问的数据存在数缓存中,这样下一次再访问这些数据的时候就可以直接从缓存中获取了。操作缓存就是直接操作内存,所以速度相当快。如果数据库中的对应数据改变的之后,同步改变缓存中相应的数据即可!
redis优缺点
优点
- 读写性能优异, Redis能读的速度是110000次/s,写的速度是81000次/s。 - 支持数据持久化,支持AOF和RDB两种持久化方式。 - 支持事务,Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行。 - 数据结构丰富,除了支持string类型的value外还支持hash、set、zset、list等数据结构。 - 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离。
缺点
- 数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。 - Redis 不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。 - 主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。 - Redis 较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费。主服务器写内存快照,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以主服务器最好不要写内存快照。 Redis 主从复制的性能问题,为了主从复制的速度和连接的稳定性,主从库最好在同一个局域网内
Redis如何进行优化
尽量使用 Redis 的散列表,把相关的信息放到散列表里面存储,而不是把每个字段单独存储,这样可以有效的减少内存使用。比如将 Web 系统的用户对象,应该放到散列表里面再整体存储到 Redis,而不是把用户的姓名、年龄、密码、邮箱等字段分别设置 key 进行存储
缓存穿透缓存击穿缓存雪崩
缓存穿透:指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。 解决方案:最简单粗暴的方法如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们就把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。
缓存击穿
缓存击穿是指热点key在某个时间点过期的时候,而恰好在这个时间点对这个Key有大量的并发请求过来,从而大量的请求打到db,属于常见的“热点”问题
缓存雪崩
大量的应用请求无法在Redis缓存中进行处理,紧接着应用将大量请求发送到数据库层,导致数据库层的压力激增 击穿与雪崩的区别即在于击穿是对于特定的热点数据来说,而雪崩是全部数据。
Redis持久化
RDB(Redis Database):指定的时间间隔能对你的数据进行快照存储。 AOF(Append Only File):每一个收到的写命令都通过write函数追加到文件中。
Redis淘汰策略
volatile-lru:从已设置过期时间的数据集(server. db[i]. expires)中挑选最近最少使用的数据淘汰。 volatile-ttl:从已设置过期时间的数据集(server. db[i]. expires)中挑选将要过期的数据淘汰。 volatile-random:从已设置过期时间的数据集(server. db[i]. expires)中任意选择数据淘汰。 allkeys-lru:从数据集(server. db[i]. dict)中挑选最近最少使用的数据淘汰。 allkeys-random:从数据集(server. db[i]. dict)中任意选择数据淘汰。 no-enviction(驱逐):禁止驱逐数据。
框架
Spring
基础概念
Spring是一个轻量级的javaEE框架,是用来解决企业级应用开发复杂度问题,复杂度就是耦合度,所有Spring就是为了解耦,怎么解耦,一个是IOC,另一个就是AOP。通过IoC容器管理POJO对象,也就是控制反转,IoC让相互协作的组件保持松散的耦合,AOP面向切片编程可以说是对OOP(面向对象编程)的补充和完善AOP以动态非侵入的方式增强服务。而AOP编程允许你把遍布于应用各层的功能分离出来形成可重用的功能组件。
Spring AOP就是基于动态代理实现的, 分为两种代理,jdk动态代理(基于接口)和cglib代理(基于类的)。如果目标对象实现了接口,就用jdk动态代理,如果未实现接口就用cglib动态代理
常用到AOP的就是安全校验、日志操作、事务操作等,给你先定义好,然后在想用的地方用,这样不会影响已经在服务器运行的项目,然后又能注入新功能,
IOC
Bean
Bean的生命周期
配置Bean
在配置文件中的bean标签中添加init-method和destroy-method属性 类实现InitializingBean与DisposableBean接口,
初始化容器
1.创建对象(内存分配) 2.执行构造方法 3.执行属性注入(set操作) 4.执行bean初始化方法
使用容器
执行业务操作
关闭容器
1.执行bean销毁方法 关闭容器的两种方式: ConfigurableApplicationContext是ApplicationContext的子类 close()方法 registerShutdownHook()方法
Bean的实例化
实例化bean的三种方式,构造方法,静态工厂和实例工厂
Bean的作用域
spring有五种作用域分别书singleton、prototype、request、session、application singleton 就是单例类型。创建容器时自动创建一个bean的对象,不管是否使用,都存在了,每次获取到的对象都是同一个对象 prototype就是一个bean定义对应多个对象实例。prototype是原型类型,在创建容器时并没有实例化,当获取bean的时候才回去创建对象。 request就是请求作用域:就是说每次用到这个bean来处理HTTP请求的时候会创建一个bean实例。请求完成后销毁这个bean session是会话作用域:session是服务器和浏览器的一次会话过程,是连续的不是一次请求。session结束后销毁,session中所有http请求共享同一个请求的bean实例 application全局作用域:是说bean是ServletContext级别的就是说是整个web项目全局共享的。与单例有点像,但是单例是作用在applicationcontext也就是一个容器当中的,一个项目不仅仅只有一个applicationContext。
Bean默认为单例
bean为单例的意思是在Spring的IOC容器中只会有该类的一个对象 bean对象只有一个就避免了对象的频繁创建与销毁,达到了bean对象的复用,性能高
Bean单例会不会产生线程安全问题
bean在容器中是单例的,会不会产生线程安全问题? 如果对象是有状态对象,即该对象有成员变量可以用来存储数据的, 因为所有请求线程共用一个bean对象,所以会存在线程安全问题。 如果对象是无状态对象,即该对象没有成员变量没有进行数据存储的, 因方法中的局部变量在方法调用完成后会被销毁,所以不会存在线程安全问题。
DI
Spring注入
Setter注入,构造器注入,集合注入,自动装配,
AOP
1.导入坐标(pom.xml) 2.制作连接点(原始操作,Dao接口与实现类) 3.制作共性功能(通知类与通知) 4.定义切入点 5.绑定切入点与通知关系(切面)
AOP如何实现
Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修 改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP 对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对 象的方法,使用jdk动态代理
注解开发
IOC注解
@Component注解
@Component注解不可以添加在接口上,因为接口是无法创建对象的。 为了让Spring框架能够扫描到写在类上的注解,需要在配置文件上进行包扫描 <context:component-scan base-package="com.itheima"/> component-scan component:组件,Spring将管理的bean视作自己的一个组件 base-package指定Spring框架扫描的包路径,它会扫描指定包及其子包中的所有类上的注解
@Controller、表现层
@Service、业务层
@Repository 数据层
@Configuration
在配置类上添加包扫描注解@ComponentScan替换<context:component-scan base-package=""/> @Configuration @ComponentScan("com.itheima") public class SpringConfig { } @ComponentScan注解用于设定扫描路径,此注解只能添加一次,多个数据请用数组格式 @ComponentScan({com.itheima.service","com.itheima.dao"})
使这个类为Spring的配置类
注解注入
@Autowired 自动装配
@Value注解 简单数据类型注入
通过@Value将外部的值动态注入到Bean中,
@Qualifier不能独立使用,必须和@Autowired一起使用
Spring 容器中匹配的候选 Bean 数目必须有且仅有一个。
@Bean注解的作用是将方法的返回值制作为Spring管理的一个bean对象 管理第三方的Bena
@Import注解(只能写一次)扫描注解可以移除 @Import参数需要的是一个数组,可以引入多个配置类。
@Import({jdbcConfig.class,MyBatisConfig.class})
@PropertySource
读取外部的properties配置文件
@PropertySource("jdbc.properties") 如果添加多个配置文件需要以数组格式
AOP注解
@EnableAspectJAutoProxy
类型 配置类注解 位置 配置类定义上方 作用 开启注解格式AOP功能
@Aspect
类型 类注解 位置 切面类定义上方 作用 设置当前类为AOP切面类
@Pointcut
类型 方法注解 位置 切入点方法定义上方 作用 设置切入点方法 属性 value(默认):切入点表达式
通知
@Before
类型 方法注解 位置 通知方法定义上方 作用 设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法前运行
@AfterReturning(
后置通知
@Around
环绕通知
@After
返回后通知
@AfterThrowin
抛出异常后通知
@EnableTransactionManagement
在配置类上Spring开启事务注解支持
@Transactional
开启事务支持
事务管理
@EnableTransactionManagement
在配置类上Spring开启事务注解支持
@Transactional
开启事务支持
SpringMVC
什么是SpringMVC
Spring MVC是是属于Spring Framework生态里面的一个模块,它是在Servlet基础上构建并且使用MVC模式设计的一个Web框架, 主要的目的是简化传统Servlet+JSP模式下的Web开发方式。 其次, Spring MVC的整体架构设计对Java Web里面的MVC架构模式做了增强和扩展
SpringMVC执行流程
子主题
1 用户点击某个请求路径,发起一个HTTP request请求,该请求会被提交到Dispatcher Servlet(前端控制器); 2 由Dispatcher Servlet请求一个或多个Handler Mapping(处理器映射器),并返回一个执行链(Handler Execution Chain); 3 Dispatcher Servlet将Handler Mapping返回的执行链中的Handler信息发送给Handler Adapter(处理器适配器); 4 HandlerAdapter 根据 Handler 信息找到并执行相应的 Handler(常称为 Controller); 5 Handler 执行完毕后会返回给 HandlerAdapter 一个 ModelAndView 对象(Spring MVC的底层对象,包括 Model 数据模型和 View 视图信息); 6 HandlerAdapter 接收到 ModelAndView 对象后,将其返回给 DispatcherServlet ; 7 DispatcherServlet 接收到 ModelAndView 对象后,会请求 ViewResolver(视图解析器)对视图进行解析; 8 ViewResolver 根据 View 信息匹配到相应的视图结果,并返回给 DispatcherServlet; 9 DispatcherServlet 接收到具体的 View 视图后,进行视图渲染,将 Model 中的模型数据填充到 View 视图中的 request 域,生成最终的 View(视图); 10 视图负责将结果显示到浏览器(客户端)
SprigMVC组件
前置控制器 DispatcherServlet。 映射控制器 HandlerMapping。 处理器 Controller。 模型和视图 ModelAndView。 视图解析器 ViewResolver。
注解
RequestMapping 请求路径
3、@Autowired 作用:spring可以自动帮你把bean里面引用的对象的setter/getter方法省略,它会自动帮你set/get
@RequestBody 作用:用于获取请求体的内容
SpringBoot
如何理解SpringBoot
SpringBoot是新一代 javaEE开发标准,开箱即用的特性舍去了spring的配置文件 没有代码生成,也不需要XML配置。避免大量的 Maven 导入和各种版本冲突
为什么要用SpringBoot
配置简单 独立运行 自动装配 无代码生成和 xml 配置 提供应用监控 易上手 提升开发效率
SpringBoot注解
1、@SpringBootApplication替代 @SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan 2、@ImportAutoConfiguration导入配置类,一般做测试的时候使用,正常优先使用@EnableAutoConfiguration 3、@SpringBootConfiguration替代@Configuration 4、@ImportResource将资源导入容器 5、@PropertySource 导入properties文件 6、PropertySources@PropertySource 的集合 7、@Rolebean角色定义为ROLE_APPLICATION(默认值)、ROLE_SUPPORT(辅助角色)、ROLE_INFRASTRUCTURE(后台角色,用户无感) 8、@Scope指定bean的作用域,默认singleton,其它包括prototype、request、session、globalSession 9、@Lazy使bean懒加载,取消bean预初始化。 10、@Primar自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否者将抛出异常。 11、@Profile指定Bean在哪个环境下被激活 12、@DependsOn依赖的bean注册完成,才注册当前类,依赖bean不存在会报错。用于控制bean加载顺序 13、@PostConstruct bean的属性都注入完毕后,执行注解标注的方式进行初始化工作 14、@Autowired默认按类型装配,如果我们想使用按名称装配,可以结合@Qualifier注解一起使用。
自动装配
整个过程主要分成加载所有的配置类,这里用@ComponentScan来加载我们application路径下的包,用@EnableAutoConfiguration来用spring factory机制来加载第三方的jar包的配置类,所有加载好后,再去加载这些配置类用@Import,@Bean等注解去加载的别的配置类,此时所有需要加载的配置类都加载好了,再去实例化这些bean,将这些bean注册到IOC中
Run方法
首先从main找到run()方法,在执行run()方法之前new一个SpringApplication对象 进入run()方法,创建应用监听器SpringApplicationRunListeners开始监听 然后加载SpringBoot配置环境(ConfigurableEnvironment),然后把配置环境(Environment)加入监听对象中 然后加载应用上下文(ConfigurableApplicationContext),当做run方法的返回对象 最后创建Spring容器,refreshContext(context),实现starter自动化配置和bean的实例化等工作
MyBatis
#{}是预编译处理,${}是字符替换。在使用 #{}时,MyBatis 会将 SQL 中的 #{}替换成“?”,配合 PreparedStatement 的 set 方法赋值,这样可以有效的防止 SQL 注入,保证程序的运行安全。
MyBatis是一款优秀的ORM持久层框架,它内部封装了jdbc,使开发者只需要关注sql语句本身,而不需要花费精力去处理加载驱动、创建连接 其他繁杂的过程。
MyBatis如何进行分页
分页方式:逻辑分页和物理分页。 逻辑分页:使用 MyBatis 自带的 RowBounds 进行分页,它是一次性查询很多数据,然后在数据中再进行检索。 物理分页:自己手写 SQL 分页或使用分页插件 PageHelper,去数据库查询指定条数的分页数据的形式。逻辑分页是一次性查询很多数据,然后再在结果中检索分页的数据。这样做弊端是需要消耗大量的内存、有内存溢出的风险、对数据库压力较大。 物理分页是从数据库查询指定条数的数据,弥补了一次性全部查出的所有数据的种种缺点,比如需要大量的内存,对数据库查询压力较大等问题。
MyBatis一级缓存二级缓存
一级缓存是 SqlSession 级别的缓存。在操作数据库时需要构造 SqlSession 对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的是 SqlSession 之间的缓存数据区(HashMap)是互相不影响。 二级缓存是 Mapper 级别的缓存,多个 SqlSession 去操作同一个 Mapper 的 sql 语句,多个 SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。
一级缓存:基于 PerpetualCache 的 HashMap 本地缓存,key是由sql语句、条件、statement等信息组成的唯一标识,它的声明周期是和 SQLSession 一致的,,默认一级缓存是开启的。 二级缓存:MyBatis的二级缓存也是基于内存,是NameSpace(Mapper)级别。默认是关闭的,也是基于 PerpetualCache 的 HashMap 本地缓存,不同在于其存储作用域为 Mapper 级别的,如果多个SQLSession之间需要共享缓存,则需要使用到二级缓存,要开启二级缓存,使用二级缓存属性类需要实现 Serializable 序列化接口(可用来保存对象的状态)。开启二级缓存数据查询流程:二级缓存 -> 一级缓存 -> 数据库。 MyBatis的二级缓存还可以自定义实现,通过引入ehcache的包来实现二级缓存。 缓存更新机制:当某一个作用域(一级缓存 Session/二级缓存 Mapper)进行了C/U/D 操作后,默认该作用域下所有 select 中的缓存将被 clear。
缓存失效
注意:一级缓存为sqlSession级别的缓存,默认开启的,不能关闭。一级缓存失效的四种情况: 1)sqlSession不同,缓存失效。 2)sqlSession相同,查询条件不同,缓存失效,因为缓存中可能还没有相关数据。 3)sqlSession相同,在两次查询期间,执行了增删改操作,缓存失效。 4)sqlSession相同,但是手动清空了一级缓存,缓存失效。 在执行commit,rollback,update方法时会清空一级缓存
所有的update操作(insert,delete,uptede)都会触发缓存的刷新,从而导致二级缓存失效,所以二级缓存适合在读多写少的场景中开启。 二级缓存针对的是同一个namespace,所以建议是在单表操作的Mapper中使用,或者是在相关表的Mapper文件中共享同一个缓存
缓存作用域
一级缓存作用域是SqlSession级别,所以它存储的SqlSession中的BaseExecutor之中,但是二级缓存目的要实现作用范围更广,所以要实现跨会话共享,MyBatis二级缓存的作用域是namespace
是否支持延迟加载
1 ) Mybatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加载, association 指的就是一对一, collection 指的就是一对多查询。在 Mybatis 配置文件中,可以配置是否 启用延迟加载 lazyLoadingEnabled=true|false 。 2 )它的原理是,使用 CGLIB 创建目标对象的代理对象,当调用目标方法时,进入拦截器方 法,比如调用 a.getB().getName() ,拦截器 invoke() 方法发现 a.getB() 是 null 值,那么就会单 独发送事先保存好的查询关联 B 对象的 sql ,把 B 查询上来,然后调用 a.setB(b) ,于是 a 的 对象 b 属性就有值了,接着完成 a.getB().getName() 方法的调用。这就是延迟加载的基本原 理。
如何进行一对多一对一查询
有联合查询和嵌套查询 , 联合查询是几个表联合查询 , 只查询一次 , 通过在 resultMap 里面 配置 association 节点配置一对一的类就可以完成 ; 嵌套查询是先查一个表 , 根据这个表里面 的结果的外键 id, 去再另外一个表里面查询数据 , 也是通过 association 配置 , 但另外一个表的 查询通过 select 属性配置。 答:能, Mybatis 不仅可以执行一对一、一对多的关联查询,还可以执行多对一,多对多的 关联查询,多对一查询,其实就是一对一查询,只需要把 selectOne() 修改为 selectList() 即 可;多对多查询,其实就是一对多查询,只需要把 selectOne() 修改为 selectList() 即可。 关联对象查询,有两种实现方式,一种是单独发送一个 sql 去查询关联对象,赋给主对 象,然后返回主对象。另一种是使用嵌套查询,嵌套查询的含义为使用 join 查询,一部分 列是 A 对象的属性值,另外一部分列是关联对象 B 的属性值,好处是只发一个 sql 查询, 就可以把主对象和其关联对象查
编程四大件
计算机网络
七层模型
物理层:利用传输介质为数据链路层提供物理连接,实现比特流的透明传输。 数据链路层:负责建立和管理节点间的链路。 网络层:通过路由选择算法,为报文或分组通过通信子网选择最适当的路径。 传输层:向用户提供可靠的端到端的差错和流量控制,保证报文的正确传输。 会话层:向两个实体的表示层提供建立和使用连接的方法。 表示层:处理用户信息的表示问题,如编码、数据格式转换和加密解密等。 应用层:直接向用户提供服务,完成用户希望在网络上完成的各种工作。
三次握手
第一次握手:客户端发送网络包,服务端收到了。 这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。 第二次握手:服务端发包,客户端收到了。 这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。 第三次握手:客户端发包,服务端收到了。 这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常
三次握手可以携带数据吗
第一次,第二次不可以,因为如果可以携带数据,就可以趁机携带大量数据来攻击服务器,第三次就可以了,客户端已经知道了服务端是正常的了
四次挥手
而终止一个连接要经过四次挥手(也有将四次挥手叫做四次握手的)。这由TCP的半关闭(half-close)造成的。所谓的半关闭,其实就是TCP提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力。
开始双方都处于连接状态,假如是客户端先发起关闭请求。四次挥手的过程如下: 客户端发送请求,到服务端,表示断开连接 服务端接受到数据,发送知道断开连接1,服务端发送达到客户端,请求断开连接,客户端收到后给服务端发送同意断开连接 ,因为客户端和服务端的连接是双向的
Http于Https
HTTP 超文本传输协议HTTP协议被用于在Web浏览器和网站服务器之间传递信息,HTTP协议以明文方式发送内容,不提供任何方式的数据加密。 HTTPS 为了解决HTTP协议的这一缺陷,需要使用另一种协议:安全套接字层超文本传输协议HTTPS,为了数据传输的安全,HTTPS在HTTP的基础上加入了SSL/TLS协议,SSL/TLS依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密。HTTPS协议的主要作用可以分为两种:一种是建立一个信息安全通道,来保证数据传输的安全;另一种就是确认网站的真实性。 HTTPS和HTTP的主要区别 1、https协议需要到CA申请证书,一般免费证书较少,因而需要一定费用。 2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl/tls加密传输协议。 3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。 4、http的连接很简单,是无状态的;HTTPS协议是由SSL/TLS+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
TCP/UDP/IP
IP 计算机的地址就称为 IP 地址,访问任何网站实际上只是你的计算机向另外一台计算机请求信息。 数据包要在互联网上进行传输,就要符合网际协议(IP)标准, **UDP:用户数据包协议** IP 是非常底层的协议,只负责把数据包传送到对方电脑,而不知道把数据包交给谁,通过端口号 UDP 就能把指定的数据包发送给指定的程序了,端口号其实就是一个数字,在0-65535 每个想访问网络的程序都需要绑定一个端口号 所以IP 通过 IP 地址信息把数据包发送给指定的电脑,而 UDP 通过端口号把数据包分发给正确的程序 但是UDP - 数据包在传输过程中容易丢失; - 大文件会被拆分成很多小的数据包来传输,但他并不知道如何组装这些数据包,从而把这些数据包还原成完整的文件。 **TCP:是一种面向连接的、可靠的、基于字节流的传输层通信协议。** - 对于数据包丢失的情况,TCP 提供重传机制; - T tCP 头除了包含了目标端口和本机端口号外,还提供了用于排序的序列号,以便接收端通过序号来重排数据包。 所以 ip是吧数据包发送给指定的电脑,udp通过端口号吧数据包分发给所需要的程序,tcp比udp多了重传机制,和数据包排序机制
cp 和 udp 是 OSI 模型中的运输层中的协议。tcp 提供可靠的通信传输,而 udp 则常被用于让广播和细节控制交给应用的通信传输。 两者的区别大致如下: tcp 面向连接,udp 面向非连接即发送数据前不需要建立链接; tcp 提供可靠的服务(数据传输),udp 无法保证; tcp 面向字节流,udp 面向报文; tcp 数据传输慢,udp 数据传输快;
TCP粘包
cp 粘包可能发生在发送端或者接收端,分别来看两端各种产生粘包的原因: 发送端粘包:发送端需要等缓冲区满才发送出去,造成粘包; 接收方粘包:接收方不及时接收缓冲区的包,造成多个包接收。cp 粘包可能发生在发送端或者接收端,分别来看两端各种产生粘包的原因: 发送端粘包:发送端需要等缓冲区满才发送出去,造成粘包; 接收方粘包:接收方不及时接收缓冲区的包,造成多个包接收。
操作系统
设计模式和7大原则
设计模式
单例模式:保证被创建一次,节省系统开销。 工厂模式(简单工厂、抽象工厂):解耦代码。 观察者模式:定义了对象之间的一对多的依赖,这样一来,当一个对象改变时,它的所有的依赖者都会收到通知并自动更新。 外观模式:提供一个统一的接口,用来访问子系统中的一群接口,外观定义了一个高层的接口,让子系统更容易使用。 模版方法模式:定义了一个算法的骨架,而将一些步骤延迟到子类中,模版方法使得子类可以在不改变算法结构的情况下,重新定义算法的步骤。 状态模式:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。 89.简单工厂和抽象工厂有什么区别? 简单工厂:用来生产同一等级结构中的任意产品,对于增加新的产品,无能为力。 工厂方法:用来生产同一等级结构中的固定产品,支持增加任意产品。 抽象工厂:用来生产不同产品族的全部产品,对于增加新的产品,无能为力;支持增加产品族
7大原则
1) 单一职责原则 2) 接口隔离原则 3) 依赖倒转(倒置)原则 4) 里氏替换原则 5) 开闭原则 6) 迪米特法则 7) 合成复用原则
数据结构
数据结构
数组
数组可以说是最基本最常见的数据结构。数组一般用来存储相同类型的数据,可通过数组名和下标进行数据的访问和更新。数组中元素的存储是按照先后顺序进行的,同时在内存中也是按照这个顺序进行连续存放。数组相邻元素之间的内存地址的间隔一般就是数组数据类型的大小
数组和链表的区别
栈
栈是一种比较简单的数据结构,常用一句话描述其特性,后进先出。栈本身是一个线性表,但是在这个表中只有一个口子允许数据的进出。这种模式可以参考腔肠动物...即进食和排泄都用一个口... 栈的常用操作包括入栈push和出栈pop,对应于数据的压入和压出。还有访问栈顶数据、判断栈是否为空和判断栈的大小等。由于栈后进先出的特性,常可以作为数据操作的临时容器,对数据的顺序进行调控,与其它数据结构相结合可获得许多灵活的处理。
队列
队列(Queue)也是一种运算受限的线性表。它只允许在表的一端进行插入,而在另一端进行删除。允许删除的一端称为队头(front),允许插入的一端称为队尾(rear)。先进先出。
表
顺序表
序表是在计算机内存中以数组的形式保存的线性表,线性表的顺序存储是指用一组地址连续的存储单元,依次存 储线性表中的各个元素、使得线性表中再逻辑结构上响铃的数据元素存储在相邻的物理存储单元中,即通过数据元 素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系。
链表是一种物理存储单元上非连续、非顺序的存储结构,其物理结构不能只管的表示数据元素的逻辑顺序,数据元 素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列的结点(链表中的每一个元素称为结点)组成, 结点可以在运行时动态生成。
双链表
双向链表也叫双向表,是链表的一种,它由多个结点组成,每个结点都由一个数据域和两个指针域组成,数据域用 来存储数据,其中一个指针域用来指向其后继结点,另一个指针域用来指向前驱结点。链表的头结点的数据域不存 储数据,指向前驱结点的指针域值为null,指向后继结点的指针域指向第一个真正存储数据的结
单链表
单向链表是链表的一种,它由多个结点组成,每个结点都由一个数据域和一个指针域组成,数据域用来存储数据, 指针域用来指向其后继结点。链表的头结点的数据域不存储数据,指针域指向第一个真正存储数据的结点。
散列表(Hash table,也叫哈希表)是一种查找算法,与链表、树等算法不同的是,散列表算法 在查找时不需要进行一系列和关键字(关键字是数据元素中某个数据项的值,用以标识一个数据 元素)的比较操作
调表
从上面的对比中可以看出,链表虽然通过增加指针域提升了自由度,但是却导致数据的查询效率恶化。特别是当链表长度很长的时候,对数据的查询还得从头依次查询,这样的效率会更低。跳表的产生就是为了解决链表过长的问题,通过增加链表的多级索引来加快原始链表的查询效率。这样的方式可以让查询的时间复杂度从O(n)提升至O(logn)。
树
树作为一种树状的数据结构,其数据节点之间的关系也如大树一样,将有限个节点根据不同层次关系进行排列,从而形成数据与数据之间的父子关系。常见的数的表示形式更接近“倒挂的树”,因为它将根朝上,叶朝下。 树的数据存储在结点中,每个结点有零个或者多个子结点。没有父结点的结点在最顶端,成为根节点;没有非根结点有且只有一个父节点;每个非根节点又可以分为多个不相交的子树。 这意味着树是具备层次关系的,父子关系清晰,家庭血缘关系明朗;这也是树与图之间最主要的区别。
二叉树
即每个结点都最多只有两个子结点的树
满二叉树:除最后一层外,每个结点都有左右子结点的二叉树
平衡二叉树又被称为AVL树,它是一棵二叉排序树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树
**完全二叉树**:除了最后一层结点,其它层的结点数都达到了最大值;同时最后一层的结点都是按照从左到右依次排布。
B树
B树和二叉树的区别:二叉树最多能有两个子节点;B树最多只能有M个子节点,最少有三个子节点
B+树
B+树非叶子节点不存储数据,每个叶子节点指向相邻的叶子节点
红黑树
平衡二叉树(AVL)为了追求高度平衡,需要通过平衡处理使得左右子树的高度差必须小于等于1。高度平衡带来的好处是能够提供更高的搜索效率,其最坏的查找时间复杂度都是O(logN)。但是由于需要维持这份高度平衡,所付出的代价就是当对树种结点进行插入和删除时,需要经过多次旋转实现复衡。这导致AVL的插入和删除效率并不高。
堆
了解完二叉树,再来理解堆就不是什么难事了。堆通常是一个可以被看做一棵树的数组对象。堆的具体实现一般不通过指针域,而是通过构建一个一维数组与二叉树的父子结点进行对应,因此堆总是一颗完全二叉树。 对于任意一个父节点的序号n来说(这里n从0算),它的子节点的序号一定是2n+1,2n+2,因此可以直接用数组来表示一个堆。 不仅如此,堆还有一个性质:堆中某个节点的值总是不大于或不小于其父节点的值。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
图
图结构一般包括顶点和边,顶点通常用圆圈来表示,边就是这些圆圈之间的连线。边还可以根据顶点之间的关系设置不同的权重,默认权重相同皆为1。此外根据边的方向性,还可将图分为有向图和无向图。
常用算法
微服务
什么是微服务
微服务架构是一种架构思想架构就是为了解耦,为了满足高性能,高并发,高可用,解决客户端如何访问服务,服务之间如何通信,服务如何治理,服务挂了怎么办。 实际开发方式是分布式系统开发,而具体的开发模式是SpringCloud+SpringBoot , SpringCloud是一个编程模型,一系列接口,一个标准,不是具体等等实现方案 ,Spring Cloud并没有重复制造轮子,它只是将各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。 -具体的实现方案有, SpringCloudNetfilx已经全面停止维护了,所以自然也就不会选择这个了, duoo+Zookeeper,不够全面,没有网关,客户端如何访问,缺失了服务治理,服务挂了怎么办, 所以只能选择SpringCloudAlibaba,一站式微服务解决方案, 客户端如何访问,getway,替代品Zuul, 服务注册于发现 Nacos 替代品Eureka ,服务如何通信Http openfeign,RPC,duboo,服务管理 sentinel 流量管理,链路监控 替代品Hystix
CAP定理BASE理论
CAP 原则又称 CAP 定理,指的是在一个分布式系统中, Consistency(一致性)、 Availability (可用性)、Partition tolerance(分区容错性),三者不可得兼。BASE理论BASE是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)三个短语的简写
HTTP -> 应用层 跨防火墙,可以在不同的局域网通信 RPC -> 远程过程调用 TCP 第四层 优点速度快,缺点 不能跨防火墙 ,局域网通信
SpirngCloud的优点
1 耦合度比较低。不会影响其他模块的开发。 2减轻团队的成本,可以并行开发,不用关注其他人怎么开发,先关注自己的开发。 3配置比较简单,基本用注解就能实现,不用使用过多的配置文件。 4微服务跨平台的,可以用任何一种语言开发。 5每个微服务可以有自己的独立的数据库也有用公共的数据库。 6直接写后端的代码,不用关注前端怎么开发,直接写自己的后端代码即可,然后暴露接口,通过组件进行服务通信
SpringCloud的缺点
部署比较麻烦,给运维工程师带来一定的麻烦。 针对数据的管理比麻烦,因为微服务可以每个微服务使用一个数据库。 系统集成测试比较麻烦 性能的监控比较麻烦。
SpringCloud常用组件
spring Cloud Eureka,服务注册中心,特性有失效剔除、服务保护 Spring Cloud Zuul,API服务网关,功能有路由分发和过滤 Spring Cloud Config,分布式配置中心,支持本地仓库、SVN、Git、Jar包内配置等模式 Spring Cloud Ribbon,客户端负载均衡,特性有区域亲和,重试机制 Spring Cloud Hystrix,客户端容错保护,特性有服务降级、服务熔断、请求缓存、请求合并、依赖隔离 Spring Cloud Feign,声明式服务调用本质上就是Ribbon+Hystrix Spring Cloud Stream,消息驱动,有Sink、Source、Processor三种通道,特性有订阅发布、消费组、消息分区Spring Cloud Bus,消息总线,配合Config仓库修改的一种Stream实现, Spring Cloud Sleuth,分布式服务追踪,需要搞清楚TraceID和SpanID以及抽样,如何与ELK整合
SpringCloud
SpringCloudNetfilx
SpringCloudAlibaba
Dubbo+Zookeeper
云原生
Linux
Docker
1. 什么是 Docker 容器? Docker 容器 在应用程序层创建抽象并将应用程序及其所有依赖项打包在一起。这使我们能够快速可靠地部署应用程序。容器不需要我们安装不同的操作系统。相反,它们使用底层系统的 CPU 和内存来执行任务。这意味着任何容器化应用程序都可以在任何平台上运行,而不管底层操作系统如何。我们也可以将容器视为 Docker 镜像的运行时实例。 2. 什么是 DockerFile? Dockerfile 是一个文本文件,其中包含我们需要运行以构建 Docker 映像的所有命令。Docker 使用 Dockerfile 中的指令自动构建镜像。我们可以docker build用来创建按顺序执行多个命令行指令的自动构建。 3. 如何从 Docker 镜像创建 Docker 容器? 为了从镜像创建容器,我们从 Docker 存储库中提取我们想要的镜像并创建一个容器。我们可以使用以下命令: $ docker run -it -d <image_name> 4. Docker Compose 可以使用 JSON 代替 YAML 吗? 是的,我们可以对Docker Compose文件使用 JSON 文件而不是YAML $ docker-compose -f docker-compose.json up 5. 什么是Docker Swarm? Docker Swarm 是一个容器编排工具,它允许我们跨不同主机管理多个容器。使用 Swarm,我们可以将多个 Docker 主机变成单个主机,以便于监控和管理。 6. 如果你想使用一个基础镜像并对其进行修改,你怎么做? 我们可以使用以下 Docker 命令将图像从 Docker Hub 拉到我们的本地系统上: $ docker pull <image_name> 7. 如何启动、停止和终止容器? 要启动 Docker 容器,请使用以下命令: $ docker start <container_id> 要停止 Docker 容器,请使用以下命令: $ docker stop <container_id> 要终止 Docker 容器,请使用以下命令: $ docker kill <container_id> 8. Docker 运行在哪些平台上? Docker 在以下 Linux 发行版上运行: CentOS 6+ Gentoo ArchLinux CRUX 3.0+ openSUSE 12.3+ RHEL 6.5+ Fedora 19/20+ Ubuntu 12.04、13.04 Docker 还可以通过以下云服务在生产中使用: 微软Azure 谷歌计算引擎 亚马逊 AWS EC2 亚马逊 AWS ECS 机架空间 提示:我们始终建议您在面试之前进行一些公司研究,要为这个特定问题做准备,请了解公司如何使用 Docker 并在您的答案中包含他们使用的平台。 9. 解释 Docker 组件。 三个架构组件包括 Docker 客户端、主机和注册表。 Docker 客户端:该组件执行构建和运行操作以与 Docker 主机通信。 Docker 主机:该组件包含 Docker 守护程序、Docker 镜像和 Docker 容器。守护进程建立到 Docker Registry 的连接。 Docker Registry:该组件存储 Docker 镜像。它可以是公共注册表,例如 Docker Hub 或 Docker Cloud,也可以是私有注册表。 10. 虚拟化和容器化有什么区别? 虚拟化 虚拟化帮助我们在单个物理服务器上运行和托管多个操作系统。在虚拟化中,管理程序为客户操作系统提供了一个虚拟机。VM 形成了硬件层的抽象,因此主机上的每个 VM 都可以充当物理机。 容器化 容器化为我们提供了一个独立的环境来运行我们的应用程序。我们可以在单个服务器或 VM 上使用相同的操作系统部署多个应用程序。容器构成了应用层的抽象,所以每个容器代表一个不同的应用。 11. 管理程序的功能是什么? 管理程序或虚拟机监视器是帮助我们创建和运行虚拟机的软件。它使我们能够使用单个主机来支持多个来宾虚拟机。它通过划分主机的系统资源并将它们分配给已安装的来宾环境来实现这一点。可以在单个主机操作系统上安装多个操作系统。有两种类型的管理程序: Native:本机管理程序或裸机管理程序,直接在底层主机系统上运行。它使我们可以直接访问主机系统的硬件,并且不需要基本服务器操作系统。 托管:托管管理程序使用底层主机操作系统。 12. 如何构建Dockerfile? 为了使用我们概述的规范创建映像,我们需要构建一个 Dockerfile。要构建 Dockerfile,我们可以使用以下docker build命令: $ docker build 13. 使用什么命令将新镜像推送到 Docker Registry? 要将新镜像推送到 Docker Registry,我们可以使用以下docker push命令: $ docker push myorg/img 14.什么是Docker引擎? Docker Engine 是一种开源容器化技术,我们可以使用它来构建和容器化我们的应用程序。Docker Engine 由以下组件支持: Docker 引擎 REST API Docker 命令行界面 (CLI) Docker 守护进程 15. 如何访问正在运行的容器? 要访问正在运行的容器,我们可以使用以下命令: $ docker exec -it <container_id> bash 16.如何列出所有正在运行的容器? 要列出所有正在运行的容器,我们可以使用以下命令: $ docker ps 17. 描述 Docker 容器的生命周期。 Docker 容器经历以下阶段: 创建容器 运行容器 暂停容器(可选) 取消暂停容器(可选) 启动容器 停止容器 重启容器 杀死容器 销毁容器 18. 什么是Docker对象标签? Docker 对象标签是存储为字符串的键值对。它们使我们能够将元数据添加到 Docker 对象,例如容器、网络、本地守护进程、图像、Swarm 节点和服务。 19. 使用Docker Compose时如何保证容器1先于容器2运行? Docker Compose 在继续下一个容器之前不会等待容器准备就绪。为了控制我们的执行顺序,我们可以使用“取决于”条件,depends_on。这是在 docker-compose.yml 文件中使用的示例: version: "2.4" services: backend: build: . depends_on: - db db: image: postgres 1 2 3 4 5 6 7 8 该docker-compose up命令将按照我们指定的依赖顺序启动和运行服务。 20.docker create命令有什么作用? 该docker create命令在指定映像上创建可写容器层,并准备该映像以运行指定命令。 ———————————————— 版权声明:本文为CSDN博主「我是不会选择做一个普通人的」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/shanghongshen/article/details/121603303
DockerFile
Dockerfifile其实就是一个文本文件,由一系列命令和参数构成,Docker可以读取Dockerfifile文件并根据Dockerfifile文件的描述来构建镜像。 基础镜像信息 维护者信息 镜像操作指令 容器启动时执行的指
DockerFile命令
FROM 指定父镜像 指定dockerfile基于那个image构建 MAINTAINER 作者信息 用来标明这个dockerfile谁写的 LABEL 标签 用来标明dockerfile的标签 可以使用Label代替Maintainer RUN 执行命令 执行一段命令 默认是/bin/sh 格式: RUN command 或者 RUN ["command" , "param1","param2"] CMD 容器启动命令 提供启动容器时候的默认命令 和ENTRYPOINT配 ENTRYPOINT 入口 一般在制作一些执行就关闭的容器中会使用 COPY 复制文件 build的时候复制文件到image中 ADD 添加文件 build的时候添加文件到image中 不仅仅局限于当前build上下文 可以来源于远程服务 ENV 环境变量 指定build时候的环境变量 可以在启动的容器的时候 通过-e覆盖 格式ENV name=value ARG 构建参数 构建参数 只在构建的时候使用的参数 如果有ENV 那么ENV的相同名字的值始终覆盖arg的参数 VOLUME 定义外部可以挂载的数据卷 指定build的image那些目录可以启动的时候挂载到文件系统中 启动容器的时候使用 -v 绑定 格式 VOLUME ["目录"] EXPOSE 暴露端口 定义容器运行的时候监听的端口 启动容器的使用-p来绑定暴露端口 格式: EXPOSE 8080 或者 EXPOSE 8080/udp WORKDI工作目录 指定容器内部的工作目录 如果没有创建则自动创建 如果指定/ 使用的是绝对地址 如果不是/开头那么是在上一条workdir的路径的相对路径 USER 指定执行用户 指定build或者启动的时候 用户 在RUN CMD
SpringBoot如何构建镜像
1 构建SpringBoot服务
2 本地测试
3maven构建打包
4 cmd下测试jar包是否能执行
5 编写dockerFirle脚本
6 jar包和dockerfile发布上去
7 dockerbuild打包镜像
8 查看镜像
9 运行镜像
0 测试容器是否启动成功
DockerCompos
Docker-Compose项目是Docker官方的开源项目,负责实现对Docker容器集群的快速编排。 Docker-Compose将所管理的容器分为三层,分别是工程(project),服务(service)以及容器(container)。
Compose允许用户通过一个docker-compose.yml模板文件(YAML 格式)来定义一组相关联的应用容器为一个项目(project)。 Compose模板文件是一个定义服务、网络和卷的YAML文件。Compose模板文件默认路径是当前目录下的docker-compose.yml,可以使用.yml或.yaml作为文件扩展名
网络
不使用网络通信
--link,需要在同一个服务器里,把一个容器的配置。配置到另一个容器,hosts配置,不推荐适用。一次只能配置一个
大量的互联网应用服务包含多个服务组件,这往往需要多个容器之间通过网络通信进行相互配合。Docker 目前提供了映射容器端口到宿主主机和容器互联机制来为容器提供网络服务。在启动容器的时候,如果不指定对应的参数,在容器外部是无法通过网络来访问容器内的网络应用和服务的。可以通过 -P 或 -p 参数来指定端口映射。当使用 -P 参数时,Docker 会随机选择一个主机可用的端口映射至容器内部开放的网络端口:使用-p时指定端口来进行映射
自定义网络
查看所有的docker网络 docker netwok ls 网络模式桥接bridge,none不配置,host,共享网络,container 容器网络连通, 直接启动 docker0默认 域名是不能访问的,
dockerCompose
docker compose是一个命令行工具,是用于定义和运行多容器Docker应用程序的工具;通过Compose,开发者可以使用YML文件来配置应用程序需要的所有服务。
K8S
DevOPS
中间件
ELK
什么是ELK
ELK是三个开源软件的缩写,分别表示:Elasticsearch , Logstash, Kibana , 它们都是开源软件。它还可以支持其它任何数据搜索、分析和收集的场景 在海量日志系统的运维中,可用于解决: 分布式日志数据集中式查询和管理、系统监控,包含系统硬件和应用各个组件的监控、 故障排查、安全信息和事件管理、报表功能等等 Beats Beats是elastic公司开源的一款用于采集系统监控数据的代理agent,是在被监控服务器上以客户端形式运行的数据收集器的统称。可以直接把数据发送给Elasticsearch或通过Logstash发送给Elasticsearch,然后进行后续的数据分析活动。集合了多种单一用途数据采集器,用于实现从边缘机器向 Logstash 和Elasticsearch 发送数据。里面应用最多的是Filebeat,是一个轻量级日志采集器。通过Kibana展示
Elasticsearch是个开源分布式搜索引擎,是一个基于 Lucene 的搜索服务器提供搜集、分析、存储数据三大功能。它的特点有:分布式,零配置,自动发现,索引自动分片,索引副本机制,restful风格接口,多数据源,自动搜索负载等。2.分布式实时文件存储,并将每一个字段都编入索引 3.文档导向,所有的对象全部是文档 4.高可用性,易扩展,支持集群(Cluster)、分片和复制(Shards 和 Replicas) 接口友好,支持 JSON 1.2、Logstash 主要是用来日志的搜集、分析、过滤日志的工具,支持大量的数据获取方式。一般工作方式为c/s架构,client端安装在需要收集日志的主机上,server端负责将收到的各节点日志进行过滤、修改等操作在一并发往elasticsearch上去。logstash就是一根具备实时数据传输能力的管道,负责将数据信息从管道的输入端传输到管道的输出端 1.3、Kibana 也是一个开源和免费的工具,Kibana可以为 Logstash 和 ElasticSearch 提供的日志分析友好的 Web 界面,可以帮助汇总、分析和搜索重要数据日志。 1.4 Beats是elastic公司开源的一款用于采集系统监控数据的代理agent,是在被监控服务器上以客户端形式运行的数据收集器的统称
6.什么是分词器 分词是将文本转换成一系列单词(Term or Token)的过程,也可以叫文本分析,在 ES 里面称为 Analysis 分词器是 ES 中专门处理分词的组件,英文为 Analyzer,它的组成如下: Character Filters:针对原始文本进行处理,比如去除 html 标签 Tokenizer:将原始文本按照一定规则切分为单词 Token Filters:针对 Tokenizer 处理的单词进行再加工,比如转小写、删除或增新等 处理 ES 提供了一个可以测试分词的 API 接口,方便验证分词效果,endpoint 是_analyze ES 也提供了很多内置的分析器。 7.elasticsearch 的倒排索引是什么? 正排索引是以文档的 ID 为关键字,表中记录文档中每个字的位置信息,查找时扫描表 中每个文档中字的信息直到找出所有包含查询关键字的文档。 而倒排索引,是通过分词策略,形成了词和文章的映射关系表,这种词典+映射表即为 倒排索引。 有了倒排索引,就能实现 o(1)时间复杂度的效率检索文章了,极大的提高了检索效 率。 所以总的来说,正排索引是从文档到关键字的映射(已知文档求关键字),倒排索引是 从关键字到文档的映射(已知关键字求文档)。
Elasticsearch
应用场景
1)全文搜索的功能 享 Elasticsearch提供了全文搜索的功能,适用于电商商品搜索、App搜索、企业内部信息搜索、T系统搜索等。 2)日志分析 复杂的业务场景通常会产生繁杂多样的日志,如Apache Log、System Log、小ySQL Log等,往往很难从繁杂的日志中获取价 值,却要承担其存储的成本。 Elasticsearch能够借助Beats、Logstash等快速对接各种常见的数据源,并通过集成的Kibanai高效地完成日志的可视化分析,让 日志产生价值。 3)运维监控 当您在ECS或者物理机中部署了Docker容器、MySQLi或MongoDB等数据库,可使用Elasticsearch结合Beats、.Logstash或 ElasticF引ow将所有日志实时集中并构建索引,然后通过集成的Kibana灵活地运用数据构建可视化运维看板,并在看板上展示主机 名称、P地址、部署情况、显示颜色等信息。 4)安全分析 当您需要通过日志解决公司内部繁杂的安全审计工作,可通过Elasticsearch分析、检索海量历史日志,高效地完成安全审计工 作。
优缺点
ElasticSearch的优缺点? 优点 ·分布式的实时文件存储,每个字段都破索引且可用于搜索。 ·分布式的实时分析搜索引擎,海量数据下近实时秒级响应。 ·简单的restful api,天生的兼容多语言开发。 ·易扩展,处理PB级结构化或非结构化数据。 缺点 ·在需要添加新数据与新字段的时候,如果elasticSearchi进行搜索是可能需要重新修改格式。之前的数据需要重新同步,对数 据的管理有很多困难。
倒排索引
倒排索引是搜索引擎的核心,搜索引擎的主要目标是在查找发生搜索条件的文档时提供快速搜索。 倒排索引是一种像数据结构一样的散列图,可将用户从单词导向文档或网页,它是搜索引擎的核心,其主要目标是快速搜索从数百 万文件中查找数据。 倒排索引是区别于正排索的概念: 正排索引:是以文档对象的唯一心作为索引,以文档内容作为记录。 倒排索引:Inverted index,指的是将文档内容中的单词作为索引,将包含该词的文档D作为记录。
分词器
ElasticSearch中的分析器是什么? 在ElasticSearch中索引数据时,数据由为索引定义的Analyzer在内部进行转换。分析器由一个Tokenizer和零个或多个 TokenFilter组成。编译器可以在一个或多个CharFilter之前。分析模块允许您在逻辑名称下注册分析器,然后可以在映射定义或 某些API中引用它们。 Elasticsearch附带了许多可以随时使用的预建分析器。或者,您可以组合内置的字符过滤器,编译器和过滤器器来创建自定义分 析器。
基本概念
(1)index索引:索引类似于mysql中的数据库,Elasticesearch中的索引是存在数据的地方,包含了一堆有相似结构的文档 数据。 (2)type类型:类型是用来定义数据结构,可以认为是mysql中的一张表,type是index中的一个逻辑数据分类 (3)document文档:类似于MySQL中的一行,不同之处在于ES中的每个文档可以有不同的字段,但是对于通用字段应该 具有相同的数据类型,文档是s中的最小数据单元,可以认为一个文档就是一条记录。 (4)Field字段:Field是Elasticsearchl的最小单位,一个document里面有多个field (5)shard分片:单台机器无法存储大量数据,es可以将一个索引中的数据切分为多个shard,分布在多台服务器上存储。有了 shard就可以横向扩展,存储更多数据,让搜索和分析等操作分布到多台服务器上去执行,提升吞吐量和性能。 (6)replica副本:任何一个服务器随时可能故障或宕机,此时shard可能会丢失,因此可以为每个shard创建多个replica副 本。replica可以在shardi故障时提供备用服务,保证数据不丢失,多个replica还可以提升搜索操作的吞吐量和性能。primary shard(建立索引时一次设置,不能修改,默认5个),replica shard(随时修改数量,默认1个),默认每个索引10个shard,5 个orimary shard,5个replica shard,最小的高可用配置,是2台服务器。
BlockingQueue
生产者线程不断的生产新的对象,并将他们插入到BlockingQueue,直到队列中object的数量达到队列存储容量的上限。也就是说当队列中对象达到容量上限的时候,生产者线程将被阻塞,不能再向队列中插入新的对象。生产者线程将保持阻塞等待状态,直到消费者线程从队列中拿走Object,让队列有空余位置放入新的对象。 消费者线程不断的从BlockingQueue取出对象并将其进行处理。如果消费者线程尝试从一个空队列中获取一个对象,消费者线程将被阻塞处于等待状态,直到生产者向队列中放入一个新的对象。 所以BlockingQueue经常被用于生产消费的缓冲队列,这就不一样需要自己定义什么时候阻塞,什么时候,唤醒线程了。BlockingQueue阻塞队列
kafka
其他
Git
常用命令
. clone(克隆): 从远程仓库中克隆代码到本地仓库 2. checkout (检出):从本地仓库中检出一个仓库分支然后进行修订 3. add(添加): 在提交前先将代码提交到暂存区 4. commit(提交): 提交到本地仓库。本地仓库中保存修改的各个历史版本 5. fetch (抓取) : 从远程库,抓取到本地仓库,不进行任何的合并动作,一般操作比较少。 6. pull (拉取) : 从远程库拉到本地库,自动进行合并(merge),然后放到到工作区,相当于 fetch+merge 7. push(推送) : 修改完成后,需要和团队成员共享代码时,将代码推送到远程仓库
Maven
Gradle