JVM 面试题笔记

JVM 全局构架图如下,包含 JVM 的全部内容 1. JVM 的内存区域是如何划分的? JVM 内存当中,运行时数据的区域包含堆、方法区(元空间)、虚拟机栈、本地方法栈、程序计数器。另外,其他的内存区域属于本地内存,本地内存就包括直接内存,直接内存是非运行时数据区的一部分。 其中,堆和方法区(元空间) 是线程共享的,虚拟机栈、本地方法栈、程序计数器都是线程私有的。 【注意】 JVM 规范对于运行时数据区域的规定是很宽松的。就拿堆来说,堆可以是连续空间,也可以是不连续空间。堆的大小可以固定,也可以再运行时按照需求进行扩展。虚拟机实现者可以使用任何垃圾回收算法管理堆,甚至完全不进行垃圾回收也是可以的。 下面逐一介绍不同部分的功能和作用 首先是线程私有的部分,程序计数器、虚拟机栈、本地方法栈。 【程序计数器】 定义:程序计数器就是记录当前所执行的字节码的行号。字节码解释器通过改变程序计数器的值,来选取下一条需要执行的字节码指令。它的生命周期随着线程的创建而创建,随着线程的结束而死亡。 作用:分支、循环、跳转、异常处理、线程恢复这些功能都需要依赖程序计数器完成。比如线程切换上下文中,从 A线程 先切换到 B线程,然后从 B线程 恢复到 A线程 的过程当中,为了 A线程 能够恢复到正确的执行位置,就需要读取 A线程 程序计数器的值,来确认切换线程前执行的位置在哪里。 为什么是线程私有:因为线程切换的过程当中,每个线程都需要一个程序计数器来记录自己的程序执行位置。 【注意】 程序计数器是唯一一个不会出现 OOM 内存不足的内存区域,因为他就只是存储一个值。 【虚拟机栈】 定义:虚拟机栈按照先进后出的方案存储的是非本地方法的调用对应的栈帧。每一次方法调用的时候会被压入栈中,方法结束的时候会被弹出栈中。栈帧中包含局部变量表、操作次数栈(存储操作数和临时计算结果)、动态链接、方法的返回地址。虚拟机的生命周期随着线程的创建而创建,随着线程的结束而死亡。 作用:存储非本地方法的栈帧,支持方法的调用和返回。当栈深度超过虚拟机允许的最大深度,会抛出 StackOverflowError 栈溢出异常。如果虚拟机栈无法动态扩展或申请到足够的内存,会抛出 OutOfMemoryError 内存不足异常。 栈帧中包含局部变量表、操作次数栈(存储操作数和临时变量)、动态链接、方法的返回地址。其中,局部变量表存放的是所有的局部变量,或者其出对应的地址(数组)。动态链接就是当前类常量池的引用。 局部变量表:存放方法参数传入的形参值,方法内的局部变量、方法的 this 引用。内部结构主要存放了编译期可知的各种数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference 类型,它不同于对象本身,可能是一个指向对象起始地址的引用指针) 操作数栈:主要作为方法调用的中转站使用,用于存放方法执行过程中产生的中间计算结果。另外,计算过程中产生的临时变量也会放在操作数栈中。 动态链接:主要作用是实现在当前方法中调用其他方法。 Class 文件的常量池里面保存有大量的符号引用,比如方法的符号引用。 当一个方法需要调用其他方法,需要将常量池中只想方法的符号引用转换为在内存地址当中的直接引用。动态链接的作用就是把常量池的符号引用转换为内存当中的直接引用。 【本地方法栈】 定义:本地方法栈和虚拟栈的结构是一样的。不同的是,本地方法栈是 JVM 调用 Native 方法的时候才会用到的,虚拟机栈是执行 Java 自身的方法 (也就是字节码) 会用到的。 作用:存储本地方法 (Native) 栈帧,支持方法的调用和返回。 【注意】 在 HotSpot 虚拟机当中,将本地方法栈和虚拟机栈合二为一了。 ...

March 22, 2025 · 4 min · SwimmingLiu

计算机网络面试题笔记

