Java面试知识点集合

一Java 集合

1. HashMap

数据结构:Java7以前是Entry数组+链表的结构,Java8以后是Node数组+链表+红黑树的结构

put:通过key计算出hash值,然后计算index = hash & (length-1)得到key在数组的下标,如果数组原本没有元素,则直接存在数组上。如果数组有元素,则出现了hash冲突,如果key相同,则修改value,否则新建节点,插入到对应的链表中。Java7以前,是头插法,Java8以后是尾插法。如果链表长度大于8且数组长度大于64则转红黑树,如果红黑树节点为6则退化为链表。

get:通过key的hash值,计算出数组下标,如果在桶上直接返回,否则按照链表或者红黑树的方式查找。

扩容:当元素数量大于负载因子*数组长度时,则需要进行扩容。扩容时,会生成原数组两倍长度的新数组,然后rehash原来的元素。

线程安全:线程不安全。Java7由于头插法在扩容时,会出现环形链表。Java8会出现值覆盖问题。

2. ConcurrentHashMap

数据结构:Java7以前是segment数组+HashEntry数组及链表,Java8以后是Node数组+链表+红黑树

put:Java7中,计算key的hash值,通过hash值定位到segment数组下标,再通过hash值计算在segment的HashEntry数组的下标,找到合适的位置插入。插入时,先通过scanAndLockForPut()获取锁,获取锁失败则自旋重试,若达到重试最大次数,则改为阻塞锁获取。Java8中,计算key的hashcode,先判断table是否需要初始化,根据key定位出Node数组的下标,如果Node数组位置为空则CAS写入,失败则自旋保证成功,如果hashcode==moved==-1则需进行扩容,如果以上都不满足,则利用synchronized锁写入数据,如果链表长度大于8且Node数组长度大于64则转红黑树。

get:不需加锁。Java7中,根据key计算hash值定位到segment,再通过计算hash值,定位到segment的HashEntry数组的下标。Java8中,根据key计算hash值,定位到Node数组下标,如果Node数组上key相同返回,否则按照链表或者红黑树的方式查找。

3. ArrayList与LinkedList

数据结构:ArrayList是数组,LinkedList是双向链表。

add:ArrayList是将数组下标i后面位置的元素拷贝到i+1后面的位置,腾出下标i,LinkedList在链表后面新建一个结点。

get:ArrayList按数组下标随机访问,LinkedList顺序查找。

扩容:ArrayList会自动扩容,每次扩容为原来数组长度的1.5倍。

Vector与ArrayList区别:vector线程安全,扩容2倍。ArrayList线程不安全,扩容1.5倍。

4. HashSet与HashTable

HashSet底层数据结构是HashMap,添加元素放在HashMap的key上,value放的是static的object对象,没有什么意义。

二 基础知识

1. TCP协议

(1)三次握手,四次挥手过程

初始状态,客户端处于CLOSED状态,服务端主动监听某个端口,处于listen状态。

第一次握手,客户端发送SYN报文,并初始化序列号X,客户端处于SYN_SEND状态。

第二次握手,服务端收到客户端的SYN报文,并设置ack=x+1,syn=y,将报文发送给客户端后,服务端处于SYN_RECV状态。

第三次握手,客户端收到服务端的SYN+ACK报文后,设置ack=y+1,并将报文发送给服务端后,客户端处于established状态,服务端收到ACK报文后,也处于established状态。

---------------------------------------------

第一次挥手:客户端打算关闭连接,发送一个FIN报文后,进入FIN_WAIT_1(客户端不发送数据了,但是还可以接收)

第二次挥手:服务端收到FIN报文,向客户端发送一个ACK报文后,服务端进入CLOSE_WAIT状态。客户端收到ACK报文后,进入FIN_WAIT_2状态。(等到服务端处理和发送完数据才将FIN发给客户端表示同意关闭连接)

第三次挥手:服务端处理完数据后,向客户端发送FIN报文后,服务端进入LAST_ACK状态。

