数据库内核实习八股(?)整理

汇聚了所有能搜刮到的题目。。。还缺少把15445ppt全部看一遍👋

Raft

Raft 的优化,介绍 PreVote,ReadIndex,LeashRead

  1. PreVote(预投票): 在 Raft 中,领导者选举是通过投票机制实现的。在传统的 Raft 中,节点在进行选举时会直接发起选举请求,然后等待其他节点的投票。然而,当网络分区或者网络故障发生时,节点可能会发起不必要的选举,这可能会导致性能下降或者分布式系统的不稳定。为了解决这个问题,PreVote 被引入。

    PreVote 是一种机制,它允许节点在正式选举之前先进行一轮“预投票”。在 PreVote 阶段,节点会向其他节点发送预投票请求,其他节点会根据自己的状态来决定是否给予预投票。只有在节点获得了大多数的预投票之后,才会发起正式的选举请求。这样做的好处是,可以避免不必要的选举,提高了系统的稳定性和性能。

  2. ReadIndex: 在 Raft 中,读操作(例如查询数据)可以由任何节点处理,而不仅仅是领导者。然而,在传统的 Raft 中,如果客户端发送一个读请求到一个跟随者节点,而该节点没有最新的数据,它必须先向领导者请求最新的数据,然后再响应客户端。这样的设计可能会导致性能瓶颈和延迟增加。

    ReadIndex 优化旨在解决这个问题。它允许跟随者节点在没有领导者的情况下响应读请求。当一个跟随者节点收到一个读请求时,它会向其他节点询问是否有更新的数据,如果大多数节点都有相同的数据,则跟随者节点可以直接响应客户端,而不需要等待领导者的响应。这样可以减少读操作的延迟,并提高系统的性能。

  3. LeashRead: LeashRead 是一种与 ReadIndex 类似的优化技术,它进一步改进了读操作的性能。在 LeashRead 中,每个跟随者节点都会记录下它上次从领导者节点收到的请求的索引(或者序列号)。当一个跟随者节点收到一个读请求时,它首先检查自己记录的领导者的索引,如果领导者的索引与请求的索引相同或者更大,则跟随者节点可以直接响应客户端,而不需要向领导者节点发起请求。这样可以进一步减少读操作的延迟,并提高系统的性能。

Raft 的选举机制

Raft 的选举机制是确保分布式系统中的节点能够达成共识并选举出一个领导者来负责处理客户端请求的关键部分。以下是 Raft 的选举机制的概述:

  1. 领导者失效检测: 在 Raft 中,每个节点都有一个超时定时器,称为选举超时(Election Timeout)。当一个节点在一段时间内没有收到来自领导者节点的消息时,它会触发选举超时。选举超时的时长通常设置为一个随机值,以避免节点同时触发选举超时。

  2. 候选者状态: 一旦节点的选举超时触发,它将转变为候选者状态,并开始一个新的选举过程。节点会向其他节点发送投票请求,并且尝试获得大多数节点的投票支持。

  3. 投票过程: 在选举过程中,候选者会向其他节点发送请求投票的消息(RequestVote RPC)。其他节点在收到该消息后会进行投票决策。节点只会投票给一个候选者,通常情况下是第一个发送投票请求的候选者,但是如果收到的候选者的任期号(term)比自己的任期号要小,或者已经投过票给了其他候选者,那么节点会拒绝投票。

  4. 选举条件: 如果候选者收到了来自大多数节点的投票(即获得了超过半数的选票),那么它就会成为新的领导者。在 Raft 中,要求获得大多数节点的选票是为了确保新的领导者能够获得足够的支持,并且可以处理客户端的请求。

  5. 任期号(Term): 在 Raft 中,每个任期都有一个唯一的任期号,用于标识领导者的任期。当一个新的领导者产生时,它会递增任期号,并且任期号会随着时间的推移而递增。

通过这样的选举机制,Raft 协议确保了在分布式系统中始终有一个领导者负责处理客户端的请求,并且可以在节点故障或者网络分区的情况下快速地选举出新的领导者,以保证系统的正常运行。