1. TCP 和 UDP 有什么区别? TCP 是一种可靠的、面向连接、基于字节流传输的传输协议,能够保证数据的顺序,但是其延迟比较大。一般适用于需要数据完整性和顺序的场景,比如文件传输、邮件、Web 网站。 UDP 是一种不可靠的、无连接、基于数据报传输的传输协议,不能保证数据的顺序,但是延迟比较小。一般适用于需要高性能要求和快速传输数据的应用,比如实时通讯、语音、视频、游戏等。 【注意】TCP 和 UDP 都属于传输层 特性 TCP UDP 连接方式 面向连接 (三次握手) 无连接 可靠性和顺序保证 可靠,保证数据按顺序送达 不可靠,不能保证数据的顺序,而不能保证安全送达 流量控制/拥塞控制 提供流量控制和拥塞控制 没有流量控制和拥塞控制 头部大小 头部较大 (20 ~ 80 字节) 较小 (只有 8 字节) 性能 较低,延迟大 较高,延迟小 是否支持广播 不支持广播 支持广播 数据传输模式 通过字节流传输 通过数据报传输 适用场景 文件传输、Web、邮件等需要可靠性的传输 实时通讯、语音、视频、游戏等高性能要求应用 基于两者的协议 1. HTTP协议:超文本和多媒体内容的协议 2. HTTPS协议:基于HTTP协议上加了一层SSL / TSL 外壳,保证了数据传输的安全性 3. FTP协议: 文件传输协议,用来传文件到服务器的 4. SMTP协议:简单邮件协议,用于发送邮件的协议 (POP3 协议: 负责邮件接受的协议) 1. HTTP 3.0 协议: 基于 UDP 的QUIC 协议 2. DHCP 协议: 动态主机配置协议,动态配置 IP 地址 3. DNS :域名解析系统,将域名转变为机器刻度的 IP 地址 【ISO 和 TCP/IP 模型】 ...

March 13, 2025 · 7 min · SwimmingLiu

设计模式面试题笔记

1. 单例模式有哪几种实现?如何保证线程安全? 首先,单例模式和工厂模式都是一种设计模式。单例模式当中,一个类只允许创建一个对象(或者说实例), 那这个类就是单例类。单例类是不可以被继承的,也没有了多态的特性。 【单例类的实现方式】 常规单例模式有五种写法,但是编写代码的过程当中,要注意以下几点: 构造器需要私有化 暴露一个公共获取单例对象的接口 (obj.getInstance()) 是否支持懒加载 延迟加载 是否线程安全 五种写法为: 饿汉式: 类加载的时候,就一起把 instance 静态实例创建好了,所以创建的过程市线程安全的。 饿汉式的单例模式虽然不支持懒加载,有点浪费资源。但其实不会占用太多资源,并且如果一个实例初始化的过程比较复杂,就应该放在启动的时候来处理,避免运行时卡顿或发生问题, 满足fail-fast 失败快速解决的设计原则 public class EagerSingleton { private static Singleton instance = new Singleton(); private Singleton (){} public static Singleton getInstance() { return instance; } } 懒汉式:相较于饿汉式的方式,修改成延迟加载的模式。注意getInstance()方法没有上锁的话,在大量线程并发请求的时候,可能创建多个实例。 public class Singleton { private static Singleton instance; private Singleton (){} public synchronized static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } 双重检查锁:饿汉式锁不支持延迟加载,然后懒汉式锁的粒度比较大,不支持高并发。双重检查锁可以实现既延迟加载,又支持高并发。其实就是在判断了没有实例之后,再进行上锁,创建实例。 但是实例必须用volatile 修饰,不然new 操作创建对象时,容易出现重排序的问题。 ...

March 7, 2025 · 5 min · SwimmingLiu

(JUC) Java并发面试题笔记