第四次挥手:客户端收到服务端的FIN报文后,返回一个ACK报文后进入TIME_WAIT状态,服务端收到ACK报文后进入CLOSE状态,客户端在2MSL后进入CLOSE状态。

(2)为什么是三次握手?

可以阻止历史重复连接初始化(如果是历史连接第三次发送RST报文终止连接,否则发送ACK报文),三次握手才能同步双方初始序列号,三次握手才可以避免浪费资源

(3)为什么是四次挥手?

因为服务端需要等待数据处理和发送完,所以FIN和ACK分开发送。

(4)TIME_WAIT和CLOSED_WAIT

2MSL:报文最大生存时间,TCP报文基于IP报文的,IP报文头部有个ttl字段,每经过一个路由则-1,ttl为0则丢弃报文。MSL应该大于等于ttl消耗为0的时间。客户端收到服务端FIN报文后,发送ACK,开始计时,如果2MSL内,服务端没有收到ACK,反而客户端重新收到FIN报文,则重新计时。(MSL是报文最大生存时间,应该大于等于TTL消耗为0的时间,数据包发送给接收方后,接收方会响应给发送方,一来一回需要2MSL时间。)

TIME_WAIT状态:防止旧数据包被收到,保证被动关闭方最后能收到ACK。(如果服务端在关闭之前发送了数据包,而数据包延迟到达,若发生了端口复用,客户端会接收到这个错误的数据包。)

TIME_WAIT过多:内存资源占用,端口资源占用,则无法创建新的连接。

(5)TCP粘包拆包

为什么发生粘包拆包?发送数据大于剩余发送缓冲区大小则拆包,发送数据大于MSS,TCP传输前拆包,将多次写入缓冲区的数据一次性发出则粘包,接收端没有及时接收缓冲区数据则粘包。

怎么解决?消息定长,设置消息边界,将消息分为消息头和消息体。

(6)滑动窗口,流量控制,拥塞控制

重传机制:①超时重传,当数据包丢失或者ACK丢失,在指定时间内没有收到ACK就会重传。如果定时器过长,网络空隙大传输效率低,过短,可能发送频繁导致拥塞。②快速重传,收到三个相同的ACK,在定时器过期之前重传丢失报文,但是不知道传哪几个报文。③SACK,在TCP头选项字段里加SACK,只重传丢失的数据。④D-SACK,让发送方知道是发出去包丢了还是ACK丢了,是不是发送数据包延迟了,是不是发送数据包重复了。

滑动窗口:发送数据需要ACK,使用滑动窗口无需等待确认,可以继续发送,而是采用累计确认。

流量控制:接收方发送的确认报文中窗口字段用来控制发送方窗口大小,从而影响发送方发送速率。窗口字段为0,则不能发送数据,发送方时不时会发送窗口探测报文,来获取接收方的窗口信息。

拥塞控制:如果发送方没有在规定时间收到ACK,则发生了超时重传,会认为出现了网络拥塞。拥塞控制避免发送方的窗口填满整个网络。发送窗口=min(接收窗口,拥塞窗口)

①慢启动:拥塞窗口指数增长1,2,4,8,直到一个 阈值

②拥塞避免:拥塞窗口达到阈值后,线性增长,8,9,10,直到超时重传发生。当发生拥塞接着慢开始,更新慢启动的阈值为拥塞窗口一半。

(7)TCP与UDP特点和区别

UDP无连接,尽最大努力交付,不保证数据可靠交付,无拥塞控制,面向报文,支持一对一,一对多,多对多通信。

TCP面向连接,保证可靠交付,有拥塞控制和流量控制,面向字节流,支持一对一。

(8)半连接队列与全连接队列满了怎么办

全连接队列满:丢弃,向客户端发送RST中止连接

(9)TCP报文信息

源端口,目的端口,序列号,确认应答号,控制位,窗口大小,校验和,数据部分。

2.Http协议

(1)地址栏输入URL发生了什么

第一步:解析URL,封装http请求报文。

第二步:DNS查询

第三步:传输层TCP

第四步:网络层IP

第五步:数据链路层MAC

(2)https工作原理以及加密算法(对称加密与非对称加密)

 