Raft 哪些 state 需要持久化,为什么

Raft 协议中需要持久化的状态主要包括以下几个部分:

  1. 当前任期号(Current Term): 每个节点在任一时刻都处于某个任期。这个任期号是一个递增的整数,用于标识当前领导者的任期。节点需要将当前任期号持久化,以便在重新启动后能够恢复到正确的状态,并且在进行选举时能够正确地比较任期号。

  2. 已投票给谁(Voted For): 每个节点在一个任期内只能投票给一个候选者。节点需要将它在当前任期内已经投票给了哪个候选者的信息持久化。这样做是为了避免在节点重新启动后重新投票,以及确保在同一个任期内只有一个候选者能够获得大多数选票。

  3. 日志条目(Log Entries): Raft 协议通过日志来记录系统状态的变化。每个节点都会维护一个日志,其中包含了已经被接受的指令或者状态变化。节点需要将日志条目持久化,以确保在节点重启后能够恢复到正确的状态,并且能够与其他节点进行一致的状态复制。

持久化这些状态的原因包括:

  • 保证一致性:通过持久化这些状态,节点能够在重新启动后保持一致的状态,避免数据丢失或者不一致。
  • 保证故障恢复:在节点发生故障或者崩溃时,持久化的状态可以帮助节点恢复到正确的状态,从而快速恢复服务。
  • 确保正确的选举过程:持久化当前任期号和已投票给谁的信息可以确保在选举过程中节点能够正确地比较任期号,并且只投票给一个候选者。
  • 确保正确的日志复制:持久化日志条目可以确保节点在重新启动后能够恢复到正确的状态,并且能够与其他节点进行一致的状态复制。

通过持久化这些关键状态,Raft 协议能够保证分布式系统的一致性和可靠性。

Raft applyIndex 和 commitIndex 的关系以及区别,是否需要持久化,持久化有什么好处

在 Raft 中,applyIndexcommitIndex 是两个关键的索引,用于管理日志的提交和应用。它们之间的关系和区别如下:

  1. applyIndexapplyIndex 是一个节点在自己的日志中已经被应用到状态机的最高索引。换句话说,applyIndex 指示了节点在其状态机中已经执行了的最后一条日志的索引。节点会将从 commitIndexapplyIndex 之间的所有日志条目应用到其状态机中。因此,applyIndex 是节点状态机中已经应用的最新的日志条目的索引。

  2. commitIndexcommitIndex 是一个节点在自己的日志中已经提交但尚未应用到状态机的最高索引。换句话说,commitIndex 指示了节点在其日志中已经被提交但尚未应用的最后一条日志的索引。节点会将从日志的起始位置到 commitIndex 之间的所有日志条目提交,但只有从 commitIndexapplyIndex 之间的日志条目才会被应用到状态机中。

关于持久化的问题,commitIndex 需要被持久化,而 applyIndex 则不需要。持久化 commitIndex 的好处包括:

  • 持久化一致性:通过持久化 commitIndex,节点可以在重启后快速恢复到上次提交的状态,确保了系统的一致性。
  • 故障恢复:在节点发生故障或者崩溃时,持久化的 commitIndex 可以帮助节点在重新启动后恢复到正确的状态,从上次提交的位置继续进行操作。
  • 避免数据丢失:持久化 commitIndex 可以防止由于节点故障或者崩溃而导致已提交但尚未应用的日志条目丢失,确保了数据的完整性。

相比之下,applyIndex 不需要持久化,因为它只是一个节点在其状态机中已经应用的日志条目的索引,而状态机的状态通常是根据应用的日志条目动态计算的,并且可以在节点重启后重新构建。因此,持久化 applyIndex 并不会提供额外的好处,而且会增加存储和写入的开销。

Raft成员变更

数据库