1. 什么是 Java 内存模型(JMM)? JMM 是 Java内存模型 , 它是Java虚拟机 JVM 定义的一种规范,用于描述多线程程序中的变量,像实例字段、静态字段和数组元素,他们如何在内存中存储和传递的规则。也就是规定线程啥时候从住内存里面读数据,啥时候把数据写回到住内存。JMM 的核心目标是确保多线程环境下的可见性、有序性和原子性, 避免硬件和编译器优化带来的不一致问题。 JMM 当中包含主内存 (所有线程共享) 和 工作内存 (每个线程私有)两种内存。 主内存:主内存是Java堆内存的一部分,是线程共享的区域,存储全局变量,比如静态变量、实例字段、数组元素等等。线程不能直接操作主内存,必须通过工作内存间接访问。 工作内存:每个线程私有的本地内存,缓存主内存的变量副本。线程对变量的读写操作均在工作内存种进行,修改后的结果需要同步会主内存。线程是通过内存屏障 ( volatile 关键字) 或者 同步操作 ( synchronized ) 实现主内存和工作内存的数据一致性的。 【可见性、有序性、原子性定义】 可见性:确保一个线程对共享变量的修改,能够及时被另外一个线程看到。 volatile就是用来保证可见性的,强制线程每次读写的时候,直接从主内存当中获取最新值。 有序性:指线程执行操作的顺序。JMM允许某些指令重排序之后再提高性能,但会保证线程内的操作顺序不会被破坏。通过happens-before (线程A发生在线程B之前)的关系来保证跨线程的有序性。 原子性:操作不可分割,线程不会在执行的过程当中被打断。例如, synchronize 关键字能确保方法或代码块的原子性 【JMM作用】 因为不同的操作系统都有一套独立的内存模型,但是Java为了满足跨平台的特性,它需要定义一套内存模型屏蔽个操作系统之间的差异。我们可以利用JMM当中定义好的从Java源码到CPU指令的执行规范,也就是使用synchronized 、volatile 等关键字,还有happens-before原则,就可以写出并发安全的代码了。 比如说,线程A和线程B同时操作 变量-1,假如最开始变量-1 是 0 首先,线程A和线程B都读取了变量-1 然后,线程B对取到的变量-1自增为1,并写回主内存 此时,线程A对读取到的变量-1也自增1,并写回主内存。这就会导致线程B的操作失效了,出现并发安全问题。 如果有JMM,我们就可以在线程A要修改数据之前,让它采用CAS乐观锁的方式进行修改。再次去读主内存当中的值,然后修改之后,再判断一下主内存的值是否发生变化。如果没有发生变化,就写回主内存。如果发生变化,就要进行自旋。 【注意】 工作内存是每个线程独立的内存空间,其他线程都是看不到的。主内存是Java堆内存的一部分,所有的实例变量、静态变量和数组元素都存储在主内存当中。 【内存间交互操作类型 (8种原子操作)】 lock 上锁:把一个变量表示为一条线程独占的状态 unlock 解锁: 把一个变量从独占状态中释放出来,释放后的变量才能被其他线程锁定 read 读取: 从主内存当中读取一个变量到工作内存中 load 载入:把read操作从主内存中得到的变量值放入工作内存的变量副本当中 use 使用:把工作内存当中的一个变量值传递给执行引擎 assign 赋值:把一个从执行引擎接收到的值赋给工作内存中的变量 store 存储:把工作内存中的一个变量的值传送给主内存中 write 写入:把store操作从工作内存中得到的变量值放入主内存的变量中 【volatile 特殊规则】 ...

March 5, 2025 · 14 min · SwimmingLiu

操作系统面试题笔记