(3)http头部字段,状态码,方法

通用标头:date时间,cache-control缓存控制,connection-keepalive(持久性连接)或者close(非持久连接)

(4)get和post的区别

get一般用于请求,post一般用于表单提交

get请求参数拼接在URL后面有长度限制容易被窃取,而Post请求参数在Body里面,没有长度限制,对用户是不可见的。

get请求会被浏览器主动cache,而post不会需要手动设置

get反复前进后退是无害的,而post会出现表单重复提交

get请求过程会产生一个TCP数据包,而post浏览器先发送header,服务器响应100continue,浏览器在发送data,服务器响应200ok。

3. 其他知识点

(1)线程与进程区别

进程是资源分配的基本单位,线程是CPU调度的基本单位。

进程拥有完整的资源,线程只独享寄存器,堆栈等资源。

(2)进程间怎么通信

管道:创建匿名管道时,管道存在于内核的一串缓存,会返回写入和读取描述符,然后将父进程进行fork,父进程和子进程同时只保留一个写入和读取描述符,不适合频繁的交换数据。

消息队列:消息队列时保存在内核中的消息链表,生命周期跟随内核,可以频繁交换数据。但是通信不及时,消息队列空间有限,不适合传输大数据,存在用户态与内核态之间的数据拷贝。

共享内存:拿出一块虚拟地址空间,映射到相同的物理空间内存中。

信号量:共享内存存在内存竞争的问题,信号量来解决互斥同步问题。信号量有两种原子操作,P操作进入共享资源之前,执行-1,信号量<0资源占用,>=0资源可使用。V操作离开共享资源之后,执行+1,信号量<=0有进程阻塞需要唤醒,>0没有阻塞进程。

(3)线程与进程状态变化

进程:创建状态,就绪状态,运行状态,阻塞状态,结束状态。

NULL->创建状态:一个新进程被创建

创建状态->就绪状态:进程初始化完成后,等待操作系统调度。

就绪->运行:进程被操作系统调度,分配CPU运行进程

运行->就绪:进程获取的时间片用完了,操作系统将其设置为就绪态等待下一次调度。

运行->结束:进程执行完成或者出错。

运行->阻塞:进程等待某个事件,比如等待IO操作

阻塞->就绪:进程等待的事件完成。

线程状态变化与进程相似,线程调度算法有,先来先服务,最短作业优先,时间片轮转,最高优先级,多级反馈队列。

(4)死锁,怎么解决

什么是死锁:线程A持有a锁之后还想要b锁,线程B持有b锁之后还想要a锁,两个线程一直处于等待状态。

死锁原因:两个进程竞争了对方不可剥夺的资源,进程间推进顺序非法。

死锁产生四个必要条件:互斥条件(资源一次只能被一个进程持有),保持并等待条件(进程已经持有一个资源并等待另一个资源),不可剥夺(进程完成任务之前,资源不可被剥夺),循环等待(存在多个进程相互等待)。

如何预防死锁:破坏请求条件(一次性分配所有资源),破坏保持条件(只要有一个资源得不到分配,则不给该进程分配其他资源),破坏不可剥夺条件(如果进程得不到其他资源,则释放自己占有的资源),破坏循环等待(进程按照资源顺序获取资源)。

如何避免死锁:预防死锁的四种策略,银行家算法。

检测死锁:jstack,jconsole

解除死锁:剥夺资源,撤销进程。

银行家算法:进程首次申请资源,如果系统可用资源大于申请资源,则分配,否则推迟分配。当进程在执行过程中继续申请资源,若本次资源超过了该资源剩余总量则拒绝分配,否则分配资源。

(5)内存泄露怎么排查

(6)CPU100%怎么排查

CPU负载:可运行和等待运行进程数

CPU利用率:正在运行的进程占CPU百分比

使用top命令查看占用率高的进程,top -Hp pid查看占用CPU高的线程ID,将线程ID转16进制,通过命令jstack 163 | grep ‘0x3be’找到有问题的代码

(7)RPC

(8)红黑树

(9)网络IO与IO复用模型

(10)零拷贝