ACID

  1. 原子性(Atomicity)

    • 含义:事务是一个不可分割的工作单位,要么全部执行成功,要么全部失败。如果事务执行过程中发生了错误,那么事务应该被回滚到最初的状态,没有产生任何影响。
    • 示例:例如,转账操作中,从一个账户扣除金额并将其添加到另一个账户。如果其中一个账户的扣除操作成功而另一个账户的添加操作失败,则需要回滚整个事务,以确保资金的一致性。
  2. 一致性(Consistency)

    • 含义:事务开始和结束时,数据库的完整性约束应该得到保持。这意味着事务在执行期间对数据库所做的任何更改必须是合法的,不会违反数据库的完整性规则。
    • 示例:例如,在银行账户中,账户的总余额应该保持不变,即使发生了转账操作或其他交易。
    • At a high level, consistency means the “world” represented by the database is logically correct. All questions (i.e., queries) that the application asks about the data will return logically correct results.
    • There are two notions of consistency: Database Consistency: The database accurately represents the real world entity it is modeling and follows integrity constraints. (E.g. The age of a person cannot not be negative). Additionally, transactions in the future should see the effects of transactions committed in the past inside of the database.
    • Transaction Consistency: If the database is consistent before the transaction starts, it will also be consistent after. Ensuring transaction consistency is the application’s responsibility
  3. 隔离性(Isolation)

    • 含义:当多个事务同时执行时,它们应该被隔离开来,互不干扰。这意味着一个事务的修改对其他事务应该是不可见的,直到事务提交。
    • 示例:例如,两个并发事务同时查询同一个银行账户的余额,应该保证每个事务看到的余额都是一致的,而不受其他事务的影响。
  4. 持久性(Durability)

    • 含义:一旦事务提交,其所做的修改应该永久保存在数据库中,即使系统发生故障或重启,也不应该丢失事务的更改。
    • 示例:例如,当用户购买商品时,系统需要确保订单信息被永久保存在数据库中,即使系统在订单提交后发生了故障或崩溃。

Join 两种实现方法 Hash Join,Sort Merge Join 的优缺点,什么时候应该用哪个

Hash Join:

优点:

  1. 适用于连接大数据集:Hash Join在处理大型数据集时性能较好,因为它可以利用哈希表的快速查找特性。
  2. 内存效率高:相对于Sort Merge Join,Hash Join对内存的需求较低,因为它只需要构建一个哈希表来存储其中一个表的数据。
  3. 性能稳定:在适当的情况下,Hash Join通常具有稳定的性能,而且不会受到输入数据的分布影响。

缺点:

  1. 不适用于内存不足的情况:如果其中一个表的大小超出了内存容量,就需要考虑分块或其他方法,否则Hash Join的性能会大幅下降。
  2. 不适用于不等连接条件:Hash Join适用于等值连接条件,如果连接条件不是等值条件,就不能使用Hash Join。

Sort Merge Join:

优点:

  1. 适用于大型表的连接:Sort Merge Join适用于连接大型表,特别是当内存不足以容纳整个表时。
  2. 适用于不等连接条件:Sort Merge Join可以处理不等值连接条件,因为它首先对连接字段进行排序,然后进行归并操作。
  3. 不受内存限制影响:由于Sort Merge Join是基于已排序的输入数据进行连接的,因此不受内存限制的影响。

缺点:

  1. 性能受排序过程影响:Sort Merge Join在执行之前需要对连接字段进行排序,如果数据量较大,排序过程可能会成为性能瓶颈。
  2. 内存占用高:相对于Hash Join,Sort Merge Join可能需要更多的内存,特别是在连接大型表时。

何时应该使用哪个?

  1. 如果连接的数据集都可以完全加载到内存中,并且连接条件是等值条件,通常情况下优先选择Hash Join。
  2. 如果其中一个表的大小超出了内存容量,但连接字段有索引,也可以考虑使用Sort Merge Join。
  3. 如果连接条件不是等值条件,或者需要连接大型表,且内存有限,可以选择Sort Merge Join。
  4. 在实际情况中,通常需要通过测试和性能分析来确定哪种连接算法更适合特定的场景。