1. 说说你知道的几种 I/O 模型 【常见的五大I/O模型】 常见的五大I/O模式分别为: 同步阻塞I/O (Blocking I/O) BIO、非阻塞I/O (Non-blocking I/O) NIO、I/O多路复用、信号量驱动I/O、异步I/O AIO 我们假如要烧水喝,看不同模型是怎么烧的水喝 I/O 模型 特性 烧水案例 同步阻塞I/O BIO 数据从网卡到内核,再从内核到用户空间,都是阻塞操作。 自己动手烧水,一直盯着,等水烧开了,倒在杯子里喝。 非阻塞I/O NIO 数据从网卡到内核不阻塞,read不到数据直接返回,但是从内核到用户空间会阻塞 (用户轮询read) 自己动手烧水,隔两分钟看一下,水烧开没有。等水烧开了,倒在杯子里喝。 I/O多路复用 只有一个线程查看多个连接是否有数据准备就绪 (看从网卡能不能read到数据到内核) 找专门烧水的领居帮忙,他把水烧好了之后,会喊你来拿。但是你要自己倒在杯子里喝。 信号驱动I/O 数据从网卡到内核之后会自动通知用户程序,然后让他read读取数据 去烧水房烧水,全自动的,有个通知灯。水烧完了之后会按你家的门铃,但是有客人来了,也会按门铃 异步I/O AIO 全程不阻塞,拷贝到用户空间之后直接回调。 和多路复用类似,但是烧完水之后不用自己倒水,他帮你倒好了,还吹凉了,你过来喝就行。 【为什么会产生各种I/O】 下图是两个不同主机上,应用程序传递数据的过程,借助该过程来理解 I/O 是如何产生的 DMA(直接内存访问)是一种不经过CPU直接在网络适配器(网卡)和主机内存之间进行数据传输的机制,用于提升数据传输效率。 【同步阻塞 I/O BIO】 同步阻塞I/O BIO 的工作机制:应用程序被阻塞,直到数据复制到应用进程的缓冲区才返回。阻塞并意味着整个操作系统都被阻塞。其他程序还可以执行,不消耗CPU事件。同步阻塞 I/O BIO 中,应用程序发起 read 调用来读取数据之后,一直被阻塞,直到内核把数据copy到用户空间。该方案适合客户端连接数量不高的情况。下图的read 和 recvfrom 函数是一个意思。 【非阻塞式 I/O NIO】 非阻塞式 I/O NIO 的工作机制:应用程序执行read 系统调用之后,内核返回一个错误码。应用程序可以继续执行,但是需要不断的轮询 read 来获取 I/O是否完成,这种方式称之为 轮询 polling 。等到数据准备就绪,从内核空间copy到用户空间的时候,进程才被阻塞,直到内核copy完成。该方案比较低效,会不停的消耗CPU资源。 ...

March 1, 2025 · 3 min · SwimmingLiu

个人简历常问问题

个人简历详情查看 -> 个人简历页 1. 异步秒杀机制的异步是如何实现的? 【正常秒杀的顺序】 查询优惠券 -> 判断秒杀库存 -> 查询订单 -> 校验是否一人一单 -> 扣减库存 -> 创建订单 【异步秒杀】 为了实现用户异步下单,其实就是把是否能够下单的判断逻辑和下单的操作拆分开。 采用Redis来判断是否有足够的库存和校验一人一单 如果满足条件,把用户、订单id、商品id保存到阻塞队列,直接给用户返回秒杀成功。 如果不满足条件,直接返回秒杀失败。 后台线程会去执行queue里边的消息 这样就可以实现异步的秒杀下单了,那么如果实现判断秒杀库存和校验一人一单呢? 【秒杀库存 + 一人一单】 用户下单之后,判断redis当中的库存key的value是否大于0 value > 0 -> 第2步 value <= 0 -> 直接返回库存不足 (返回1) 如果库存充足,判断redis当中的秒杀商品key的 set 集合是否已包含userid 包含userid, 说明用户已经下单了,直接返回当前用户已下单 (返回2) 不包含 userid -> 第3步 如果用户没有下单,将用户的 userid 存入 set 里面 (返回0) 【注意】 整个操作是原子性的,这样就确保了不会出现超卖现象和一人多单现象 -- 1.参数列表 -- 1.1.秒杀商品id local voucherId = ARGV[1] -- 1.2.用户id local userId = ARGV[2] -- 1.3.订单id local orderId = ARGV[3] -- 2.数据key -- 2.1.库存key local stockKey = 'seckill:stock:' .. voucherId -- 2.2.秒杀商品订单key local orderKey = 'seckill:order:' .. voucherId -- 3.脚本业务 -- 3.1.判断库存是否充足 get stockKey if(tonumber(redis.call('get', stockKey)) <= 0) then -- 3.2.库存不足,返回1 return 1 end -- 3.2.判断用户是否下单 SISMEMBER orderKey userId if(redis.call('sismember', orderKey, userId) == 1) then -- 3.3.存在,说明是重复下单,返回2 return 2 end return 0 【阻塞队列实现下单】 ...

