八股文(一)
1. 为什么不使用本地缓存,而使用Redis?
Redis相比于本地缓存(如JVM中的缓存)有以下几个显著优势:
- 高性能与低延迟:Redis是一个基于内存的数据库,其读写性能非常高,通常可以达到几万甚至几十万的QPS(每秒查询量)。而本地缓存虽然也基于内存,但在处理大量数据时,Redis由于其优化的数据结构和算法,通常能提供更高效的性能。
- 分布式能力:Redis是一个分布式系统,可以在多台服务器上运行,以实现数据的高可用性和负载均衡。相比之下,本地缓存通常只在单个应用程序实例中运行,不具备分布式能力。
- 数据持久化:Redis支持数据持久化,可以将内存中的数据异步地保存到硬盘上,防止数据丢失。而本地缓存一般不具备数据持久化的功能,一旦系统崩溃或重启,缓存中的数据将会丢失。
- 丰富的数据结构:Redis不仅支持简单的键值对存储,还支持多种数据结构,如字符串、列表、哈希表、集合和有序集合等。这使得Redis能够处理更复杂的数据存储和查询需求。
- 社区支持和广泛应用:Redis有成熟的社区支持和广泛的应用案例,许多大型互联网公司和企业都在使用Redis作为缓存解决方案,因此可以获得丰富的经验和技术支持。
2. Redisson是如何设计和实现分布式锁的?
Redisson实现分布式锁主要基于Redis的特性和Redisson客户端的封装。其设计和实现包括以下几个方面:
- 使用Redis命令:Redisson通过Redis的
SET
、EXPIRE
、LUA
脚本等命令来实现锁的获取、释放和续期等功能。 - 可重入锁:Redisson的分布式锁支持可重入特性,即同一个线程可以多次获取同一把锁。这是通过在Redis中存储线程的ID和重入次数来实现的。
- 看门狗(Watchdog)机制:为了避免锁因为客户端异常或网络问题而永久丢失,Redisson引入了看门狗机制。当客户端持有锁时,看门狗会定期续期锁的过期时间,直到客户端显式释放锁为止。
- 锁的超时和续期:Redisson允许设置锁的过期时间,并可以通过看门狗机制自动续期。如果客户端在持有锁期间崩溃或未能释放锁,锁将在过期时间后自动释放,防止死锁的发生。
3. Redisson的锁过期续期问题是怎么解决的?
Redisson通过看门狗(Watchdog)机制来解决锁过期续期的问题。当客户端成功获取锁后,Redisson会启动一个后台线程(看门狗)来监控锁的过期时间。如果锁即将过期且客户端仍然持有锁,看门狗会自动续期锁的过期时间,以防止锁因过期而被释放。默认情况下,看门狗的续期时间是锁过期时间的三分之一,但这个值可以在配置中进行调整。
4. 锁重入的问题是怎么解决的?
Redisson的分布式锁通过以下方式解决锁重入的问题:
- 线程ID记录:当客户端获取锁时,Redisson会在Redis中存储当前线程的ID和重入次数。这样,当同一个线程再次尝试获取锁时,Redisson会检查线程ID和重入次数,如果匹配则允许线程重入并增加重入次数。
- 释放锁时减少重入次数:当客户端释放锁时,Redisson会检查并减少重入次数。如果重入次数减至0,则表示该线程已经完成了所有加锁操作,此时可以安全地释放锁并从Redis中删除相关记录。
5. Redis里所有command的原子性是如何实现的?
Redis中的大多数命令都是原子性的,这意味着这些命令在执行过程中不会被其他命令打断。Redis实现原子性的主要方式包括:
- 单线程模型:Redis采用单线程模型来处理命令。虽然Redis的底层使用了多线程来实现网络通信和数据持久化等功能,但处理客户端命令的核心逻辑是单线程的。这种设计简化了并发控制的问题,并保证了命令执行的原子性。
- Lua脚本:Redis支持使用Lua脚本来执行多个命令。在Lua脚本执行期间,Redis会暂停其他命令的执行,直到脚本执行完毕。这样,Lua脚本中的多个命令就可以作为一个整体来执行,保证了原子性。
6. Redisson的时序时钟问题及解决方案;Redis和Redisson过期时间问题,刚好进行full gc的时候可能导致锁被释放
再深入聊一下,redisson的时序时钟问题,有什么方法去避免,假如说我当前的线程成功持有锁,但是不巧的是它的处理没有执行完,碰到full gc的stw,刚好这个锁的时间过期了,被释放了,其他线程就可以获取这个锁,对这类的时序时钟问题怎么解决?或者有没有其他的分布式锁可以解决这个问题? redis和redisson过期时间问题,刚好进行full gc的时候可能导致锁被释放。
在Redis和Redisson中,虽然full gc(全局垃圾回收)主要影响的是JVM中的对象回收,但如果JVM中的Redis客户端或Redisson客户端因为full gc而暂停执行,确实有可能导致以下问题:
- 锁续期失败:如果Redis客户端或Redisson客户端在持有锁期间进行full gc并暂停执行,那么看门狗线程(如果有的话)也可能被暂停。如果看门狗线程的暂停时间过长,超过了锁的过期时间,那么锁可能会被意外释放
Redisson的时序时钟问题主要源于Java虚拟机(JVM)中的垃圾收集(GC)过程,特别是当发生Stop-The-World(STW)事件时,可能导致Redisson客户端的锁续期操作被延迟或错过,进而使得锁在有效期内被意外释放。针对这个问题,可以考虑以下几种解决方案:
总之,解决Redisson的时序时钟问题需要综合考虑多种因素,并根据实际应用场景和需求来选择最合适的解决方案。
- 优化JVM的GC配置:
- 使用低停顿的GC算法,如G1 GC,以减少GC对应用性能的影响。
- 调整JVM的堆内存设置,以避免频繁的GC操作。
- 实现客户端层面的锁续期机制:
- 在客户端代码中实现一个单独的线程或定时器,用于定期检查锁的状态并在必要时续期。
- 这种方法需要客户端能够准确获取锁的剩余有效期,并与Redis进行交互以续期。
- 使用Redis的发布/订阅功能:
- 利用Redis的发布/订阅机制,当锁即将过期时,由Redis发送通知给所有持有锁的客户端,提醒它们进行续期。
- 但这种方法需要额外的Redis配置和客户端逻辑,且可能增加系统的复杂性和延迟。
- 考虑使用其他分布式锁实现:
- 如果Redisson的时序时钟问题无法解决或影响过大,可以考虑使用其他分布式锁库,如基于ZooKeeper的分布式锁实现。
- ZooKeeper使用其内部的节点和观察者机制来管理锁的状态,对时序时钟的依赖较小。
- 结合使用多种策略:
- 在实际应用中,可能需要结合使用上述多种策略来最大程度地减少时序时钟问题的影响。例如,可以同时使用更长的锁过期时间和客户端层面的锁续期机制。
- 监控和日志记录:
- 在生产环境中,加强对分布式锁使用的监控和日志记录是非常重要的。这可以帮助及时发现和解决潜在的问题,并优化锁的使用策略。
7. ZooKeeper的临时节点
ZooKeeper中的临时节点(Ephemeral Nodes)是一种特殊类型的节点,具有以下特性:
- 生命周期与会话绑定:临时节点的生命周期与创建该节点的客户端会话直接相关。一旦客户端与ZooKeeper集群的会话结束(如客户端断开连接或崩溃),该临时节点将被自动删除。
- 不可创建子节点:临时节点不能拥有子节点。尝试在临时节点下创建子节点将会失败。
- 瞬时性:由于其短暂的生命周期特性,临时节点非常适合于表示短暂状态或会话相关的数据。
ZooKeeper的临时节点常用于分布式锁、服务发现与注册、领导选举等场景。例如,在分布式锁的实现中,客户端可以在ZooKeeper中创建临时节点作为锁的标志,当客户端退出或连接断开时,锁自动释放。
8. 为什么考虑引入线程池?
引入线程池的主要原因包括:
- 降低线程创建和销毁的开销:线程池会提前创建一定数量的线程,当需要处理任务时直接从线程池中获取已经创建好的线程,避免了频繁地创建和销毁线程所带来的开销。
- 控制并发线程数量:线程池可以限制同时执行的线程数量,避免因过多线程导致系统资源耗尽或性能下降的问题。
- 提高响应速度:线程池中的线程可以复用,减少了线程创建的时间,提高了任务的响应速度。
- 提高系统稳定性:线程池可以有效地管理线程的生命周期,避免因线程失控导致系统崩溃的情况。
9. 忽略线程复用减少开销外,使用线程池的其他原因
除了线程复用减少开销外,使用线程池还有以下原因:
- 方便管控线程并发数量:通过线程池可以方便地设置最大线程数,从而控制并发执行的线程数量,避免系统资源被过度消耗。
- 提高系统资源的利用率:线程池中的线程可以被复用,减少了因线程频繁创建和销毁而导致的资源浪费。
- 简化编程模型:线程池提供了一种高级的并发编程模型,使得开发者可以更加专注于业务逻辑的实现,而无需过多关注线程的管理和调度。
- 支持异步执行:线程池可以方便地实现任务的异步执行,提高系统的响应速度和吞吐量。
- 支持定时和周期性任务:某些线程池(如ScheduledThreadPoolExecutor)还支持定时和周期性任务的执行,满足更复杂的业务需求。
11. 线程池中的核心参数和它的调度策略是怎么样的?
线程池中的核心参数及其调度策略主要包括以下几个方面:
核心参数:
-
corePoolSize(核心线程数):线程池中的常驻核心线程数。当线程数小于核心线程数时,新任务到来会创建新的线程去执行;当线程数等于核心线程数时,新任务会被添加到任务队列中等待执行。
-
maximumPoolSize(最大线程数):线程池能够容纳同时执行的最大线程数。当任务队列满且当前线程数小于最大线程数时,会创建新的线程来执行任务。
-
keepAliveTime(线程空闲时间):当线程数大于核心线程数时,空闲线程存活的时间。当空闲时间达到该值时,多余的空闲线程会被销毁。
-
unit(时间单位):keepAliveTime的时间单位,如秒、毫秒等。
-
workQueue(任务队列):用于存放待执行的任务。
-
threadFactory(线程工厂):用于创建新线程的工厂,可以自定义线程的创建过程。
-
handler(拒绝策略):当任务队列已满且线程数达到最大线程数时,对新任务的拒绝处理策略。
调度策略:
-
任务提交:当有新任务提交时,首先判断当前线程数是否小于核心线程数,如果是,则创建新线程执行任务;否则,将任务添加到任务队列中。
-
任务队列处理:如果任务队列未满,则继续将任务添加到队列中;如果队列已满且当前线程数小于最大线程数,则创建新线程执行任务;如果队列已满且线程数已达到最大线程数,则执行拒绝策略。
-
线程空闲时间:当线程数大于核心线程数时,多余的线程在空闲时间达到keepAliveTime后会被销毁,直到线程数回降到核心线程数。
12. core线程数设置成和max线程数一致,设置这个参数的意图是什么?
将core线程数(核心线程数)设置成和max线程数(最大线程数)一致,主要有以下几个意图:
-
资源控制:在一些资源紧张的环境下,将两者设置为一致可以节约系统资源,避免频繁地创建和销毁线程带来的开销。这样可以确保线程池中的线程数量始终保持在一个固定的水平,减少资源的浪费。
-
简化配置:在某些场景下,可能不需要动态调整线程池的大小,将core线程数和max线程数设置为一致可以简化配置过程,使线程池的管理更加直观和方便。
-
并发控制:在一些需要严格控制并发执行线程数量的业务场景下,将两者设置为一致可以确保系统的并发度不会超出预期,防止因线程过多而导致的资源竞争和系统崩溃等问题。
13. 如果核心线程数为0,最大线程数100,这个时候任务来了怎么办?
如果核心线程数为0,最大线程数为100,当任务到来时,线程池会按照以下步骤处理:
-
判断当前线程数:由于核心线程数为0,线程池不会立即创建新线程来执行任务。
-
任务入队:任务会被添加到任务队列中等待执行。如果任务队列有容量限制且已满,则继续下一步。
-
创建新线程:如果任务队列已满,且当前线程数小于最大线程数(100),线程池会创建新的线程来执行任务。
-
执行拒绝策略:如果任务队列已满,且当前线程数已达到最大线程数(100),则执行拒绝策略,根据配置的拒绝策略来处理新任务,如抛出异常、直接丢弃等。
然而,需要注意的是,在核心线程数为0的情况下,如果任务队列为空且没有线程在执行任务,那么当新任务到来时,由于线程池中没有现成的线程可以执行任务,且核心线程数为0,因此线程池会先检查是否有线程在执行任务(即使这些线程是之前因任务完成而空闲下来的)。如果没有线程在执行任务,线程池会创建一个新线程来执行任务,即使这个新线程在任务执行完毕后可能会立即变为空闲状态。这是为了确保线程池不会因为没有核心线程而一直将任务放入队列中等待执行。
14. 锁机制与AQS的公平和非公平策略
在Java中,AbstractQueuedSynchronizer
(AQS)是一个用于构建锁和其他同步类的框架。它提供了一套基础的同步机制,用于管理线程对共享资源的访问。在AQS的基础上,锁机制可以分为公平锁和非公平锁两种类型。
公平锁(Fair Lock):
- 公平锁意味着按照线程请求锁的顺序来分配锁。即先请求锁的线程将先获得锁。
- 在AQS中,公平锁通常是通过维护一个FIFO(先进先出)的队列来实现的。当线程请求锁而锁已被占用时,该线程会被添加到队列的尾部并等待。当锁被释放时,队列中的第一个线程(即最早请求锁的线程)将获得锁。
非公平锁(Non-Fair Lock):
- 非公平锁不保证按照线程请求锁的顺序来分配锁。新到达的线程可能会插队到队列中的等待线程之前,并尝试获取锁。
- 在AQS中,非公平锁的实现允许新到达的线程在锁被释放时直接尝试获取锁,而无需排队。这可能会导致某些已经等待很长时间的线程被新到达的线程“插队”。
在AQS中,没有获取到锁的线程会被封装成Node节点,并加入到AQS维护的CLH(Craig, Landin, and Hagersten)队列中。这个队列是一个虚拟的双向队列,其中只存在节点之间的关联关系,并没有实际的队列实例。当锁被释放时,AQS会唤醒队列中的一个或多个线程来尝试获取锁。
15. 可重入锁的实现
可重入锁是一种允许同一个线程多次获取锁的锁机制。在AQS的基础上,可重入锁的实现通常依赖于线程和锁之间的关联以及状态变量的管理。
- 线程与锁的关联:通常,可重入锁会维护一个线程ID(如
exclusiveOwnerThread
),用于记录当前持有锁的线程。当线程尝试获取锁时,如果该线程已经是锁的持有者,则允许其再次获取锁。 - 状态变量(state):AQS内部维护了一个状态变量(
volatile int state
),用于表示锁的状态。在可重入锁中,这个状态变量通常用于记录锁的重入次数。当线程首次获取锁时,状态变量会被设置为1;当同一个线程再次获取锁时,状态变量会增加。释放锁时,状态变量会相应减少。 - 重入次数的记录:为了支持可重入性,需要在锁的实现中引入额外的属性或数据结构来记录每个线程获取锁的次数。这通常是通过在Node节点或锁对象本身中维护一个计数器来实现的。
16. 记录锁次数的变量的线程安全和全局可见性
在并发编程中,记录锁次数的变量需要保证线程安全和全局可见性,以避免数据不一致和竞态条件。
- 线程安全:通常,记录锁次数的变量会使用
volatile
关键字来修饰,以确保其修改对所有线程立即可见。此外,还可以利用CAS(Compare-And-Swap)机制来安全地更新这个变量的值,避免使用锁带来的性能开销。 - 全局可见性:
volatile
关键字保证了变量的修改对所有线程立即可见,无需进行额外的同步操作。这意味着当一个线程修改了锁次数的变量后,其他线程可以立即看到这个修改后的值。
在AQS中,状态变量(state
)就是这样一个既保证了线程安全又保证了全局可见性的变量。它使用volatile
修饰,并通过CAS机制来安全地更新其值。这样,无论是公平锁还是非公平锁,在AQS的基础上实现的可重入锁都能够正确地记录锁的重入次数,并确保线程安全和全局可见性。
17. volatile是怎么保证可见性的?
volatile
关键字在Java中是一种轻量级的同步机制,它主要有两个作用:
-
保证可见性:当一个变量被声明为
volatile
后,它会告诉JVM,这个变量是不稳定的,每次使用它都需要从主内存中重新读取,而不是使用线程本地的工作内存中的副本。这确保了当一个线程修改了volatile
变量的值后,其他线程能够立即看到这个修改后的值,从而保证了变量的可见性。 -
禁止指令重排序:在多线程环境下,编译器和处理器可能会对指令进行重排序以优化性能。然而,这种重排序可能会导致线程安全问题。
volatile
关键字可以禁止这种重排序,确保指令的执行顺序与代码中的顺序一致。
具体来说,volatile
变量在写入时,会先写入主内存,然后再写入线程的工作内存;在读取时,会先从主内存中读取最新的值,然后放入线程的工作内存中。这样,就保证了不同线程之间对 volatile
变量的可见性。
18. 在Redis里是K-V存储,当存储达到一定容量,会进行内存淘汰。现在想象一个简易的K-V存储,你如何设计一个内存淘汰策略?
设计一个简易的K-V存储的内存淘汰策略,可以考虑以下几种常见的策略:
-
最近最少使用(LRU, Least Recently Used):淘汰最长时间未被访问的数据。这种策略假设最近被访问的数据在未来更有可能被再次访问。
-
最不常用(LFU, Least Frequently Used):淘汰访问次数最少的数据。这种策略考虑了数据的访问频率,但实现起来相对复杂,需要额外的数据结构来记录每个数据的访问次数。
-
随机淘汰:随机选择并淘汰一些数据。这种策略实现简单,但可能不是最高效的,因为它不考虑数据的访问模式。
-
基于容量的淘汰:当存储的数据量超过预设的容量阈值时,淘汰所有数据并重新开始。这种策略适用于某些特定场景,如缓存系统需要定期刷新的情况。
-
混合策略:结合以上多种策略,根据实际需求选择或调整淘汰策略。例如,可以先尝试使用LRU策略,当LRU策略无法满足需求时,再引入LFU策略或随机淘汰策略作为补充。
在实现时,可以根据存储系统的具体需求和资源情况来选择合适的淘汰策略。对于简易的K-V存储,LRU策略通常是一个不错的选择,因为它实现相对简单且效果良好。
19. 最近最少访问元素,你会用什么数据结构去做?
为了高效地实现最近最少访问(LRU, Least Recently Used)元素的管理,通常会使用哈希表(HashMap)和双向链表(Doubly Linked List)相结合的数据结构。哈希表用于提供快速的访问能力,双向链表则用于按照访问顺序排列元素。
具体实现时,可以将哈希表的键(Key)设置为元素的唯一标识,值(Value)设置为双向链表中的节点。当访问某个元素时,首先通过哈希表快速定位到对应的节点,然后将其从链表中移除并重新插入到链表的头部(表示最近访问)。这样,链表的尾部就始终保存着最近最少访问的元素。当需要淘汰元素时,只需从链表的尾部移除节点即可。
20. 如何用LinkedHashMap去实现LRU算法?
LinkedHashMap
是Java中的一个扩展了 HashMap
的类,它维护了一个运行于所有条目的双重链接列表。这个列表定义了迭代器的顺序,可以是插入顺序或者是访问顺序。通过设置 accessOrder
属性为 true
,LinkedHashMap
可以按访问顺序来排序其元素,这使得它非常适合实现LRU缓存。
使用 LinkedHashMap
实现LRU算法的基本步骤如下:
-
创建LinkedHashMap实例:设置初始容量、加载因子和
accessOrder
为true
,以确保按照访问顺序排序。 -
重写removeEldestEntry方法:
LinkedHashMap
提供了一个受保护的方法removeEldestEntry(Map.Entry<K,V> eldest)
,当元素被添加到映射中时,会调用此方法。如果此方法返回true
,则最老的元素将被移除。因此,可以通过重写此方法来实现基于容量的LRU缓存淘汰策略。 -
添加元素:向
LinkedHashMap
中添加元素时,如果元素数量超过了设定的容量阈值,并且removeEldestEntry
方法返回true
,则最老的元素(即最近最少访问的元素)将被自动移除。 -
访问元素:通过
get
或put
方法访问元素时,LinkedHashMap
会自动将该元素移动到链表的头部,表示最近访问。
这样,LinkedHashMap
就能够按照LRU算法的要求来管理缓存中的数据。
22. MySQL中索引是用什么样的数据结构支持的?
MySQL中最常用的索引结构是B+树(B-Tree Plus)。B+树是一种自平衡的树数据结构,它维护着数据排序,允许搜索、顺序访问、插入和删除操作都在对数时间内完成。B+树与B树的主要区别在于:
- B+树的所有值(或数据的指针)都存在于叶子节点,叶子节点之间通过指针相连,形成有序链表,便于范围查询。
- B+树的非叶子节点仅存储键值信息,不存储数据记录指针,这使得B+树在相同数据量的情况下,能够拥有更多的分支(即更大的扇出),从而减少树的高度,提高查询效率。
- B+树更适合用于数据库和操作系统的文件系统,因为它提供了更好的范围查询性能和磁盘读写性能。
23. 为什么使用B+树不用其他的数据结构?
数据库选择B+树作为索引结构的主要原因包括:
- 高效的范围查询:由于B+树的所有叶子节点通过指针相连,可以很容易地进行范围查询。
- 磁盘读写优化:B+树的高度较低,减少了磁盘I/O操作的次数。数据库系统经常需要处理大量数据,而磁盘I/O操作是数据库性能的瓶颈之一。
- 高效的插入和删除操作:B+树通过分裂和合并节点来保持树的平衡,这使得插入和删除操作也能在对数时间内完成。
- 缓存友好:由于B+树的高度较低,并且非叶子节点不包含数据记录,这使得B+树更加缓存友好,因为缓存中可以存储更多的索引节点。
24. 索引命中情况分析
对于联合索引index_a_b_c
:
-
SELECT * WHERE a=? AND c=?
:这个查询不会完全利用到index_a_b_c
索引,因为索引是按照a, b, c
的顺序构建的。数据库可以使用索引来快速定位到a
的值,但之后需要扫描所有具有相同a
值的记录来找到满足c
条件的记录。这种扫描称为索引范围扫描或索引过滤。 -
SELECT * WHERE a=? AND c=? AND b=?
:这个查询理论上可以利用到索引,但是否完全利用取决于查询优化器的决策和索引的使用方式。理想情况下,数据库会先通过索引定位到满足a
和b
条件的记录,然后检查这些记录中是否有满足c
条件的。然而,由于索引是按照a, b, c
的顺序构建的,数据库可能仍然需要进行一些额外的检查或扫描来确保满足所有条件。
25. 覆盖索引和联合索引的区别
-
覆盖索引(Covering Index):覆盖索引是指查询中的列完全包含在索引中,因此在查询时不需要访问表中的数据行。这可以显著提高查询性能,因为避免了访问表中的数据页。覆盖索引可以是单列索引,也可以是联合索引的一部分。
-
联合索引(Compound Index):联合索引(也称为复合索引或组合索引)是索引中包含多个列的索引。这些列按照索引中指定的顺序进行排序。联合索引可以加速包含索引中列的查询,特别是当查询条件包含索引的前缀列时。然而,如果查询条件跳过了索引中的某些列(如上面的
a=? AND c=?
示例),则可能无法完全利用索引的优势。
简而言之,覆盖索引关注的是查询列是否完全包含在索引中,而联合索引关注的是索引中包含了哪些列以及这些列的顺序。两者都是优化数据库查询性能的重要工具。
26. 什么是回表?
回表是数据库查询优化中的一个概念,特指在使用非聚集索引(Secondary Index 或 Non-Clustered Index)进行查询时的一种操作。具体来说,当查询通过非聚集索引找到数据行的位置(通常是主键或行标识符)后,数据库引擎需要再根据这些位置信息从聚集索引或数据文件中读取完整的数据行的过程被称为回表。这是因为非聚集索引中不包含所有的数据列,仅包含索引键值和数据行的位置标识符(如主键值或行指针)。如果查询需要的列不在非聚集索引中,数据库就必须回到表中读取完整的数据行。
例如,在MySQL中,如果有一个表employees
,包含字段employee_id
(主键)、name
和department
,并在name
字段上创建了非聚集索引。当执行查询SELECT department FROM employees WHERE name = 'Alice';
时,MySQL会首先使用name
字段上的非聚集索引找到name
为'Alice'的记录的位置(即employee_id
),然后再根据这个employee_id
回到employees
表中读取完整的数据行,以获取department
列的值。
27. 事务隔离级别如何解决脏读、幻读和不可重复读问题?可重复读的情况下是如何解决的?
事务隔离级别是数据库用来定义事务之间相互隔离的程度的一种机制。MySQL中主要有四种事务隔离级别:读未提交(Read Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)和串行化(Serializable)。
脏读:一个事务读取了另一个事务未提交的数据。在读已提交及以上级别可以避免脏读。
不可重复读:一个事务内多次读取同一数据集合时,由于其他事务的修改,导致前后读取的数据不一致。在可重复读及以上级别可以避免不可重复读。MySQL的InnoDB存储引擎在可重复读级别下,通过多版本并发控制(MVCC)和Next-Key Locks等技术,确保事务内多次读取的数据是一致的。
幻读:一个事务内按照相同的查询条件多次执行查询时,原本不存在的行(phantom rows)在事务后续的查询中突然出现。在可重复读级别下,MySQL的InnoDB存储引擎使用Next-Key Locks来防止幻读。Next-Key Locks是行锁和间隙锁的组合,它不仅锁定索引记录本身,还锁定索引记录之间的间隙,从而防止其他事务在这些间隙中插入新行。但是,需要注意的是,可重复读级别本身并不足以防止所有类型的幻读,特别是针对范围查询之外的插入操作。要达到完全防止幻读的效果,需要使用串行化隔离级别。
28. MVCC机制
MVCC(Multi-Version Concurrency Control,多版本并发控制)是一种并发控制的方法,用于在数据库管理系统中实现高并发。它允许多个事务同时读取同一数据项的不同版本,从而避免了读取操作之间的冲突。
在MVCC中,每个事务都有一个唯一的标识符(如事务ID),并且每个数据项都会记录其创建和修改的事务ID。当事务读取一个数据项时,它会根据当前事务的ID和数据的版本信息来判断自己是否有权访问这个版本的数据。
具体来说,MVCC通过维护数据的多个版本来实现并发控制。当一个事务对数据进行修改时,它实际上是在当前数据的基础上创建一个新版本,并将这个版本与事务ID关联起来。同时,系统会保留旧版本的数据,以便其他事务在需要时可以读取。
在可重复读隔离级别下,MVCC通过构建事务的快照视图来确保事务内多次读取的数据是一致的。当事务开始时,它会根据当前的数据版本和事务ID构建一个快照视图。在事务执行期间,所有基于这个快照的读取操作都会看到事务开始时已提交的数据版本,而不会受到其他事务的影响。
此外,MVCC还依赖于数据库的隐藏字段(如事务ID、回滚指针等)、undo日志等机制来实现数据的版本控制和恢复。通过这些机制,MVCC能够在不锁定数据的情况下实现高并发的读写操作,从而提高了数据库的性能和吞吐量。
29.算法:无序数组中寻找第K大元素(堆排序)
import java.util.PriorityQueue; public class KthLargestElement { public int findKthLargest(int[] nums, int k) { // 创建一个最大堆,大小为k PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a, b) -> b - a); for (int num : nums) { // 如果堆未满,直接添加 if (maxHeap.size() < k) { maxHeap.offer(num); } else if (num > maxHeap.peek()) { // 如果当前元素大于堆顶元素,移除堆顶元素并添加当前元素 maxHeap.poll(); maxHeap.offer(num); } // 否则,忽略当前元素 } // 堆顶元素即为第k大元素 return maxHeap.peek(); } public static void main(String[] args) { KthLargestElement kthLargest = new KthLargestElement(); int[] nums = {3, 2, 1, 5, 6, 4}; int k = 2; int result = kthLargest.findKthLargest(nums, k); System.out.println("The " + k + "th largest element is: " + result); }
}
相关文章:
八股文(一)
1. 为什么不使用本地缓存,而使用Redis? Redis相比于本地缓存(如JVM中的缓存)有以下几个显著优势: 高性能与低延迟:Redis是一个基于内存的数据库,其读写性能非常高,通常可以达到几万…...
灵茶八题 - 子数组 ^w^
灵茶八题 - 子数组 w 题目描述 给你一个长为 n n n 的数组 a a a,输出它的所有连续子数组的异或和的异或和。 例如 a [ 1 , 3 ] a[1,3] a[1,3] 有三个连续子数组 [ 1 ] , [ 3 ] , [ 1 , 3 ] [1],[3],[1,3] [1],[3],[1,3],异或和分别为 1 , 3 , …...
git clone private repo
Create personal access token Clone repo $ git clone https://<user_name>:<personal_access_tokens>github.com/<user_name>/<repo_name>.git...
vue3+ts+pinia+vant-项目搭建
1.pnpm介绍 npm和pnpm都是JavaScript的包管理工具,用于自动化安装、配置、更新和卸载npm包依赖。 pnpm节省了大量的磁盘空间并提高了安装速度:使用一个内容寻址的文件存储方式,如果多个项目使用相同的包版本,pnpm会存储单个副本…...
自动化测试概念篇
目录 一、自动化 1.1 自动化概念 1.2 自动化分类 1.3 自动化测试金字塔 二、web自动化测试 2.1 驱动 2.2 安装驱动管理 三、selenium 3.1 ⼀个简单的web自动化示例 3.2 selenium驱动浏览器的工作原理 一、自动化 1.1 自动化概念 在生活中: 自动洒水机&am…...
Mojo值的生命周期(Life of a value)详解
到目前为止,我们已经解释了 Mojo 如何允许您使用 Mojo 的所有权模型构建内存安全的高性能代码而无需手动管理内存。但是,Mojo 是为 系统编程而设计的,这通常需要对自定义数据类型进行手动内存管理。因此,Mojo 允许您根据需要执行此操作。需要明确的是,Mojo 没有引用计数器…...
java对接kimi详细说明,附完整项目
需求: 使用java封装kimi接口为http接口,并把调用kimi时的传参和返回数据,保存到mysql数据库中 自己记录一下,以做备忘。 具体步骤如下: 1.申请apiKey 访问:Moonshot AI - 开放平台使用手机号手机号验证…...
鸿蒙媒体开发【基于AVCodec能力的视频编解码】音频和视频
基于AVCodec能力的视频编解码 介绍 本实例基于AVCodec能力,提供基于视频编解码的视频播放和录制的功能。 视频播放的主要流程是将视频文件通过解封装->解码->送显/播放。视频录制的主要流程是相机采集->编码->封装成mp4文件。 播放支持的原子能力规…...
django集成pytest进行自动化单元测试实战
文章目录 一、引入pytest相关的包二、配置pytest1、将django的配置区分测试环境、开发环境和生产环境2、配置pytest 三、编写测试用例1、业务测试2、接口测试 四、进行测试 在Django项目中集成Pytest进行单元测试可以提高测试的灵活性和效率,相比于Django自带的测试…...
48天笔试训练错题——day40
目录 选择题 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 编程题 1. 发邮件 2. 最长上升子序列 选择题 1. DNS 劫持又称域名劫持,是指在劫持的网络范围内拦截域名解析的请求,分析请求的域名,把审查范围以外的请求放行,否则返回…...
LabVIEW在DCS中的优势
DCS(Distributed Control System,分布式控制系统)是一种用于工业过程控制的自动化系统。它将控制任务分散到多个控制单元中,通过网络连接和协调这些单元来实现对整个过程的监控和控制。DCS通常用于大型工业设施,如化工…...
英特尔:从硅谷创业到全球科技巨头
在科技行业,英特尔不仅是一个品牌,更是一种精神的象征。自1968年成立以来,英特尔经历了从初创企业到全球半导体产业领导者的华丽转变,其发展历程是科技创新与市场战略完美结合的典范。本文将深入探讨英特尔的发展历程,…...
生物计算与纳米技术:交汇前沿的科学领域
在当今科技迅猛发展的时代,生物计算和纳米技术作为前沿科技领域的两个重要方向,正在逐渐融合并带来深远的影响。生物计算涉及使用生物系统进行计算和数据存储,而纳米技术则关注制造极小尺度的电子器件和材料科学。本文将深入探讨这两个领域的…...
C#中栈和队列
在C#中,Stack和Queue是两种不同的集合类型,它们用于实现后进先出(LIFO)和先进先出(FIFO)的数据结构。 Stack(堆栈) Stack是一个后进先出的集合,这意味着最后一个添加到堆…...
技战法丨攻防演练防御——纵深、联动、诱捕(可搬运、可cv)
演习活动经过近几年的发展,攻击方的专业水平已大幅提高,逐渐呈现出隐秘化、APT化的趋势。其利用渗透技术对目标系统做深入探测,不断挖掘防守方网络系统的薄弱环节,这就要求防守方构建立体式纵深防护体系来抵御入侵。同时ÿ…...
1、 window平台opencv下载编译, 基于cmake和QT工具链
1. 环境准备,源码下载 1.1 前置环境 qt 下载安装cmake 安装,可参考: https://blog.csdn.net/qq_51355375/article/details/139186681 1.2 opencv 源码下载 官网地址: https://opencv.org/releases/ 下载源码: 2 …...
C++20三向比较运算符详解
三向比较运算符可以用于确定两个值的大小顺序,也被称为太空飞船操作符。使用单个表达式,它可以告诉一个值是否等于,小于或大于另一个值。 它返回的是类枚举(enumeration-like)类型,定义在 <compare> …...
监听机制与耗电量
一、监听机制与耗电量的关系 监听机制通常涉及对特定事件、状态或数据的持续监测。在移动设备和嵌入式系统中,这种监听可能由多种组件和传感器实现,如GPS、传感器(如加速度计、陀螺仪)、网络连接等。监听的频率越高,意…...
C++ //练习 16.29 修改你的Blob类,用你自己的shared_ptr代替标准库中的版本。
C Primer(第5版) 练习 16.29 练习 16.29 修改你的Blob类,用你自己的shared_ptr代替标准库中的版本。 环境:Linux Ubuntu(云服务器) 工具:vim 代码块 template <typename> class BlobP…...
【Mode Management】CanNm处于PBS状态下接收到一帧诊断报文DCM会响应吗
目录 前言 正文 1.CanNm从RSS状态切换到PBS状态行为分析 1.1.CanNm动作 1.2.ComM动作 1.3.DCM动作 1.4 小结 2.CanNM在PBS状态下收到一帧诊断报文行为分析 2.1.DCM动作1 2.2. ComM动作 2.3. DCM动作2 2.3. CanNm动作 2.4 问题 2.5 分析 3.总结 前言 我们知道EC…...
【C++】模版:范式编程、函数模板、类模板
目录 一.范式编程 二.函数模板 1.概念与格式 2.原理 3.实例化 4.匹配规则 三.类模板 一.范式编程 在写C函数重载的时候,可能会写很多同一类的函数,例如交换函数: void Swap(int& left, int& right) {int temp left;left r…...
验证图片旋转
最近在使用百度图片翻译时遇到一个问题,就是图片会翻转90,经与百度沟通,发现是原始图片中有个旋转参数引起的。 于是写个demo验证一下。 // 获取元数据中的旋转方向 func getOrientation() int {//打开图像文件f, err : os.Open("image…...
宏景eHR /ajax/ajaxService SQL注入漏洞复现
0x01 产品简介 宏景eHR人力资源管理软件是一款人力资源管理与数字化应用相融合,满足动态化、协同化、流程化、战略化需求的软件。 0x02 漏洞概述 宏景eHR /ajax/ajaxService 接口处存在SQL注入漏洞,,未经身份验证的远程攻击者通过利用SQL注入漏洞配合数据库xp_cmdshell可…...
从源码看 Redis:深入理解 redisDb 和 redisObject
Redis 是一个广泛使用的内存数据库,以其高性能和丰富的数据结构而闻名。不同于磁盘数据库,磁盘数据库将数据读取到文件中维护,而内存数据库将数据存储在内存中,意味着其想要维护数据,必须在代码中维护一个保存数据的结…...
unity中实现流光效果——世界空间下
Properties{_MainTex ("Texture", 2D) "white" {}_FlowColor ("Flow Color", Color) (1, 1, 1, 1) // 流光颜色_FlowFrequency ("Flow Frequency", Float) 1.0 // 流光频率_FlowSpeed ("Flow Speed", Float) 1.0 // 流光…...
项目经验分享:用4G路由器CPE接海康NVR采用国标GB28181协议TCP被动取流一段时间后设备就掉线了
最近我们在做一个生态化养殖的项目时,发现一个奇怪的现象: 项目现场由于没有有线网络,所以,我们在现场IPC接入到海康NVR之后,再通过一款4G的CPE接入到天翼云的国标GB28181视频平台;我们采用UDP协议播放NVR…...
【RabbitMQ】RabbitMQ不公平分发_预取值
一、不公平分发 1、简介 RabbitMQ中的不公平分发(Unfair Dispatch)是指当多个消费者(Consumers)同时订阅同一个队列(Queue)时,消息的分发机制并非严格平均或公平,而是基于某些条件…...
最新AI模型使用指南和模型
市面上最好的AI大模型 OpenAI GPT-4: 概述:GPT-4 是 OpenAI 发布的最新一代大型语言模型,具备更强的理解和生成自然语言的能力。特点: 强大的文本生成和理解能力。支持多语言处理。可用于各种应用场景,如对话生成、内容…...
数据结构之八大基本排序方法
在数据结构中,排序是一个重要的操作,它有助于提高数据的可读性和可操作性。排序算法有多种,各有优缺点,适用于不同的场景。以下是八大经典排序算法的介绍: 1. 冒泡排序(Bubble Sort) 原理&…...
《Milvus Cloud向量数据库指南》——什么是高可用:深入理解数据库系统中的高可用性架构
什么是高可用:深入理解数据库系统中的高可用性架构 在信息技术日新月异的今天,高可用性(High Availability,简称HA)已成为衡量一个系统,尤其是数据库系统稳定性和可靠性的重要标准。高可用性的核心目标在于确保系统能够持续不断地提供服务,最大限度地减少因维护活动、硬…...
店铺营业执照在哪个网站做年审/seo优化流程
e.clientX,e.clientY, e.pageX , e.pageY , e.offsetX , e.offsetY , e.screenX , e.screenY欢迎访问我的博客,祝码农同胞们早日走上人生巅峰,迎娶白富美~~~ 声明:参考文档你是如何理解var ee||window.event的 刚才写博客《javascript实现简单…...
wordpress房地产/seo实战优化
前言 上篇博客 主要说到了Spring IOC对bean类xml配置文件、使用反射等将其加载、解析并注册至Spring 容器中,以达到后期的使用操作。 但是,这个对象需要使用,依旧需要实例化操作,针对实例化,Spring就给整个项目进行了…...
做前端常用的网站及软件下载/常见的营销型网站
据《华盛顿邮报》网站1月29日报道,美国国务院得出结论称,前国务卿希拉里克林顿使用私人服务器收发的电子邮件中22封包含“最高机密”。由于邮件内容过于敏感,将不会对外公布。 美国国务院发言人约翰柯比称,经确认,“最…...
网站开发工单/微信引流推广精准粉
一键编译安装mysql数据库 说明:请把所需要的软件包放在文件夹中,把文件夹重命名为packages,然后压缩成packages.zip的包,上传到linux系统上任何目录都可以,然后在上传packages.zip包的当前目录(例如&#x…...
零售网站建设/长沙市云网站建设
CocoaPods简介 当你开发iOS应用时,会经常使用到很多第三方开源类库,比如AFNetWorking等等。手动去下载所需类库十分麻烦。另外一种常见情况是,你项目中用到的类库有更新,你必须得重新下载新版本,重新加入到项目中&…...
网站片头怎么做/宁波网站推广优化公司怎么样
1. 连接时间的日志 连接时间日志一般由/var/log/wtmp和/var/run/utmp这两个文件记录,不过这 两个文件无法直接cat查看,并且该文件由系统自动更新,可以通过如下: w/who/finger/id/last/lastlog/ac 进行查看 [rootxhot ~]# who roo…...