三 多线程与并发

1.  synchronized使用

修饰实例方法时,对实例对象加锁,修饰静态方法时,对类对象加锁,修饰代码块时,对传入对象加锁。

2. synchronized底层原理

修饰代码块:使用monitorenter和monitorexit指令,monitorenter指令指向同步代码块的开始位置,monitorexit指令指向同步代码块的结束位置。执行monitorenter指令时,线程尝试获取锁,若计数器为0则可以获取成功,获取后计数器+1,执行monitorexit指令后计数器设为0,表示释放锁。如果获取锁失败,则线程进入阻塞队列,直到锁被其他线程释放。

修饰方法:修饰方法并没有monitorenter和monitorexit指令,而是通过ACC_SYNCHRONIZED访问标识表明该方法需要同步,隐式调用monitorenter和monitorexit指令。

3. synchronized锁升级过程

无锁到偏向锁:当锁对象第一次被线程获取时,虚拟机会把对象头Mark Word中的标志位设置为01,同时用CAS尝试将线程id记录在对象头中。如果CAS成功则,加锁成功,若失败则表示有其他线程竞争锁,则升级为轻量级锁。当锁对象第二次被线程获取时,先比较对象头中的线程id与当前线程id是否相等,如果相等直接进入同步代码,否则CAS尝试修改对象头的线程id,如果CAS成功则获取锁成功,否则升级为轻量级锁。

偏向锁到轻量级锁:在轻量级锁状态下,当前线程会在栈帧建立一个LockRecord,用于存储锁对象头的Mark Word的拷贝,并将Lock Record的owner指针指向锁对象,再CAS尝试将对象头的Mark Word指向栈帧的Lock Record。CAS修改成功则获取轻量级锁,如果CAS失败,则自旋重试一定次数后,升级为重量级锁。

重量级锁:依赖操作系统mutex,会发生用户态到内核态的切换。

4. synchronized与Lock区别

synchronized是JVM层面的,Lock是JDK层面的。

synchronized是非公平锁,Lock可以设定为公平锁与非公平锁。

synchronized不可以中断等待锁的线程,Lock能够中断等待锁的线程。

synchronized锁自动释放,Lock需手动释放。

synchronized可以锁代码块和方法,Lock只能锁代码块。

synchronized是独占锁,Lock提供读锁来实现多线程读。

5. ThreadLocal

通常主存中的变量是共享的,为了让线程都有自己专属的本地变量,ThreadLocal类可以让线程拥有自己的私有变量,内部使用ThreadLocalMap来实现。ThreadLocalMap中key为ThreadLocal的弱引用,value是强引用。

6.  线程池

Java可以通过继承Thread类,实现Runnable接口和Callable接口。通过ThreadPoolExecutor来创建线程池。

线程池参数:corePoolSize,maxPoolSize,workQueue等。新任务进来需要获取线程来执行,如果运行的线程数量小于corePoolSize,则创建新线程来执行新任务。如果运行的线程数大于corePoolSize,接着判断workQueue阻塞队列是否已满,如果还没满,则将新任务放到阻塞队列中,如果阻塞队列满了,则判断运行的线程数是否大于maxPoolSize,如果不大于则创建新的线程执行任务,如果大于则执行饱和策略。当线程池中的线程数量大于核心线程数,且没有新任务要执行,核心线程外的线程不会立马销毁,而是等待,直到时间超过keepAliveTime才会被销毁。