February 27, 2025 · 1 min · SwimmingLiu

Redis面试题笔记

1. Redis主从复制的原理 【主从复制的原理】 同步:从节点向主节点发送psync命令进行同步,从节点保存主节点返回的 runid 和 offset 全量复制:如果是第一次连接或者连接失败且repl_backlog_buffer 缓存区不包含slave_repl_offset, 则生成主节点的数据快照(RDB文件)发给从节点 增量复制:全量复制完毕后,主从节点之间会保持长连接。如果连接没有断开或者slave_repl_offset仍然在repl_backlog_buffer中,则将后续的写操作传递给从节点,让数据保持一致。 【全量复制细节】 全量复制的过程是基于TCP长连接的,主要流程如下 从节点发送psync ? -1表示需要建立连接进行同步,主节点返回主节点ID runid 和 复制进度offset (第一次同步用 -1 表示)。从节点接受之后,保存主节点的信息。 主节点执行bgsave命令生成数据快照RDB文件,然后将RDB文件发送给从节点。从节点接受文件后,清除现有的所有数据,然后加载RDB文件 如果在制作数据快照RDB文件的过程当中,主节点接收到了新的写操作,主节点会将其记录在repl buffer 里面。然后将repl buffer当中的写操作发给从节点,让其数据保持一致。 【增量复制细节】 如果主从节点意外断开连接,为了保持数据的一致性,必须重新同步数据。如果使用全量复制来保持一致性的话,开销太大,所以采用增量复制。 增量复制的具体流程如下: 连接恢复后,从节点会发送psync {runid} {offset}, 其中主节点ID runid 和 复制进度offset用于标识是哪一个服务器主机和复制进度。 主节点收到psync 命令之后,会用conitnue响应告知从节点,采用增量复制同步数据 最后,主节点根据offset查找对应的进度,将断线期间未同步的写命令,发送给从节点。同时,主节点将所有的写命令写入repl_backlog_buffer, 用于后续判断是采用增量复制还是全量复制。 【注意】从节点 psync 携带的 offset 为 slave_repl_offset。如果 repl_backlog_buffer包含slave_repl_offset 对应的部分,则采用增量复制,否则采用全量复制。repl_backlog_buffer的默认缓冲区大小为1M 【为什么要主从复制】 备份数据:主从复制实现了数据的热备份,是持久化之外的数据冗余方式 故障恢复:当主节点宕机之后,可以采用从节点提供服务。 负载均衡: 主从复制实现了读写分离,只有主节点支持读写操作,从节点只有读操作。在读多写少的场景下,可以提高Redis服务器的并发量。 2. Redis集群的实现原理是什么? 【Redis集群基本知识】 定义: Redis集群由多个实例组成,每个实例存储部分数据 (每个实例之间的数据不重复) 。 【注】集群和主从节点不是一个东西,集群的某一个实例当中可能包含一个主节点 + 多个从节点 为什么用 问题 解决方案 容量不足 数据分片,将数据分散不存到不同的主节点 高并发写入 数据分片,将写入请求分摊到多个主节点 主机宕机问题 自动切换主从节点,避免影响服务, 不需要手动修改客户端配置 节点通信协议:Redis集群采用Gossip协议, 支持分布式信息传播、延迟低、效率高。采用去中心化思想,任意实例(主节点)都可以作为请求入口,节点间相互通信。 分片原理: 采用哈希槽(Hash Slot)机制来分配数据,整个空间可以划分为16384 (16 * 1024)个槽。 每个Redis负责一定范围的哈希槽,数据的key经过哈希函数计算之后对16384取余可定位到对应的节点。 ...

February 20, 2025 · 9 min · SwimmingLiu

Java集合面试题笔记