优化 Nested Loop Join 为 Hash Join的好处

  1. 性能提升: Hash Join通常比Nested Loop Join具有更好的性能。在Nested Loop Join中,对于外部表中的每一行,都需要在内部表中进行线性搜索以查找匹配的行。这种搜索方式的时间复杂度是O(n*m),其中n是外部表的大小,m是内部表的大小。而在Hash Join中,只需要对其中一个表进行哈希处理,然后通过哈希表快速查找另一个表的匹配行,时间复杂度是O(n+m),通常情况下比Nested Loop Join更快。

  2. 减少资源消耗: 在Nested Loop Join中,需要在内存中保留外部表的当前行,并且需要频繁地访问内部表以查找匹配的行。这样会占用大量的CPU和内存资源。而在Hash Join中,只需要构建一个哈希表来存储其中一个表的数据,然后通过哈希表快速查找匹配行,不需要频繁地访问内部表,可以减少资源消耗。

  3. 适用于大型数据集: 当数据集较大时,Nested Loop Join的性能可能会非常低下,因为需要进行大量的线性搜索操作。而Hash Join在处理大型数据集时性能较好,因为可以利用哈希表的快速查找特性,通常比Nested Loop Join更适用于大型数据集的连接操作。

分布式hash join怎么实现

分布式 Hash Join 是一种在分布式系统中执行连接操作的方法,其中连接的两个表存储在不同的节点上。实现分布式 Hash Join 需要解决数据分布、数据传输、哈希函数选择和节点间通信等多个方面的问题。下面是实现分布式 Hash Join 的基本步骤:

  1. 数据分布: 首先,需要将连接的两个表分别分布在不同的节点上。通常会采用一种分片或分区策略,使得连接键相同的行被分配到相同的节点上,以便后续的连接操作。

  2. 哈希函数选择: 为了实现分布式 Hash Join,需要选择一个合适的哈希函数来将连接键映射到节点。这个哈希函数需要保证相同连接键的行被哈希到相同的节点上,从而确保连接的正确性。

  3. 数据传输: 在进行连接之前,需要将连接的两个表中的数据发送到执行连接操作的节点上。这涉及到网络通信和数据传输,通常需要考虑数据压缩和并行传输等技术来提高效率。

  4. 哈希分区: 在接收到连接表的数据之后,每个节点需要使用相同的哈希函数对接收到的数据进行哈希分区,将相同连接键的行分配到相同的节点上。

  5. 局部连接: 每个节点在本地执行 Hash Join 操作,即将本地表与远程表进行连接。由于相同连接键的行已经被哈希分区到了同一个节点上,因此可以直接在本地进行连接操作,减少数据传输的开销。

  6. 结果传输: 在局部连接完成后,需要将连接结果传输回客户端或者其他节点。这涉及到网络通信和数据传输,同样需要考虑效率和并行传输等技术。

  7. 最终连接: 最后,在接收到所有局部连接的结果之后,可以在客户端或者一个中心节点上进行最终的连接操作,得到最终的连接结果。

事务四种隔离级别,从低到高说一下。使用怎么样的并发控制手段,实现这四种隔离级别。

在数据库管理系统中,有四种主要的隔离级别,它们是:

  1. 读未提交(Read Uncommitted):在这个级别下,事务可以读取到其他事务未提交的数据变更。这意味着一个事务可以看到另一个正在进行的事务的未提交修改。实现这个级别的并发控制手段通常是使用锁来防止脏读(Dirty Read)。

  2. 读已提交(Read Committed):在这个级别下,事务只能读取到其他已提交的事务的数据变更。这意味着一个事务不会读取到未提交的数据,但是可能会读取到另一个事务已经提交的数据。通常通过在读取过程中加锁,或者使用快照隔离来实现。

  3. 可重复读(Repeatable Read):在这个级别下,确保同一个事务在多次读取同一数据时,会看到同样的数据。这意味着即使其他事务对数据进行了修改或插入,也不会影响到当前事务中对该数据的读取。常用的并发控制手段是使用多版本并发控制(MVCC)来实现。

  4. 串行化(Serializable):在这个级别下,事务之间完全隔离,就好像事务是依次顺序执行一样。这意味着并发执行的事务不能相互干扰,每个事务都仿佛在一个独立的执行环境中。常用的实现方法是使用严格的锁机制来确保事务的完全隔离。