饱和策略:抛异常拒绝新任务(AbortPolicy),不处理任务丢弃(DiscardPolicy),丢弃最早未处理的任务(DiscardOldestPolicy),当触发拒绝策略只要线程池没关闭,则使用调用线程直接运行任务,并发小,性能不高,不允许失败。(CallerRunsPolicy

7. AQS

AbstractQueueSynchronizer是实现锁的框架,内部维护了一个先进先出队列和一个state状态变量。state=0表示未锁定状态,调用lock()时,CAS尝试获取锁,成功则获取锁,失败则调用acquire()。如果state=0,CAS尝试获取锁,失败则判断当前线程是否持有锁,未持有锁则构造节点添加到队尾。如果前驱节点是头节点CAS获取锁,不是则将前驱节点waitStatu修改为signal,当前线程挂起。ReentrantLock的state是重入次数,ReentrantReadWriteLock的state高16位表示获取读锁的次数,低16位表示写锁的重入次数。

Semaphore(信号量):允许多个线程同时访问资源。比如路口堵车,一次发放5个通行证,获得通行证的车可以通过路口。

CountDownLatch(倒计时器):允许一个线程等待其他线程操作完成后再执行。每当有线程调用countDown,则计数器减1,计数器为0,则被阻塞的线程才继续执行。

CyclicBarrier(循环栅栏):当线程到达屏障,调用await后阻塞,当最后一个线程到达屏障,所有阻塞的线程才会唤醒继续执行。

四 JVM

1. jvm运行时数据区

虚拟机栈:线程隔离,主要保存执行方法时的局部变量,操作数栈,动态链接,方法出口。不需要GC,不会OOM,但会SOM。

本地方法栈:线程隔离,为虚拟机执行本地方法服务,不需要GC。

程序计数器:线程隔离,当前线程执行字节码行号指示器,不需要GC。

堆:线程共享,存放数组和实例对象,需要GC。

本地内存:Java8以前,在堆上用永久代实现了方法区,受GC管理,Java8以后方法区移到了本地内存的元空间中,不需要GC。方法区存放静态变量,常量,类型信息,其中运行时常量池存放编译器生成的字面量和符号引用。

2. 垃圾回收

(1)垃圾识别:

①引用计数法:当对象被引用一次,就将对象头里面记录的引用次数加1,如果引用次数为0,则对象可回收。

②可达性算法:使用虚拟机栈,本地方法栈,方法区常量和静态变量引用的对象作为GC ROOT根节点,根据对象之间的引用关系,形成引用链,如果对象不在任意一个引用链上,则表示对象可回收。如果对象第一次GC,会给一次垂死挣扎的机会,执行finalize方法,如果对象进入引用链,则不会被GC,但如果对象再次被GC,则直接回收。

(2)垃圾回收算法:

①标记清除:根据可达性算法标记并回收对象,不需要移动,但有内存碎片问题。

②标记复制:将内存空间平分成AB两个区域,比如对象分配在A区域,对A区域进行标记清除后,将存活的对象移动到B区域。内存实际使用只有一半,需要移动对象。

③标记整理:对内存中的对象进行标记清除后,将存活对象往一端移动。需要移动对象,移动对象会触发STW。

④分代收集:

空间划分:将堆分成老年代和新生代区域(2:1),新生代又分成eden区,s0区,s1区(8:1:1),对老年代的GC称为Full GC,对新生代的GC称为Young GC。

分配回收:对象首先在Eden区分配,当Eden区将满会触发young GC,将存活对象分代年龄加1并移动到s0区。当发生下一次young GC时,将eden和s0区存活的对象分代年龄加1,移动到s1区。

对象晋升:当对象分代年龄到达设定阈值,大对象直接分配在老年代区,s0或s1区相同年龄的对象超过一半以上,则大于等于该年龄的对象晋升老年代。

分配担保:发生young GC之前,如果老年代的最大连续空间大于新生代所有对象的总空间,则安全young GC。否则,虚拟机查看是否允许担保失败,如果允许且老年代最大连续空间大于历次晋升到老年代对象的平均值,则安全young GC。否则触发full GC。

STW:在GC期间,只有垃圾回收线程在工作,其他工作线程挂起。Full GC会清理整堆,STW时间较长,尽可能避免对象过早进入老年代。

safe point:开始GC的地方称为safe point。主要是循环末尾,方法返回前,异常抛出位置,调用方法的call之后。

(3)垃圾收集器

①CMS:老年代收集器,标记清除算法。初始标记阶段,标记GC Roots能关联的对象,单个标记线程,会STW。并发标记阶段,进行GC Roots Tracing,单个标记线程与工作线程一起运行。重新标记阶段,修正并发标记阶段用户线程运行导致标记变动的那部分对象的标记记录,多个重新标记线程,会STW。并发清除阶段,清除线程与工作线程一起运行。缺点:需要线程较多,占用CPU资源,无法处理浮动垃圾,空间碎片问题。

②G1:它将空间分成大小相等的空间连续的region区,每个region区根据需要扮演老年代,新生代区和大对象区。根据region区垃圾堆积的价值大小维护一个优先列表,优先收集回收价值大的region区,减少了STW时间。初始标记,并发标记,最终标记,筛选回收。工作步骤与CMS相同,不同点是筛选回收需要STW。

3. 类加载机制

加载:通过类全限定名来获取二进制字节流,将字节流所代表的静态存储结构转化为方法区的运行时存储结构,在内存中生成这个类的class对象。

验证:确保字节流包含的信息是否符合虚拟机的规范,并且不会危害虚拟机安全。

准备:为类中定义的静态变量分配内存并进行初始化。

解析:将常量池中符号引用转为直接引用。

初始化:根据程序员的意图对变量和资源进行初始化。

五 中间件

1. redis

(1)数据结构:①string,list,set,sorted set,hash,bitmap,hyperloglog,geospatial

(2)缓存淘汰策略:FIFO淘汰最早数据,LRU淘汰最近最少使用,LFU淘汰最近使用频率最低。

(3)key失效机制:访问时触发被动删除,定期主动删除。

(4)memcache和redis比较:memcache支持多线程异步IO,不支持持久化,仅支持string,不支持主从同步。redis单线程处理请求,支持持久化,数据结构丰富,支持主从同步。

(5)持久化:RDB下,redis通过fork一个子进程,与父进程共享数据区和代码段,redis持久化交由子进程处理,父进程继续处理请求,如果有写请求,则将数据段页面复制一份,主进程与子进程各持有一份数据。对redis性能影响小,恢复快,但是可能出现长时间的数据丢失。AOF下,将写命令追加到日志末尾。最多丢失1秒数据,但是AOF文件太大,数据恢复慢。

(6)高可用:①选主策略:从节点priority设置越低优先级越高,数据越多优先级越高,runid越小越容易选中。②哨兵:监控集群,消息通知故障,故障转移,配置中心。③主从同步:从节点第一次连接上主节点,会触发主节点全量复制生成RDB快照,并将新的写请求缓存到内存中,然后将RDB发送给从节点,从节点将RDB写进本地磁盘并加载到内存。然后刚缓存的写操作通过AOF文件发送给从节点进行重放,之后主从节点通过AOF进行数据同步。

(7)常见问题:①缓存雪崩:缓存挂了,请求都打到DB上,通过主从模式和集群模式保证高可用,快速熔断机制。②缓存穿透:id=-1,对参数进行检查或布隆过滤器。③缓存击穿:热点数据失效,避免key集中失效,互斥锁更新,随机退避。

六 数据库

1.  InnoDB和MyISAM比较

innoDB支持事务,支持表锁行锁,支持外键,支持在线热备,MyISAM不支持事务,不支持外键,仅支持表锁,支持压缩表和空间数据索引。

2. Hash表,二叉查找树,平衡二叉树,红黑树,B树,B+树

hash表:等值查询效率高,但不支持范围查找,范围查找只能通过全表扫描。

二叉查找树:可支持折半查找,但是如果树不分叉,则变成线性查找

平衡二叉树:左右子树高度差不超过1。时间复杂度与树高相关,不支持范围查找。

红黑树:通过路径上节点的颜色进行约束,确保没有一条路径比其他路径长两倍。但是一个节点存一个元素。

B树:树节点存放多个元素,每个元素包含键值和数据,父节点元素不会出现在子节点,所有叶子节点在同一层,之间没有指针连接。不支持范围查找,一页数据量有限。

B+树:只有叶子节点存储数据,非叶子节点存储键值,叶子节点使用双向指针连接。支持范围查找。

3. Mysql索引

(1)innoDB:B+树索引,Hash索引

MyISAM:全文索引(用于查找文本中的关键字),空间索引

(2)独立的列:不要让索引字段成为表达式的一部分和函数参数。

覆盖索引:在索引上查找自己想要的字段就不会出现回表。

联合索引:当多个列作为查询条件时,多列索引比单个索引号。

最左前缀原则:查询语句的条件字段顺序是否与联合索引的字段顺序保持一致。

前缀索引:对于BLOB,TEXT,VARCHAR的字段可以建立前缀索引。

索引下推:select * from itemcenter where name like '敖%' and size=22 and age = 20;MySQL5.6以前,找到主键后回表去主键索引找到数据行,再拿size和age比较。mysql5.6以后,可以通过联合索引先过滤一部分主键。

(3)唯一索引和普通索引选择之change buffer

当要更新一个数据页时,如果数据页在内存中则直接更新,否则缓存到change buffer中,在下次访问这个数据页,将数据页加载到内存,然后执行change buffer与页有关的操作。而唯一索引更新操作都需要检查是否违反了唯一性约束,所以唯一索引不能使用change buffer,普通索引支持change buffer。

(4)explain关键字

select-type:simple简单查询,union联合查询,subquery子查询

table:要查询的表

possible_keys:可选择的索引

key:实际使用的索引

rows:扫描的行数

type:索引查询类型。const主键或唯一索引,ref非唯一索引,range范围查找,index扫描索引树,all扫描全表

(5)索引失效的情况:or,like '%三',索引列顺序,函数参数,参与计算,使用!=,<>,not in

4.事务

(1)原子性:事务是不可分割的最小单位,事务的所有操作要么全部成功要么全部失败回滚。一致性:数据库在事务执行前后都保持一致性状态,一致性下,所有事务对一个数据的读取结果都是相同的。隔离性:一个事务在修改提交前对其他事务不可见。持久性:一旦事务提交,其所作的修改将被永远保存在数据库中。

(2)事务隔离级别:①未提交读,会脏读,不可重复读,幻读,②已提交读,会不可重复读,幻读,③可重复读,会幻读,④可串行化。

脏读:A事务读到了B事务还未提交的数据。

不可重复读:同一事务内多次读取同一数据,读出不同的结果

幻读:同一事物下,连续执行查询,第二次查询返回了之前不存的行。

5. MVCC

版本号:

系统版本号:是一个递增数字,没开启一个新事务,系统版本号自动递增。

事务版本号:事务开始时的系统版本号。

隐藏列:

MVCC 在每行记录后面都保存着两个隐藏的列,用来存储两个版本号:

创建版本号:即事务id,表示创建一个数据行的快照时的系统版本号。

删除版本号:如果该快照的删除版本号大于当前事务版本号表示快照有效,否则表示快照已经删除。

MVCC实现了已提交读和可重复读两个事务隔离级别。MVCC是通过每行记录后面的两个隐藏列(事务id,回滚指针)来实现的。

(insert/update)undo log,readview主要包含当前活跃的读写事务id。在已提交读级别下,事务在每次查询都会生成一个独立的readview,在可重复读级别下,事务开始后第一次读取数据生成一个readview。MVCC是为了避免select加锁,insert,update,delete需要加锁。

6.锁

算法:

Record Lock:锁定一个记录上的索引,而不是记录本身。

Gap Lock:锁定记录索引之间的间隙,但不包含记录索引本身。

Next-Key Lock:记录锁与间隙锁的结合,不仅锁定当前记录,还锁定当前记录的间隙。(锁的是索引,不是记录)

innoDB存储引擎中,select不可重复读通过MVCC解决,update和delete不可重复读通过Record Lock解决,insert不可重复读通过Next-Key Lock解决。

全局锁:在做全库逻辑备份时,加全局锁。

7.其他知识

范式:第一范式属性不可分,第二范式每个非主属性完全依赖键码,第三范式非主属性不传递函数依赖键码

主从复制:binlog线程将主服务器数据写入二进制文件。I/O线程负责从主服务器读取二进制文件,并写入从服务器的中继日志。SQL线程读取中继日志,解析主服务器的数据更改在从服务器重放。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值