1. 说说 Java 中 HashMap 的原理? 【HashMap定义】 结构:数组 + 链表 + 红黑树 (JDK 1.8 之后) 默认值:初始容量为16 (数组长度),负载因子为 0.75。当存储的元素为 16 * 0.75 = 12个时,会触发Resize() 扩容操作,容量 x 2 并重新分配位置。但是扩容是有一定开销的,频繁扩容会影响性能。另外,TREEIFY_THRESHOLD 转换为红黑树的默认链表长度阈值为 8, UNTREEIFY_THRESHOLD 从红黑树转换为链表的阈值为 6。 两个阈值采用不同值的原因是防止刚转换为红黑树,又变成链表,反复横跳,消耗资源。 数组下标位置计算方法:首先使用key的hashCode()方法计算下标位置,然后通过 indexFor() (JDK 1.7 以前) 计算下标值。 JDK 1.7后,为了提高计算效率采用 (len - 1) & hash 来确定下标值。 【注】数组的长度len 是2的幂次方时,(len - 1) & hash 等价于 hash % len。 这也是为什么数组长度必须是2的幂次方。 【HashMap线程不安全】 为了保证HashMap的读写效率高,它的操作是非同步的,也就是说读写操作没有锁保护。所以多线程场景下是线程不安全的。 【HashMap不同版本区别】 JDK 1.7: 数组 + 链表,链表部分采用头插法,多线程会导致出现环形链表。扩容会计算每个元素hash值,并分配到新的位置,开销大。 JDK 1.8:数组 + 链表 + 红黑树,采用高低位置来分配位置,即判断(e.hash & oldCap) == 0, 减少了计算hash的次数 【HashMap的PUT方法】 ...

February 20, 2025 · 5 min · SwimmingLiu

Java基础题面试笔记

1. 序列化和反序列化 1.序列化和反序列化:把对象转换为字节流,用于存储和传输;读取字节流数据,重新创建对象。 2.序列化不包括静态对象:序列化和反序列化的本质是调用对象的writeObject和readObject方法,来实现将对象写入输出流和读取输入流。但是,静态变量不属于对象,所以调用这两个方法就没法儿让静态变量参与。 2. 什么是不可变类? 1.不可变类:初始化之后,就不能修改的类。 2.修饰方法:final 和 private 修饰所有类和变量 3.不可修改:不暴露set方法,只能通过重新创建对象替代修改功能(String的replace方法) 4.优缺点: 优点:线程安全,缓存友好 缺点:频繁拼接和修改会浪费资源 3. Exception和Error区别? 1.Exception和Error定义区别:Exception是可处理程序异常,Error是系统级不可回复错误 2.try-catch建议: 1.范围能小则小 2.Exception最好要写清楚具体是哪一个Exception(IOException) 3.null值等能用if判断的,不要用try-catch,因为异常比条件语句低效 4.finally不要直接return和处理返回值 4. Java 中的 hashCode 和 equals 方法之间有什么关系? 1、equals() 和 hashCode() 的关系 如果两个对象euqals() 为 true, 则其 hashCode()一定相同 如果两个对象hashCode() 相同,其equals()结果不一定为true 2、为什么重写equals()之后,一定要重写hashCode() 当重写equals() 之后,通常是重新定义了两个对象相等的逻辑。如果不重写hashCode()方法, 则在散列集合(HashMap 和 HashSet)中,可能无法正确存储和检索,因为两个相同的对象可能有不同的hash值。 例如,下方Person类重写了equals() 方法,但是没有重新hashCode() public class Person { private String name; private int age; @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; Person person = (Person) obj; return age == person.age && Objects.equals(name, person.name); } } 创建相同的对象,并添加到HashSet中 ...

February 19, 2025 · 4 min · SwimmingLiu

MySQL面试题笔记