这些隔离级别是为了解决并发控制中的各种问题而设计的,例如脏读、不可重复读和幻读。通过选择适当的隔离级别,可以根据应用程序的需求来平衡并发性和数据一致性。

脏读、不可重复读和幻读

脏读 dirty reads:read before commit【有些时候t2后续其实abort了。但是t1还是去读了t2abort之前写的东西】

解决:加shared lock

不可重复读Non-repeatable reads:【脏读也属于这个】

幻读phantom reads:幻读侧重的方面是某一次的 select 操作得到的结果所表征的数据状态无法支撑后续的业务操作。更为具体一些:select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入,此时就发生了幻读。

解决:加行锁

• Read-Write Conflicts (“Unrepeatable Reads”): A transaction is not able to get the same value when reading the same object multiple times.

• Write-Read Conflicts (“Dirty Reads”): A transaction sees the write effects of a different transaction before that transaction committed its changes.

• Write-Write conflict (“Lost Updates”): One transaction overwrites the uncommitted data of another concurrent transaction.

2PL

优点

  1. 保证冲突可串行化: 2PL 确实能够生成满足冲突可串行化的调度,这是因为2PL确保了事务在执行期间会获取所有需要的锁,并且不会释放锁直到事务完成。由于2PL的锁管理策略,生成的调度图的优先级图是无环的,这意味着这种调度是冲突可串行化的,即没有出现交叉的写入操作。

  2. 存在级联终止问题: 但是,2PL 也存在级联终止的问题,即当一个事务发生了回滚时,可能会导致其他事务也需要回滚的情况。这是因为当一个事务在释放锁之前发生了错误或需要回滚时,它所持有的锁可能会被其他事务等待,这些事务可能已经被提交或者正在执行。因此,其他事务可能需要回滚以保持系统的一致性。

缺点:

  1. 死锁风险: 2PL容易导致死锁的发生。【拿锁的phase】当多个事务相互等待对方持有的锁时,可能会出现死锁的情况,导致系统无法继续进行。解决死锁问题需要引入死锁检测和死锁恢复机制,增加了系统的复杂性和开销。

  2. 锁粒度过大或过小: 2PL中的锁粒度对于并发控制至关重要。如果锁粒度过大,会降低并发性能,增加系统的开销;如果锁粒度过小,会增加锁的竞争和冲突,导致频繁的锁等待和释放,降低系统的效率。

  3. 长事务影响: 由于2PL中事务需要在加锁阶段获取所有需要的锁,并且在事务结束之前不会释放这些锁,因此长事务可能会持有锁的时间过长,导致其他事务的等待时间增加,影响系统的吞吐量和性能。

  4. 不适用于高并发场景: 在高并发场景下,2PL可能会导致大量的锁竞争和冲突,增加了系统的开销和复杂性。特别是在读多写少的场景下,2PL可能会导致读写之间的竞争,降低了系统的并发性能。

  5. 出现dirty reads:【释放phase】【除非改成STRONG STRICT 2PL,即commit才放所有的锁】A schedule is strict if a value written by a txn is not read or overwritten by other txns until that txn finishes.

B 树,B+树,红黑树,跳表的优缺点,什么场景用什么

B 树、B+ 树、红黑树和跳表都是常见的数据结构,用于实现有序集合的索引结构,各有其优点和适用场景。