1.MySQL 中的数据排序是怎么实现的? 1.排序方法:索引排序和文件排序 (filesort) 2.索引排序:如果order by xxx的字段为索引字段,则利用索引进行排序。效率最高,索引默认有序。 3.文件排序 (filesort):内存排序(单路排序和双路排序)和磁盘排序,具体取决于排序数据的大小。其中,内存排序使用单路排序或双路排序,取决于max_length_for_sort_data(默认为4096个字节) 4.双路排序:取row_id(如果有主键,则为主键)和select a,b,c order by xxx的xxx字段放入sort_buffer(排序缓存)中,将排序后的row_id回表查询a,b,c 5.单路排序: 直接把要查的所有字段放入sort_buffer里,排序后直接得到结果集合 6.磁盘排序(归并排序):将数据分为多份文件,单独对文件进行排序,然后合并成一个有序的大文件 2. MySQL 的 Change Buffer 是什么?它有什么作用? 1.ChangeBuffer定义:Change Buffer是InnoDB缓冲当中的一块缓存区,用于暂存二级索引的修改,避免二级索引页修改产生的随机IO 2.ChangeBuffer注意事项:只能用于二级索引,不能用于其他任何索引,包括主键索引和唯一索引都不行。 3.如果ChangeBuffer挂了,更改操作未执行,是否会出现脏数据? 首先,ChangeBuffer也会保存在磁盘空间里面,redo log会记录Change Buffer当中的修改操作,确保数据一致性。 知识拓展1:一级索引和二级索引区别 一级索引(聚簇索引):数据表的主键索引,数据和索引存储在同一B+树的叶子节点中。每个表只能有一个一级索引。 二级索引(非聚簇索引):除主键外的其他索引,叶子节点存储索引列的值和对应的主键值。通过二级索引查询时,需要先通过二级索引获取主键值,再通过主键值查询数据,这个过程称为“回表”。 知识拓展2: MySQL中有哪些常见索引?都有什么区别? 在MySQL中,索引是提高查询效率的关键工具。常见的索引类型包括主键索引、唯一索引、普通索引、全文索引和空间索引。 1. 主键索引(Primary Key Index) 定义:主键索引是一种特殊的唯一索引,用于唯一标识表中的每一行数据。每个表只能有一个主键索引,且主键列的值不能为空。 特点:主键索引的叶子节点存储完整的数据行,因此查询效率高。在InnoDB存储引擎中,主键索引是聚簇索引,数据存储与索引结构合并。 2. 唯一索引(Unique Index) 定义:唯一索引确保索引列的每个值都是唯一的,但允许有空值。与主键索引类似,不同之处在于唯一索引允许列值为NULL。 特点:唯一索引的叶子节点存储索引列的值和对应的主键值。在InnoDB中,唯一索引是非聚簇索引,数据存储与索引结构分开。 3. 普通索引(Index) 定义:普通索引是最基本的索引类型,没有任何限制。索引列的值可以重复,也可以为NULL。 特点:普通索引的叶子节点存储索引列的值和对应的主键值。在InnoDB中,普通索引是非聚簇索引,数据存储与索引结构分开。 4. 全文索引(Fulltext Index) 定义:全文索引用于对文本数据进行全文搜索,适用于MyISAM存储引擎。它允许对文本字段进行复杂的搜索,如查找包含特定单词的记录。 特点:全文索引的叶子节点存储文档的词项信息。在MyISAM中,全文索引是非聚簇索引,数据存储与索引结构分开。 5. 空间索引(Spatial Index) 定义:空间索引用于对地理空间数据进行索引,支持空间数据类型的快速查询。它适用于存储地理位置、地图等空间数据的表。 特点:空间索引的叶子节点存储空间数据的索引信息。在MyISAM中,空间索引是非聚簇索引,数据存储与索引结构分开。 总结: 主键索引:用于唯一标识每一行数据,值不能为空。 唯一索引:确保索引列的值唯一,但允许有空值。 普通索引:最基本的索引类型,允许重复和空值。 全文索引:用于对文本数据进行全文搜索,适用于MyISAM存储引擎。 空间索引:用于对地理空间数据进行索引,支持空间数据类型的快速查询。 3. 详细描述一条 SQL 语句在 MySQL 中的执行过程。 连接器判断用户是否成功建立连接,数据库连接的权限校验 连接器会查询缓存,key 是 SQL 语句,value 是查询结果。如果命中,直接返回查询结果。(MySQL 8.0之后,就移除这个功能了)。 分析器分析SQL语法和词法是否有误 优化器生成SQL的执行计划,确定使用的索引和调整where的执行顺序(包括连表顺序) 执行器判断当前用户是否有权限查询该表,然后执行该SQL语句 ...

February 19, 2025 · 9 min · SwimmingLiu