B 树

  • 优点

    1. B 树的节点通常比较大,可以减少磁盘 I/O 次数,适合在磁盘上存储大量数据的索引。
    2. B 树的平衡性好,插入和删除操作的代价较低。
  • 缺点

    1. B 树的节点通常较大,可能导致树的高度较高,从而增加了查找操作的开销。
    2. B 树的节点分裂和合并操作可能比较复杂,实现难度较高。

B+ 树

  • 优点

    1. B+ 树在内部节点只存储索引信息,叶子节点存储了全部数据,这样可以提高内存利用率。
    2. B+ 树的叶子节点形成了一个有序链表,便于范围查询和范围遍历。
  • 缺点

    1. B+ 树的非叶子节点只包含索引信息,可能需要更多的磁盘 I/O 操作来定位到具体的数据。
    2. 插入和删除操作可能需要更新更多的节点,导致操作代价较高。

红黑树

  • 优点

    1. 红黑树是一种平衡二叉查找树,保证了查找、插入和删除操作的时间复杂度稳定在 O(log n)。
    2. 红黑树的实现比较简单,易于理解和实现。
  • 缺点

    1. 红黑树的节点可能会比较深,因此在大规模数据集合的情况下,性能可能不如 B 树和 B+ 树。
    2. 红黑树不支持范围查询,对于需要范围查询的场景不太适用。

跳表

  • 优点

    1. 跳表是一种高效的动态数据结构,支持快速的查找、插入和删除操作,时间复杂度为 O(log n)。
    2. 跳表的实现比较简单,易于理解和实现。
  • 缺点

    1. 跳表需要额外的空间来存储索引层,占用了较多的内存空间。
    2. 跳表的维护和调整操作可能比较复杂,尤其是在多线程环境下。

适用场景

  • 当数据量较大,且存储在磁盘上时,适合使用 B 树和 B+ 树。
  • 当数据量较小,或者内存空间较为有限时,可以考虑使用红黑树。
  • 当需要支持范围查询,并且数据集合较大时,可以选择 B+ 树或跳表。
  • 当需要高效的插入、删除和查找操作,并且数据量不是特别大时,可以选择红黑树或跳表。

布隆过滤器作用

布隆过滤器(Bloom Filter)是一种数据结构,主要用于快速判断一个元素是否属于一个集合,它可以有效地解决查找性能要求高、数据量大的场景下的问题。

布隆过滤器的作用包括以下几个方面:

  1. 快速成员查询: 布隆过滤器可以快速判断一个元素是否属于一个集合,具有快速的查找速度。如果一个元素被布隆过滤器判断为不在集合中,那么该元素一定不在集合中;但如果一个元素被布隆过滤器判断为在集合中,那么该元素可能存在于集合中,也可能是误判。

  2. 节省存储空间: 布隆过滤器可以使用比较少的存储空间来表示一个大型集合,这是因为布隆过滤器不存储元素本身,而是使用位数组和一组哈希函数来表示元素的存在情况,从而节省了存储空间。

  3. 应用于缓存和数据库查询优化: 布隆过滤器常常被用于缓存系统中,用于快速判断一个缓存中是否存在某个值,从而减少不必要的数据库查询。通过布隆过滤器可以快速判断一个值是否在缓存中,如果不在,则直接从数据库中获取,从而减少了对数据库的访问。

  4. 防止缓存穿透和击穿: 布隆过滤器可以用于防止缓存穿透和击穿问题。当一个请求查询一个不存在于缓存中的值时,布隆过滤器可以快速判断该值是否一定不存在于缓存中,如果一定不存在,则直接返回空结果,避免了对数据库的查询。

  5. 网络安全领域的黑名单过滤: 布隆过滤器可以用于网络安全领域,比如黑名单过滤,快速判断一个 IP 地址或者 URL 是否在黑名单中。

总的来说,布隆过滤器适用于对查询性能要求高、数据量大的场景,能够提供快速的成员查询,并且节省存储空间。但需要注意的是,布隆过滤器存在一定的误判率,因此在一些对准确性要求较高的场景中,需要谨慎使用。

DBMS vs OS

2PL的优劣

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值