MYSQL面试题总结(题目来源JavaGuide)

MYSQL基础架构 

问题1:一条 SQL语句在MySQL中的执行过程 

1. 解析阶段 (Parsing)

  • 查询分析:当用户提交一个 SQL 语句时,MySQL 首先会对语句进行解析。这个过程会检查语法是否正确,确保 SQL 语句符合 MySQL 的语法规则。如果发现语法错误,MySQL 会返回一个错误信息。

  • 语法树生成:解析器会将 SQL 语句转换为一个内部的语法树(也叫解析树)。这棵树表示了 SQL 语句的结构。

2. 优化阶段 (Optimization)

  • 查询优化:优化器会分析生成的语法树,检查不同执行计划的成本,并选择一个最优的执行计划。执行计划的选择是基于表的大小、索引的可用性、查询的复杂性等因素。

  • 索引选择:优化器会决定是否使用索引,并选择最合适的索引来加速查询。

  • 查询重写:优化器可能会对 SQL 语句进行重写,使得查询更加高效。例如,某些复杂的子查询可以被重写为联接查询(JOIN)。

3. 执行阶段 (Execution)

  • 执行计划执行:根据优化器选择的执行计划,MySQL 会执行实际的数据库操作。执行计划会决定查询顺序、表扫描、索引扫描等。

  • 数据检索:MySQL 会访问存储引擎来检索数据。存储引擎负责从磁盘中读取数据(或者从内存中读取,如果数据在缓存中)。在执行过程中,MySQL 可能会进行数据的排序、聚合等操作。

4. 结果返回阶段 (Result Returning)

  • 数据返回:查询执行完毕后,MySQL 会将结果返回给客户端。对于 SELECT 查询,返回的结果可能是一个结果集;对于其他类型的查询(如 INSERT、UPDATE、DELETE),则返回影响的行数。

MySQL 执行过程简化流程图:

  1. 用户提交 SQL 语句。

  2. SQL 语句被解析成语法树。

  3. 优化器选择最优的执行计划。

  4. 存储引擎执行查询操作。

  5. 将结果返回给客户端。

内部架构对执行过程的影响:

  • 查询缓存:如果查询结果已被缓存且数据没有变化,MySQL 可能直接从缓存中获取结果,而不需要重新执行查询。

  • 存储引擎:MySQL 支持不同的存储引擎(如 InnoDB 和 MyISAM),每种存储引擎的行为不同,尤其是在执行查询时,它们对数据的管理和检索方式有所差异。

  • 锁机制:在并发环境下,MySQL 会使用不同的锁机制(行锁、表锁等)来保证数据的一致性,这会影响查询的执行。

MYSQL存储引擎 

问题1: MySQL 提供了哪些存储引擎?

MySQL 提供了多个存储引擎,每个引擎在性能、事务支持、数据存储方式等方面有所不同。下面是 MySQL 中最常见的存储引擎:

1. InnoDB

  • 特点:这是 MySQL 默认的存储引擎,支持 事务外键行级锁ACID(原子性、一致性、隔离性、持久性) 等特性。

  • 事务支持:支持全事务(支持 COMMIT 和 ROLLBACK),使用 写前日志(WAL) 技术,能够保证数据的完整性。

  • 锁机制:使用行级锁,避免了表级锁的阻塞,适合高并发环境。

  • 适用场景:适合 OLTP(联机事务处理)应用程序,尤其是在需要高并发的环境下。

2. MyISAM

  • 特点:MyISAM 是 MySQL 传统的存储引擎,不支持事务,也不支持外键。

  • 事务支持:不支持事务管理。

  • 锁机制:使用表级锁,这在写操作频繁的场景下会导致性能瓶颈。

  • 适用场景:适用于读多写少的场景,特别是对性能要求较高的查询密集型应用。

  • 优点:查询速度较快、数据压缩功能较强。

  • 缺点:不支持事务、崩溃恢复较差。

3. Memory(HEAP)

  • 特点:Memory 存储引擎将所有数据存储在内存中,因此访问速度非常快。

  • 事务支持:不支持事务。

  • 锁机制:使用表级锁。

  • 适用场景:适用于临时表和缓存场景,尤其是需要快速查询的小型数据集。

  • 优点:非常高效的读写性能,适合做临时数据存储。

  • 缺点:数据存储在内存中,服务器重启时数据会丢失,不适合持久化数据存储。

4. CSV

  • 特点:CSV 存储引擎将数据存储为逗号分隔的文本文件。每一行数据对应一个记录,每一列数据用逗号分隔。

  • 事务支持:不支持事务。

  • 适用场景:适用于数据导入导出,或者需要与其他系统(如 Excel)进行数据交互的场景。

  • 优点:数据存储格式简单,便于与外部系统交换。

  • 缺点:性能较差,不适合大规模数据存储和频繁更新。

5. ARCHIVE

  • 特点:ARCHIVE 存储引擎用于存储历史数据,采用压缩存储方式,可以节省存储空间。

  • 事务支持:不支持事务。

  • 锁机制:使用表级锁。

  • 适用场景:适用于归档存储大量历史数据,且数据访问频率低的场景。

  • 优点:节省存储空间,适合存储大规模历史数据。

  • 缺点:不支持索引,只能执行简单的 SELECT 查询,无法执行 UPDATE 或 DELETE 操作。

6. Federated

  • 特点:Federated 引擎允许跨服务器访问数据,即使数据存储在不同的 MySQL 实例中,也可以像查询本地表一样查询远程表。

  • 事务支持:不支持事务。

  • 适用场景:适用于分布式数据库系统,能够连接并访问其他 MySQL 实例的数据。

  • 优点:方便远程表的访问。

  • 缺点:性能较差,复杂度较高,不支持事务和本地索引。

7. NDB (Cluster)

  • 特点:NDB 存储引擎是 MySQL Cluster 的一部分,提供高可用性和数据分布式存储功能。

  • 事务支持:支持事务。

  • 锁机制:使用行级锁,并支持分布式锁。

  • 适用场景:适用于需要高可用、高性能、分布式数据库系统的场景,通常用于电信、金融等行业。

  • 优点:支持分布式存储和高可用性。

  • 缺点:配置复杂,性能优化较为困难。

8. TokuDB

  • 特点:TokuDB 是一个高效的事务型存储引擎,支持 ACID 和数据压缩,能够处理大数据量的写入操作。

  • 事务支持:支持事务。

  • 锁机制:使用行级锁。

  • 适用场景:适用于需要处理大规模数据且对写入性能要求较高的应用。

  • 优点:支持高效的数据压缩,能够显著提高写入性能。

  • 缺点:性能优化较为复杂,社区支持不如 InnoDB。

9. Blackhole

  • 特点:Blackhole 引擎不存储数据,所有插入的数据都会被丢弃,但可以用来复制数据。

  • 事务支持:不支持事务。

  • 适用场景:适用于日志系统或者数据复制场景,特别是作为复制源使用时。

  • 优点:不占用存储空间,适合特殊场景。

  • 缺点:不能存储数据,适用性有限。

10. Spider

  • 特点:Spider 存储引擎用于分布式数据库,它支持跨多个 MySQL 实例进行数据分区和分布式查询。

  • 事务支持:支持事务。

  • 适用场景:适用于大规模分布式数据库,尤其是在需要水平扩展的应用中。

  • 优点:支持高效的分布式数据存储。

  • 缺点:配置和维护较为复杂,性能可能受限于分布式架构。

总结

MySQL 提供了多种存储引擎,可以根据应用的具体需求选择合适的存储引擎:

  • InnoDB:适合事务和高并发的 OLTP 应用。

  • MyISAM:适合对性能要求较高的查询密集型应用。

  • Memory:适合缓存和临时数据存储。

  • CSV:适合数据交换和导出导入。

  • NDB (Cluster):适合需要高可用性的分布式应用。

 问题2:MySQL 存储引擎架构了解吗?

MySQL 的存储引擎架构是分层的,主要包括以下几个层次:

  1. MySQL Server 层(SQL 层)

  2. 存储引擎接口层(Storage Engine Interface)

  3. 存储引擎层(Storage Engines)

1. MySQL Server 层(SQL 层)

  • SQL 解析和优化:MySQL Server 层负责处理客户端发来的 SQL 请求。它首先会解析 SQL 语句并进行语法检查。接下来,优化器会根据查询的内容生成执行计划,并选择最优的查询策略。

  • 查询执行:执行计划确定后,SQL 层会将查询任务传递给存储引擎层进行实际的数据存取操作。这个过程通常通过存储引擎接口(Storage Engine Interface)进行交互。

2. 存储引擎接口层(Storage Engine Interface)

存储引擎接口层是 SQL 层和存储引擎层之间的一个抽象层,提供了统一的接口来访问不同的存储引擎。这个接口层允许 MySQL 支持多种存储引擎,使得用户可以选择最适合其应用的存储引擎。

  • SQL 层和存储引擎的交互:MySQL Server 会调用存储引擎接口进行数据的操作(如 INSERT、SELECT、UPDATE、DELETE)。通过接口,MySQL 可以与不同的存储引擎进行交互,而不需要了解存储引擎的具体实现细节。

  • 操作指令:接口提供的指令包括数据的插入、查询、删除、更新、锁定、事务管理等功能。不同的存储引擎会根据其特性实现这些操作。

3. 存储引擎层(Storage Engines)

存储引擎层是 MySQL 数据库的核心组成部分,负责具体的数据存储和访问操作。每种存储引擎都有其独特的特性和实现方式。例如,InnoDB 支持事务、行级锁和外键约束,而 MyISAM 不支持事务和外键。不同的存储引擎在数据的存储格式、索引、并发控制、崩溃恢复等方面有所不同。

  • 存储引擎的工作:存储引擎在接收到 SQL 层的请求后,负责执行实际的底层数据操作(如写入磁盘、读取数据、创建索引等)。在执行这些操作时,存储引擎会通过其内部机制来确保数据的完整性和一致性。

  • 常见存储引擎:如 InnoDBMyISAMMemoryCSVNDB 等。

 问题3:MyISAM 和 InnoDB 的区别

特性InnoDBMyISAM
事务支持支持(ACID)不支持事务
锁机制行级锁表级锁
外键支持支持外键不支持外键
数据存储表空间,支持自动扩展每个表单独存储(.MYD.MYI
崩溃恢复支持,自动恢复不支持自动恢复,需要手动修复
性能高并发事务支持,较慢的读操作适用于查询密集型,读性能更高
索引聚集索引(主键为聚集索引),二级索引指向主键非聚集索引
表大小支持大表小表,适合数据量较小的场景
适用场景高并发、事务性应用(如 OLTP)只读或读多写少的应用(如数据仓库、日志)

MYSQL事务 

问题1:何谓事务?

事务(Transaction) 是数据库管理系统中的一个重要概念,指的是一组作为单个单位执行的数据库操作。这些操作要么全部成功,要么全部失败。事务确保了数据库的 ACID 属性(原子性、一致性、隔离性、持久性),从而保证了数据库操作的完整性和一致性。

事务的四个特性(ACID 属性)

  1. 原子性(Atomicity)

    • 事务中的所有操作要么全部成功,要么全部失败。换句话说,事务被视为一个原子操作,要么完全执行,要么完全不执行。

    • 如果事务中的一部分操作失败,所有已经执行的操作都会被回滚到事务开始之前的状态。

  2. 一致性(Consistency)

    • 事务必须使数据库从一个一致的状态变为另一个一致的状态。即使发生了系统崩溃或其他错误,事务也应保证数据库的完整性和一致性没有被破坏。

    • 例如,如果数据库中有某些约束(如外键约束),在事务执行过程中,这些约束必须得到保持。

  3. 隔离性(Isolation)

    • 每个事务的执行不应受到其他事务的干扰。在多个事务并发执行时,事务的执行应该是彼此隔离的,避免出现事务之间相互干扰或产生脏数据。

    • 隔离性有不同的级别:读未提交(Read Uncommitted)读已提交(Read Committed)可重复读(Repeatable Read)串行化(Serializable),它们控制了不同事务对数据的可见性。

  4. 持久性(Durability)

    • 一旦事务成功提交,对数据库的修改就是永久性的,即使发生系统崩溃或其他故障,数据的修改也不会丢失。

    • 数据库会通过日志(如写前日志、重做日志等)保证即使系统发生崩溃,也能在恢复时保证数据的持久性。

事务的基本操作

  1. 开始事务(Start Transaction):事务开始时,数据库会记录一个事务的开始标志,表示事务已经启动。

  2. 提交事务(Commit):事务中所有操作成功执行后,可以使用 COMMIT 命令提交事务,表示事务中的所有操作将永久保存到数据库。

  3. 回滚事务(Rollback):如果事务执行过程中出现错误,使用 ROLLBACK 命令撤销事务中的所有操作,恢复到事务开始之前的状态。

事务的使用场景

事务通常用于需要确保数据一致性和完整性的操作场景,如:

  • 银行转账:从账户 A 转账到账户 B,需要保证从账户 A 扣除金额和向账户 B 增加金额两个操作要么都成功,要么都失败。

  • 在线购物:用户购买商品时,确保商品库存减少、订单记录插入、支付完成等操作要么都成功,要么都不发生(如果支付失败,则不更新库存等)。

  • 银行贷款:贷款申请时,需要保证贷款金额被正确计算和存储,以及相关的利息、还款期等信息都被正确处理。

事务的隔离级别(Isolation Levels)

在数据库系统中,不同事务之间可能会并发执行,这时就需要控制事务之间的 隔离性。SQL 标准定义了四种隔离级别,用来控制不同事务对数据的可见性和访问冲突:

  1. 读未提交(Read Uncommitted)

    • 允许一个事务读取另一个事务未提交的数据(脏读)。即便另一个事务回滚,当前事务也已经看到这些数据。

    • 最低的隔离级别,性能最好,但可能导致不一致数据。

  2. 读已提交(Read Committed)

    • 事务只能读取已经提交的数据。一个事务不会读取到其他事务未提交的数据(避免了脏读)。

    • 但是,可能会出现“不可重复读”问题,即同一个查询在事务内可能返回不同的结果。

  3. 可重复读(Repeatable Read)

    • 保证同一个事务中的查询结果是稳定的,即使其他事务对数据进行了修改,当前事务中的查询结果也不会发生变化。

    • 但是,可能会出现“幻读”现象,即某些记录可能在同一事务中被另一事务插入,导致查询结果与预期不一致。

  4. 串行化(Serializable)

    • 最高的隔离级别,完全避免了脏读、不可重复读和幻读。所有事务按顺序执行,互相不干扰。

    • 性能最差,因为它要求事务串行执行,但保证了数据的最大一致性。

事务的例子

假设我们有一个银行账户转账的场景,账户 A 和账户 B 都有一些资金。我们想从账户 A 转账到账户 B。

START TRANSACTION;-- 从账户 A 扣除转账金额
UPDATE accounts SET balance = balance - 100 WHERE account_id = 'A';-- 向账户 B 增加转账金额
UPDATE accounts SET balance = balance + 100 WHERE account_id = 'B';-- 如果以上操作都成功,提交事务
COMMIT;

在这个例子中,如果在两个 UPDATE 语句之间发生了崩溃,系统将不会知道这笔交易的状态,因此不会进行半成功的操作。通过事务,只有当两个操作都成功时,数据才会被永久保存,否则它会被回滚

问题2:何谓数据库事务? 

数据库事务(Database Transaction)是指在数据库管理系统(DBMS)中,一组数据库操作的集合,这些操作要么全部成功,要么全部失败。事务是数据库操作中的一个逻辑单元,通常包含多个对数据库的操作,如插入、更新、删除等。事务的目的是确保数据库的一致性、完整性和可靠性。

数据库事务确保了对数据库的操作要么完全完成,要么在出现问题时回滚至事务开始之前的状态,从而保证数据的一致性和可靠性。

问题3:ACID 特性指的是什么? 

ACID 特性 是数据库事务的四个核心特性,它们确保了数据库在处理多个事务时的数据一致性、可靠性和安全性。ACID 是原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)这四个特性的缩写。每个特性都解决了事务处理中的不同问题,确保在发生错误、崩溃或并发操作时,数据库仍然能够保持正确性和完整性。

1. 原子性(Atomicity)

  • 定义:原子性确保事务中的所有操作要么完全执行,要么完全不执行。事务是一个不可分割的单位。如果事务中的任何一个操作失败,整个事务都会回滚,所有已经执行的操作都会撤销,数据库回到事务开始前的状态。

  • 举例:假设你从账户 A 转账 100 元到账户 B,事务包括两个操作:从账户 A 扣款 100 元和向账户 B 增加 100 元。如果扣款成功,但增加到账户 B 失败,原子性确保整个事务回滚,账户 A 的余额恢复,账户 B 不会增加 100 元。事务不会留下不一致的中间状态。

2. 一致性(Consistency)

  • 定义:一致性保证事务在执行前后,数据库始终处于一个合法的状态。每个事务都必须遵循数据库的约束规则(如数据类型、主外键约束等),并且所有的数据变动必须确保数据库在执行事务前后的完整性。

  • 举例:假设数据库定义了一个约束规则,账户的余额不能为负数。如果在转账过程中,账户 A 的余额被扣除而余额变为负数,一致性将确保事务失败并回滚,数据库状态不允许发生不一致或非法操作。

3. 隔离性(Isolation)

  • 定义:隔离性确保并发执行的事务彼此独立,一个事务的执行不会受到其他事务的干扰。即使多个事务并发执行,它们的数据修改对彼此是隔离的,其他事务无法读取到尚未提交的数据。

  • 隔离级别:数据库提供了四种隔离级别来平衡并发性能和数据一致性:

    • 读未提交(Read Uncommitted):事务可以读取其他事务尚未提交的数据,可能会发生“脏读”。

    • 读已提交(Read Committed):事务只能读取其他事务已经提交的数据,避免了脏读,但可能会出现“不可重复读”。

    • 可重复读(Repeatable Read):同一事务内的查询结果始终保持一致,避免了不可重复读,但可能会出现“幻读”。

    • 串行化(Serializable):事务之间完全隔离,所有事务按顺序执行,避免了脏读、不可重复读和幻读,但性能较差。

  • 举例:如果事务 A 正在查询账户余额,事务 B 同时修改余额。隔离性确保事务 A 在读取数据时不会看到事务 B 尚未提交的修改,避免了脏读。

4. 持久性(Durability)

  • 定义:持久性确保一旦事务提交,其对数据库的更改将永久保存,不会丢失。即使系统崩溃或发生其他故障,已提交的事务所做的修改会被持久化到磁盘或其他持久存储中。

  • 举例:假设事务 A 成功执行并提交了对账户余额的修改,持久性确保即使系统突然崩溃,账户的余额变化依然会被保留下来,并恢复到正确的状态。


ACID 特性总结

特性

说明

举例

原子性

事务中的所有操作要么全都成功,要么全都失败,操作不可分割。

如果转账操作的某一部分失败,整个转账事务会回滚。

一致性

事务开始前和结束后,数据库必须处于一致的状态,符合所有规则。

转账时,账户余额不可能为负数。

隔离性

事务的执行不应受到其他事务的干扰,不同事务的操作是隔离的。

不同事务并发执行时,一个事务的更新对另一个事务不可见。

持久性

一旦事务提交,对数据库的更改是永久性的,不会丢失。

提交的转账操作即使在系统崩溃后也不会丢失。

ACID 特性的重要性

  • 原子性:确保事务要么完全执行,要么完全不执行,避免部分操作影响数据库。

  • 一致性:保持数据库在事务执行前后处于合法状态,避免产生不一致的数据。

  • 隔离性:确保多个事务同时执行时,它们的操作不会互相干扰,保证数据的独立性。

  • 持久性:确保已提交的事务不会丢失,即使系统发生故障,数据也能恢复。

问题4:并发事务带来了哪些问题? 

并发事务是指多个事务同时在数据库中执行,它们可能涉及对相同数据的操作。尽管并发处理可以提高系统的吞吐量和响应速度,但也带来了若干问题,因为多个事务在执行时可能会互相干扰,导致数据不一致或产生异常行为。常见的并发事务问题主要包括以下几种:

1. 脏读(Dirty Read)

  • 定义:一个事务读取了另一个事务尚未提交的数据。如果该事务回滚,那么第一个事务读取的数据就变成了无效或不一致的。

  • 问题产生:当事务 A 修改了某个数据并尚未提交时,事务 B 读取了该数据。如果事务 A 后续回滚,事务 B 所读取的数据就是“脏”的。

  • 例子

    • 事务 A 对账户余额进行更新,但没有提交。

    • 事务 B 读取到账户余额更新后的数据。

    • 事务 A 回滚,账户余额恢复到原值。

    • 结果:事务 B 的数据读取结果并不正确。

  • 解决方法:通过使用 读已提交 隔离级别(Read Committed)来避免脏读。

2. 不可重复读(Non-Repeatable Read)

  • 定义:一个事务在读取某个数据项后,在该事务的后续操作中,该数据项的值发生了变化。这是由于其他事务对数据的修改。

  • 问题产生:在同一个事务中,多个读取操作期望返回相同的结果,但由于其他事务的干扰,数据发生了变化。

  • 例子

    • 事务 A 读取账户余额为 100 元。

    • 事务 B 修改了同一账户余额为 150 元并提交。

    • 事务 A 再次读取账户余额时,得到的是 150 元,而不是第一次读取的 100 元。

    • 结果:事务 A 期望的余额没有得到一致的返回,导致不一致的读取。

  • 解决方法:通过使用 可重复读 隔离级别(Repeatable Read)来避免不可重复读。

3. 幻读(Phantom Read)

  • 定义:一个事务在执行查询时,另一个事务插入、删除或更新了符合该查询条件的记录,使得第一次查询和第二次查询的结果不一致。幻读主要发生在查询返回的是行而不是具体的值时。

  • 问题产生:一个事务执行了查询,另一个事务在该查询范围内插入或删除了数据,导致事务 A 执行的多次查询结果不一致。

  • 例子

    • 事务 A 执行查询:查找余额大于 100 元的账户,假设返回了 10 条记录。

    • 事务 B 插入了一条新的余额大于 100 元的账户。

    • 事务 A 再次执行相同的查询,结果包含了新增的账户,变成了 11 条记录。

    • 结果:事务 A 得到了不同的查询结果,这就是幻读。

  • 解决方法:通过使用 串行化 隔离级别(Serializable)来避免幻读。

4. 丢失更新(Lost Update)

  • 定义:两个事务并发修改同一数据项时,某个事务的更新会被另一个事务的更新覆盖,导致其中一个事务的更新丢失。

  • 问题产生:两个事务读取相同数据并进行修改,最后提交时,第二个事务的修改会覆盖第一个事务的修改,导致第一个事务的更改丢失。

  • 例子

    • 事务 A 和事务 B 都读取账户余额,并将其修改为不同的金额。

    • 事务 A 提交了它的修改,事务 B 也提交了它的修改。

    • 事务 B 的提交覆盖了事务 A 的更新,导致事务 A 的更新丢失。

  • 解决方法:通过使用 行级锁乐观锁,确保在事务提交前,修改的数据没有被其他事务更改。

5. 死锁(Deadlock)

  • 定义:两个或多个事务在执行过程中,由于互相持有对方需要的锁,导致无法继续执行,陷入死锁状态。

  • 问题产生:事务 A 持有锁 1,并等待锁 2;事务 B 持有锁 2,并等待锁 1。由于相互等待,事务无法继续执行,导致死锁。

  • 例子

    • 事务 A 请求锁定表 X,然后请求锁定表 Y。

    • 事务 B 请求锁定表 Y,然后请求锁定表 X。

    • 结果:事务 A 和事务 B 都在等待对方释放锁,无法继续执行,进入死锁状态。

  • 解决方法

    • 使用 死锁检测机制,在发生死锁时回滚其中一个事务。

    • 或者通过 锁粒度 控制,避免多个事务持有锁时发生死锁。


如何避免并发事务问题?

  1. 使用合适的事务隔离级别

    • 事务隔离级别控制事务执行时对数据的访问限制,合适的隔离级别可以有效避免并发问题。数据库提供了四个标准的隔离级别,分别是:

      • 读未提交(Read Uncommitted):最低级别,允许脏读。

      • 读已提交(Read Committed):避免脏读,但可能出现不可重复读。

      • 可重复读(Repeatable Read):避免脏读和不可重复读,但可能出现幻读。

      • 串行化(Serializable):最高级别,避免所有并发问题,但性能最差。

  2. 加锁机制

    • 使用 行级锁表级锁 来限制多个事务对相同数据的并发访问。例如,锁定被修改的数据行,防止其他事务同时修改该行。

    • 悲观锁(Pessimistic Locking)会主动加锁,确保事务执行时没有其他事务对其进行修改。

    • 乐观锁(Optimistic Locking)假设没有冲突,在提交时检查是否发生了其他事务的修改,若有冲突则回滚。

  3. 死锁检测与处理

    • 使用 死锁检测算法 来监控和解决死锁。当数据库检测到死锁时,可以自动选择回滚某个事务以打破死锁循环。

    • 可以设置合理的事务超时,以减少死锁的概率。

  4. 使用合适的事务粒度

    • 控制事务的范围,不要让事务锁住过多不必要的数据,尽量缩小锁的粒度。

问题5:不可重复读和幻读区别

不可重复读(Non-Repeatable Read)幻读(Phantom Read) 都是并发事务中常见的问题,它们的区别主要在于 读取的数据内容读取行为的影响范围。下面我会详细解释它们的含义、区别和示例:

1. 不可重复读(Non-Repeatable Read)

  • 定义:不可重复读指的是在一个事务中,某个数据在两次读取之间发生了变化。也就是说,事务在执行多次相同查询时,查询的结果会不同,因为其他事务对该数据进行了修改。

  • 问题来源:其他事务修改了当前事务所读取的数据,导致同一事务中的两次读取结果不一致。

  • 发生场景:通常发生在 读已提交可重复读 隔离级别下(虽然可重复读解决了脏读问题,但可能无法避免不可重复读)。

  • 示例

    • 事务 A 执行查询 SELECT balance FROM accounts WHERE account_id = 'A',返回余额 100 元。

    • 事务 B 修改了账户 A 的余额,将其更新为 150 元,并提交。

    • 事务 A 再次执行相同的查询,返回余额 150 元。

    • 问题:事务 A 在同一事务中执行了两次相同的查询,却得到了不同的结果。

  • 解决方案:可以通过 可重复读 隔离级别(Repeatable Read)来避免不可重复读,这种级别保证同一事务中的查询结果始终一致。

2. 幻读(Phantom Read)

  • 定义:幻读指的是在一个事务中执行的查询结果发生变化,原因是其他事务插入、更新或删除了符合查询条件的数据。幻读问题通常发生在执行 范围查询 时,查询的结果集因为其他事务的插入或删除而发生了变化。

  • 问题来源:其他事务插入或删除了数据,导致事务查询的结果发生变化。

  • 发生场景:通常发生在 可重复读 隔离级别下,因为该级别保证同一事务中的数据读取是稳定的,但不会避免幻读。要避免幻读,必须使用 串行化 隔离级别。

  • 示例

    • 事务 A 执行查询 SELECT * FROM accounts WHERE balance > 100,假设返回 10 条记录。

    • 事务 B 插入了一条新的余额大于 100 元的账户,并提交。

    • 事务 A 再次执行相同的查询,结果返回了 11 条记录。

    • 问题:事务 A 的两次查询结果发生了变化,第一次查询得到了 10 条记录,第二次查询得到了 11 条记录。新增的记录就是“幻影”数据。

  • 解决方案:通过使用 串行化 隔离级别(Serializable)来避免幻读,这种级别要求事务按顺序执行,避免并发插入、删除等操作干扰查询结果。


不可重复读 vs 幻读:关键区别

特性

不可重复读

幻读

问题类型

同一事务内的两次查询返回不同结果,因为其他事务修改了数据。

同一事务内的查询结果发生变化,因为其他事务插入、删除了数据。

查询的类型

发生在单个数据行的修改中。

发生在 范围查询(如 WHERE balance > 100)中。

查询行为

事务多次查询相同的行数据时,得到不同的值。

事务多次查询相同的条件时,返回的记录数发生变化。

发生原因

其他事务对同一数据行进行了修改(更新)。

其他事务插入或删除了符合查询条件的行。

示例

事务 A 查询账户余额为 100 元,事务 B 修改余额为 150 元,事务 A 再次查询余额为 150 元。

事务 A 查询余额大于 100 元的账户,事务 B 插入一个余额大于 100 元的账户,事务 A 再次查询时得到不同的记录数。

隔离级别解决方案

可重复读(Repeatable Read)

串行化(Serializable)


总结

  • 不可重复读 是指同一事务在执行相同查询时,读取的数据在事务执行期间发生了变化,通常是因为其他事务修改了该数据。

  • 幻读 是指在同一事务中,执行多次查询时,查询结果的记录数发生变化,通常是因为其他事务插入或删除了符合查询条件的数据。

两者的根本区别在于 不可重复读 关注的是数据值的变化,而 幻读 关注的是查询结果集的变化。避免这两种问题的方法也不同,通常需要根据具体的应用场景选择合适的 隔离级别 来控制并发事务的行为。

问题6: SQL 标准定义了哪些事务隔离级别?

SQL 标准定义了 四种事务隔离级别,它们控制了事务执行时对其他事务修改数据的可见性。事务隔离级别的主要目的是平衡 并发性能数据一致性,通过设置不同的隔离级别,可以在不同场景下选择合适的并发控制策略。

这四种隔离级别是:

1. 读未提交(Read Uncommitted)

  • 描述:这是最低的隔离级别。在该级别下,事务 A 可以读取事务 B 未提交的数据。由于可以读取到其他事务正在修改的数据,可能会导致 脏读(Dirty Read) 问题。

  • 问题

    • 脏读:事务 A 读取到事务 B 尚未提交的数据,如果事务 B 最终回滚,事务 A 就读取到了无效数据。

  • 适用场景:这种隔离级别通常用于对数据一致性要求不高的场景,或者在某些需要极高并发性能的应用中使用,但一般不推荐在数据一致性要求较高的业务场景中使用。

  • 例子

    • 事务 A 查询账户余额(可能读取到事务 B 更新但未提交的数据)。

2. 读已提交(Read Committed)

  • 描述:在该隔离级别下,一个事务只能读取其他事务已经提交的数据。虽然避免了脏读,但仍然可能发生 不可重复读(Non-Repeatable Read) 问题,即同一个事务中,读取的数据会因为其他事务的提交而发生变化。

  • 问题

    • 不可重复读:事务 A 在第一次查询时读取到某个数据,事务 B 对该数据进行了修改并提交,然后事务 A 再次查询该数据,结果发生变化。

  • 适用场景:适用于大多数业务场景,尤其是对于性能和数据一致性有一定要求的应用场景。

  • 例子

    • 事务 A 执行查询时,事务 B 对该数据进行修改并提交。事务 A 再次查询时,得到不同的结果。

3. 可重复读(Repeatable Read)

  • 描述:在该隔离级别下,事务 A 在执行查询时,保证同一个事务内多次查询结果相同。换句话说,事务 A 在读取某个数据时,无论事务 B 是否提交,它都会看到同样的值,避免了 不可重复读 问题。但是,在这个级别下,幻读(Phantom Read) 仍然可能发生。

  • 问题

    • 幻读:事务 A 查询某个条件的数据时,事务 B 插入、更新或删除了满足查询条件的数据。事务 A 再次执行相同查询时,结果发生了变化。

  • 适用场景:适用于对数据一致性要求较高,但仍允许一定程度的并发操作的场景,如银行系统、在线商城等。

  • 例子

    • 事务 A 查询余额大于 100 元的账户,事务 B 插入了一条余额大于 100 元的账户。事务 A 再次查询时,结果包含了新增的账户。

4. 串行化(Serializable)

  • 描述:这是最高的隔离级别。在该级别下,事务完全按照顺序执行,不允许并发执行。事务之间的所有操作都严格隔离,避免了脏读、不可重复读和幻读问题。虽然事务之间不会互相影响,但这种隔离级别会大大降低并发性能。

  • 问题

    • 串行化隔离级别通过完全串行化事务来解决所有并发问题,但由于事务执行的严格顺序,会显著降低系统的吞吐量和并发性。

  • 适用场景:适用于对数据一致性要求极高的场景,且事务的并发性不是首要考虑的问题,如资金转账、库存操作等重要业务。

  • 例子

    • 事务 A 查询某个账户余额,事务 B 同时修改该账户余额。串行化隔离级别确保事务 A 必须等事务 B 完成后才能继续执行,保证事务之间的完全隔离。


事务隔离级别总结

隔离级别

脏读

不可重复读

幻读

说明

读未提交 (Read Uncommitted)

允许

允许

允许

事务可以读取到其他事务未提交的数据,导致脏读问题。

读已提交 (Read Committed)

不允许

允许

允许

事务只能读取其他事务已提交的数据,避免脏读,但可能出现不可重复读。

可重复读 (Repeatable Read)

不允许

不允许

允许

事务内查询结果是稳定的,避免不可重复读,但可能发生幻读。

串行化 (Serializable)

不允许

不允许

不允许

事务完全按顺序执行,避免脏读、不可重复读和幻读,但性能较差。

隔离级别的影响

  • 性能 vs 数据一致性:随着隔离级别的提高,数据一致性得到增强,但并发性能会降低。例如,串行化 隔离级别会使并发事务完全按顺序执行,极大地降低系统的吞吐量。

  • 选择隔离级别:在实际应用中,可以根据数据一致性要求和系统性能需求来选择合适的隔离级别。例如,银行、支付等需要保证数据一致性的系统可能使用 可重复读串行化 隔离级别,而对性能要求较高、数据一致性相对宽松的场景则可以使用 读已提交读未提交

问题7:MySQL 的默认隔离级别是什么?能解决幻读问题么? 

MySQL 中,默认的事务隔离级别是 可重复读(Repeatable Read)

1. 可重复读(Repeatable Read)

  • 默认隔离级别:MySQL 使用 可重复读 作为默认的事务隔离级别。在此级别下,事务内的查询结果保持一致,意味着同一个事务中多次查询相同数据时,结果会一致。它能够有效避免 脏读(Dirty Read)不可重复读(Non-Repeatable Read) 问题。

  • 如何工作

    • 事务 A 执行查询,结果会被锁定,即使其他事务修改了数据,事务 A 的查询结果也不会变化。

    • 事务 A 在后续查询时,读取到的数据与第一次查询时相同,避免了不可重复读的问题。

2. 幻读问题

  • 尽管 可重复读 隔离级别可以避免脏读和不可重复读,但它 不能完全解决幻读(Phantom Read) 问题。

  • 幻读 是指在同一事务内,执行多次相同查询时,查询的结果集会发生变化,原因是其他事务在查询过程中插入、更新或删除了符合条件的数据行。可重复读 隔离级别无法防止这种情况,因为它只保证对单个数据行的稳定读取,而不保证整个查询结果集的一致性。

  • 例子

    • 事务 A 执行查询 SELECT * FROM accounts WHERE balance > 100,假设返回 10 条记录。

    • 事务 B 插入了一条余额大于 100 元的账户,并提交。

    • 事务 A 再次执行相同的查询时,返回的记录数变为 11 条,新增的账户就属于幻读数据。

3. 如何解决幻读问题?

  • 串行化(Serializable) 隔离级别可以完全避免幻读、不可重复读和脏读问题。它强制事务按顺序执行,从而避免了并发修改、插入或删除数据的干扰。

  • 如何实现:在 串行化 隔离级别下,MySQL 会通过使用锁来串行化查询操作,使得其他事务不能在当前事务执行期间插入、更新或删除符合查询条件的数据行。

  • 缺点:虽然 串行化 能解决所有并发问题,但性能较差,因为它限制了并发事务的执行,适用于对一致性要求非常高的场景,但通常会牺牲并发性。


总结

  • MySQL 默认隔离级别可重复读(Repeatable Read)

  • 解决幻读问题吗?可重复读 隔离级别不能完全解决幻读问题,它只能防止脏读和不可重复读。如果要避免幻读,必须使用 串行化(Serializable) 隔离级别。

问题8:什么是 MVCC?有什么用?原理了解么? 

什么是 MVCC?

MVCCMulti-Version Concurrency Control,多版本并发控制)是一种并发控制技术,用于提高数据库系统在并发事务环境中的性能和一致性。MVCC 允许多个事务同时执行并读取数据库中的数据,同时确保每个事务都能够看到数据库的一个一致视图,而不会受到其他事务并发修改的影响。

MVCC 的作用

MVCC 主要用于解决数据库中的 并发问题,尤其是在以下方面:

  1. 提高并发性:通过避免长时间的锁定操作,允许多个事务同时执行,从而提高系统的吞吐量和响应速度。

  2. 避免脏读、不可重复读和幻读:MVCC 通过让每个事务在其自己的“快照”下执行,避免了其他事务对它的数据的影响。

  3. 事务隔离:MVCC 确保事务之间的隔离性,确保每个事务看到的是一致且独立的数据。

MVCC 原理

MVCC 的核心思想是通过为每个数据行维护多个版本,来实现并发控制。每个事务会看到自己开始时的数据库快照,而不是其他事务正在修改的实时数据。这样,即使其他事务修改了数据,当前事务也不会受到影响。

1. 数据版本管理

  • 每个数据行在数据库中都有多个版本,每个版本都有一个 时间戳事务 ID,用于标记该数据版本属于哪个事务。

  • 数据行的版本信息通常会包括 创建时间过期时间

    • 创建时间:表示数据版本何时由某个事务创建。

    • 过期时间:表示数据版本何时被另一个事务修改或删除。

  • 在执行 SELECT 查询时,数据库会根据事务的时间戳,选择合适的版本返回给事务。一个事务只会看到在其开始时已经提交的数据版本,未提交的数据对该事务不可见。

2. 实现方式

  • 每次对数据行进行修改时(例如更新、删除),数据库系统不会直接覆盖原数据,而是为其创建一个新的版本,同时更新该版本的创建时间戳或事务 ID。

  • 每个事务会有一个 全局唯一的事务 ID时间戳,并且每个数据行会根据事务的时间戳来判断它是否对当前事务可见。

3. 读取数据

  • 当事务执行查询时,数据库会根据事务的 开始时间戳 来决定它能看到哪些数据版本。只有在该事务开始之前 已提交 的数据版本才是可见的。

  • 如果某个事务修改了数据并尚未提交,那么其他事务就无法看到该数据的最新版本,从而避免了 脏读

4. 提交和回滚

  • 提交:当事务提交时,修改的数据版本会被标记为可见,并且会将相应版本的 过期时间 设置为当前事务的提交时间戳。

  • 回滚:如果事务回滚,所有由该事务修改的数据行将被撤销,任何由该事务创建的数据版本都会被标记为无效,其他事务将无法看到这些回滚的数据。

5. 删除操作

  • 删除操作通常不会立即移除数据,而是通过添加标记或修改版本的过期时间来实现。这使得其他事务在操作时不会看到被删除的数据,直到删除操作完全提交。

MVCC 的优缺点

优点:

  1. 提高并发性:通过减少锁的使用,允许多个事务并发执行,减少了事务间的冲突。

  2. 减少锁竞争:因为每个事务有自己独立的“视图”,可以避免长期的 行级锁表级锁,从而提高了数据库的性能。

  3. 避免读锁:由于 MVCC 允许事务读取其 快照 数据(即事务开始时的数据视图),因此可以避免读取时加锁,减少了 锁等待死锁 的风险。

缺点:

  1. 存储开销:每个数据行可能有多个版本,需要占用更多的存储空间。数据版本的管理和清理也会带来额外的复杂度。

  2. 垃圾回收(清理过期版本):数据库需要定期清理不再需要的过期数据版本,否则会浪费存储空间。这个过程在 MVCC 中通常被称为 清理垃圾(garbage collection)。

  3. 复杂的实现:为了管理多个版本和确保事务的正确性,MVCC 的实现通常会比传统的基于锁的并发控制机制更复杂,且对数据库的内部架构提出了更高的要求。

MySQL 中的 MVCC

在 MySQL 中,特别是 InnoDB 存储引擎中,MVCC 是实现事务隔离的核心技术。以下是 MySQL 中 MVCC 的具体实现:

  • 每个数据行的版本信息:InnoDB 会为每行数据存储额外的两个隐藏字段:DB_TRX_ID(事务 ID)和 DB_ROLL_PTR(回滚指针)。DB_TRX_ID 标识该数据版本属于哪个事务,DB_ROLL_PTR 用于指向回滚日志,以便恢复数据。

  • 事务 ID 和时间戳:InnoDB 会为每个事务分配一个唯一的事务 ID,用于标记事务的开始时间。当事务开始时,它会创建一个数据库的快照,仅能看到在它开始之前已经提交的数据。

  • 一致性视图:每个事务在查询数据时,会有一个一致性视图,确保它只能看到在该事务开始前已经提交的版本。对于正在进行修改的行,事务不会看到这些修改的结果,直到它们被提交。

MVCC 在 MySQL 中解决的问题

  • 脏读:事务无法读取到其他事务未提交的数据。

  • 不可重复读:通过为每个事务提供一致性视图,避免了事务间读到的同一数据不一致的问题。

  • 幻读:虽然 MVCC 在一定程度上能够通过一致性视图来避免幻读,但在某些情况下仍然需要配合行锁来避免幻读(这与事务的隔离级别有关,可重复读 隔离级别下可通过加锁来避免幻读)。

总结

MVCC 是数据库管理系统中重要的并发控制机制,通过提供数据的多版本管理,支持多个事务并发执行,同时保证每个事务的数据一致性和隔离性。它能够有效地避免 脏读不可重复读 问题,但仍然需要通过锁机制和适当的隔离级别来解决 幻读 问题。InnoDB 在 MySQL 中通过 MVCC 来提升并发性能和事务的隔离性,避免了传统基于锁的机制中的一些性能瓶颈。

MYSQL锁 

问题1:表级锁和行级锁了解吗?有什么区别? 

特性表级锁行级锁
锁的粒度锁定整张表锁定特定的行
并发性能低(多事务操作同一表时会互相阻塞)高(允许多个事务并发操作不同行)
锁定范围锁定表中的所有数据行锁定特定的数据行,其他行可供操作
死锁的风险较低,因锁定的范围大较高,多个事务可能同时锁定不同的行
性能影响可能较大,因为整个表会被锁住性能较好,只有当前操作的行被锁定
适用场景操作较少、并发较低的场景高并发、频繁更新、不同行操作的场景
支持的引擎支持所有存储引擎(如 MyISAM、InnoDB)通常由支持事务的存储引擎(如 InnoDB)支持

问题2:行级锁的使用有什么注意事项? 

1. 死锁(Deadlock)

  • 问题描述:死锁发生在两个或多个事务互相等待对方释放锁的情况下,导致事务无法继续执行。

  • 原因:行级锁因为粒度较小,可能导致多个事务在锁定不同的行时相互依赖,形成死锁。例如:

    • 事务 A 锁定了行 1,并准备锁定行 2。

    • 事务 B 锁定了行 2,并准备锁定行 1。

    • 这时候,事务 A 和事务 B 互相等待对方释放锁,导致死锁。

  • 解决方法

    • 使用 较短的事务:尽量缩短事务的执行时间,避免长时间持有锁。

    • 统一的加锁顺序:确保所有事务按相同的顺序对表中的行加锁,避免互相交叉加锁。

    • 数据库自动死锁检测:InnoDB 会自动检测死锁并回滚其中一个事务来解除死锁。

    • 显式的锁超时:可以使用 LOCK_TIMEOUT 设置超时,避免长时间等待锁。

2. 锁粒度和性能

  • 问题描述:行级锁虽然比表级锁更细粒度,但它的管理复杂度更高,可能会导致 锁竞争锁争用。当大量事务同时访问相同的数据行时,可能会导致性能下降。

  • 解决方法

    • 避免过多的并发操作锁定相同的数据行:尽量将事务的锁定范围限制在小范围内,避免多个事务争抢同一行。

    • 合理设计索引:确保事务锁定的数据行具有合适的索引,避免因全表扫描而锁定大量行。

3. 锁升级(Lock Escalation)

  • 问题描述:当事务执行时锁定大量行时,某些数据库系统可能会尝试将行级锁升级为表级锁,以减少锁的管理开销(这通常是自动的)。尽管 InnoDB 不会自动进行锁升级,但若大量事务对同一表进行行级锁定,仍可能引发性能瓶颈。

  • 解决方法

    • 保证事务尽可能快速地提交或回滚,减少持有锁的时间。

    • 使用合适的事务隔离级别,避免不必要的行锁竞争。

4. 隐式锁定

  • 问题描述:某些操作(如 UPDATEDELETE)可能会在没有明确指定的情况下隐式地加锁,这可能会导致不必要的锁持有。

  • 解决方法

    • 明确锁定数据行:使用 SELECT ... FOR UPDATESELECT ... LOCK IN SHARE MODE 来显式锁定数据行,避免事务隐式地加锁其他行。

5. 适当使用 SELECT ... FOR UPDATE

  • 问题描述SELECT ... FOR UPDATE 用于在读取数据时加锁,确保该行在事务完成之前不会被其他事务修改。需要谨慎使用,特别是在高并发环境中,否则可能会导致大量锁竞争。

  • 解决方法

    • 仅对需要修改的数据行使用 FOR UPDATE,避免在查询过程中不必要地锁定大量行。

    • 在读取时避免选择不需要修改的数据行,尽量缩小加锁范围。

6. 间隙锁(Gap Lock)

  • 问题描述:InnoDB 的行级锁有时会引入间隙锁,尤其是在 可重复读(Repeatable Read) 隔离级别下。间隙锁并不会直接锁定数据行,而是锁定 行与行之间的间隙,这可以防止其他事务插入新的行。

  • 原因:间隙锁有时可能导致 幻读(Phantom Reads) 问题,即在同一事务内,数据集中的某些数据行可能由于其他事务插入新行而发生变化。

  • 解决方法

    • 使用 行锁 + 间隙锁 时,务必确保事务逻辑的完整性。

    • 考虑在事务中使用合适的锁来避免幻读,如增加 显式行锁 或调整隔离级别。

7. 避免大事务

  • 问题描述:长时间持有行级锁可能会导致锁竞争,甚至影响整个系统的响应时间。尤其在进行大规模的数据更新时,可能会因为锁长时间占用而影响并发性能。

  • 解决方法

    • 尽量拆分大事务,避免长时间持有行级锁。

    • 对大批量的更新操作,采用分批处理的方式,减少锁的持续时间。

8. 事务隔离级别的选择

  • 问题描述:行级锁的行为也会受到事务隔离级别的影响。在较低隔离级别下(如 读未提交读已提交),事务可能会看到未提交的数据,导致脏读或不一致数据。而在 可重复读 隔离级别下,行级锁和间隙锁的使用可能会导致幻读问题。

  • 解决方法

    • 可重复读 隔离级别下,注意间隙锁的使用,避免幻读。

    • 在需要保证较高一致性的业务场景中,适当提高事务的隔离级别,选择 串行化 隔离级别来解决可能的并发问题。

问题3:共享锁和排他锁呢? 

1. 共享锁(Shared Lock)

1.1 概念

共享锁允许多个事务同时读取(即共享)同一数据行,但不允许修改该行数据。也就是说,持有共享锁的事务可以对数据进行 读取操作,但不能进行 写入操作。多个事务可以同时持有同一数据行的共享锁,前提是它们都只是进行读取操作。

1.2 特点

  • 并发读取:多个事务可以同时对同一数据行加共享锁,并进行读取操作。

  • 禁止修改:即使事务持有共享锁,其他事务不能对该数据行进行修改,直到所有共享锁都被释放。

  • 适用于读取操作:共享锁适用于那些只需要读取数据、不需要修改数据的场景。

1.3 适用场景

  • 只读事务:当事务仅仅需要读取数据,并不需要修改数据时,可以使用共享锁,允许多个事务并发读取。

  • 防止被修改:如果你想要在读取数据的同时,确保其他事务不能修改数据,可以使用共享锁。

1.4 例子

假设有一个 products 表,事务 A 和事务 B 都只需要读取数据:

BEGIN;
SELECT * FROM products WHERE product_id = 1 LOCK IN SHARE MODE; -- 获取共享锁
-- 事务 A 执行查询操作BEGIN;
SELECT * FROM products WHERE product_id = 1 LOCK IN SHARE MODE; -- 获取共享锁
-- 事务 B 执行查询操作

在这种情况下,事务 A 和事务 B 都可以对同一行数据加共享锁,允许它们并发地读取数据,但都不能修改该数据,直到所有共享锁被释放。

2. 排他锁(Exclusive Lock)

2.1 概念

排他锁是最严格的锁类型,允许事务对数据行进行 读取修改,并且排除了其他事务对该数据行的任何访问(包括读取和修改)。也就是说,持有排他锁的事务可以对数据进行任意操作,其他事务在此期间无法对该行进行任何操作。

2.2 特点

  • 禁止其他事务访问:当一个事务持有排他锁时,其他事务不能读取或修改该行数据,直到持有排他锁的事务提交或回滚。

  • 独占操作:排他锁确保了对数据行的完全控制,适用于需要对数据进行修改的事务。

  • 强一致性:由于排他锁会独占数据行的访问,因此它确保了数据的一致性,防止其他事务并发修改。

2.3 适用场景

  • 数据修改事务:当事务需要对数据进行修改时,通常使用排他锁,确保其他事务无法同时修改相同的数据行。

  • 需要强一致性的操作:在需要保证数据一致性的场景下,使用排他锁可以防止其他事务修改或读取数据。

2.4 例子

假设有一个 inventory 表,事务 A 需要修改库存数量:

BEGIN;
SELECT * FROM inventory WHERE product_id = 1 FOR UPDATE; -- 获取排他锁
UPDATE inventory SET stock_quantity = stock_quantity - 1 WHERE product_id = 1;
-- 事务 A 修改库存BEGIN;
SELECT * FROM inventory WHERE product_id = 1 FOR UPDATE; -- 尝试获取排他锁,事务 B 等待
-- 事务 B 无法在事务 A 提交之前修改库存

在这种情况下,事务 A 对 product_id = 1 的数据行获取了排他锁,事务 B 无法对同一行数据进行任何操作,直到事务 A 提交或回滚。

3. 共享锁与排他锁的对比

特性

共享锁(Shared Lock)

排他锁(Exclusive Lock)

允许的操作

只允许读取,不允许修改

允许读取和修改

并发性

允许多个事务同时读取同一行数据

不允许其他事务对该数据行进行任何操作

锁定范围

只锁定当前行,允许其他事务对不同行加锁

锁定当前行,不允许其他事务访问该行

适用场景

适用于只读事务或需要防止修改数据的场景

适用于需要对数据进行修改的事务

多个事务操作

多个事务可以同时持有同一数据行的共享锁

只能有一个事务持有该数据行的排他锁

允许的其他事务操作

其他事务可以加共享锁进行读取,但不能修改数据

其他事务无法对该数据行进行任何操作(包括读取和修改)

是否阻塞

只有当事务尝试修改数据时才会阻塞

事务提交或回滚之前,任何对该数据行的访问都会被阻塞

4. 如何在 SQL 中使用共享锁和排他锁

  • 共享锁:可以通过 LOCK IN SHARE MODE 来显式地请求共享锁。

    SELECT * FROM products WHERE product_id = 1 LOCK IN SHARE MODE;

  • 排他锁:可以通过 FOR UPDATE 来显式地请求排他锁。

    SELECT * FROM products WHERE product_id = 1 FOR UPDATE;

    该语句获取排他锁,阻止其他事务对该行进行任何修改或读取操作,直到当前事务提交或回滚。

问题4:意向锁有什么作用?

意向锁(Intention Lock) 主要用于帮助 InnoDB 存储引擎 快速判断是否可以对某个表使用表锁,从而避免在加锁时进行不必要的检查,提升锁管理的效率。

1. 作用概述

意向锁是一种 表级锁,它用于表明一个事务打算对 表中的某些行 加锁(共享锁或排他锁)。通过引入意向锁,数据库系统能够 快速判断事务是否在某个表上加锁,避免在加行级锁时进行全表扫描。这样可以在行级锁和表级锁之间实现更高效的协调,提升系统的并发性能。

2. 意向锁的两种类型

意向锁有两种类型,它们是互相兼容的:

2.1 意向共享锁(IS)

  • 用途:表示事务打算对某些行加 共享锁(即读取操作)。

  • 作用:其他事务可以在同一表上加 共享锁(即允许并发读取),但 不能加排他锁(即不能修改数据)。

2.2 意向排他锁(IX)

  • 用途:表示事务打算对某些行加 排他锁(即修改操作)。

  • 作用:其他事务可以在同一表上加 共享锁,但 不能加排他锁(即不能修改数据)。

3. 意向锁的作用与工作机制

3.1 表锁和行锁的协调

意向锁的最重要作用之一是帮助系统在获取 行级锁 时,避免冲突。它提供了一个 预先声明的信号,告诉系统当前事务是否有意图在某个表的行上加锁。

  • 例子:当事务 A 打算在 products 表的某一行上加排他锁(修改数据)时,事务 A 会首先获得 意向排他锁(IX),然后它会对特定的行加排他锁。如果其他事务想要对同一表中的数据行加共享锁,系统会根据意向锁判断是否允许。

3.2 加速表级锁的判断

意向锁避免了在获取行级锁时对整个表进行扫描,从而加速了加锁操作。如果事务 A 在某张表上已经获得了意向锁(IS 或 IX),其他事务如果想对该表进行表级锁操作时,系统可以快速判断当前表的锁状态,而不需要遍历所有行的锁状态。

3.3 兼容性

意向锁之间是 互相兼容的,即:

  • 意向共享锁(IS)意向共享锁(IS) 可以共存。

  • 意向排他锁(IX)意向排他锁(IX) 可以共存。

  • 意向共享锁(IS)意向排他锁(IX) 也可以共存。

但是,意向共享锁(IS)意向排他锁(IX) 与其他行级锁之间的冲突是不可避免的:

  • ISX(排他锁) 冲突。

  • IXX(排他锁) 冲突。

4. 使用场景

意向锁的设计目的是减少系统对 表级锁行级锁 之间的冲突和开销,尤其在事务需要加锁大量数据行时,意向锁 使得数据库系统能快速判断是否可以加锁。

4.1 表锁与行锁的结合

在某些事务中,可能需要对一整张表加锁(表锁),但大部分操作只会修改表中的部分数据行。通过意向锁,系统能够识别该事务打算锁定的行,提前避免与其他事务的冲突。

5. 总结

  • 意向锁 通过提供表级的锁定信息,使得 行级锁表级锁 之间能更高效地协调和管理。

  • 它的 兼容性 使得多个事务可以在不冲突的情况下,对同一张表进行行级锁操作。

  • 通过意向锁,InnoDB 能够快速判断是否可以对表进行表锁操作,提高并发性能并减少锁争用。

问题5: InnoDB 有哪几类行锁?

InnoDB 存储引擎提供了三种主要的 行锁 类型:记录锁(Record Lock)、间隙锁(Gap Lock)、和 临键锁(Next-Key Lock)。这些锁类型是为了提高并发性能,同时避免数据不一致和 幻读(Phantom Read)等问题。它们通过不同的方式锁定数据,确保事务的隔离性和一致性。

1. 记录锁(Record Lock)

定义

记录锁是对 单个数据行 加锁,锁定的是数据库中的具体记录。这种锁类型通常是在 SELECT FOR UPDATESELECT ... LOCK IN SHARE MODE 查询时使用的。

作用

  • 锁定单行记录,确保其他事务不能对该行进行修改或删除。

  • 在行级锁的基础上,是最常见的行锁类型。

应用场景

  • 当事务需要修改或读取某个特定数据行时使用记录锁,确保该行数据在事务期间不被其他事务修改。

例子

SELECT * FROM products WHERE product_id = 1 FOR UPDATE;

这会对 product_id = 1 加一个 记录锁,使得其他事务不能修改该行数据,直到当前事务提交或回滚。


2. 间隙锁(Gap Lock)

定义

间隙锁是指锁住某个 范围(间隙),但是不包括具体的 记录。它锁定的是数据行 之间的空隙,即两个数据行之间的范围,但不包括这两行数据本身。

作用

  • 防止幻读:通过锁定数据之间的间隙,间隙锁防止其他事务插入符合某些条件的新数据行,避免了 幻读 问题。

  • 在使用 范围查询插入操作 时,间隙锁能够确保在某个区间内没有其他事务插入数据,确保事务的一致性。

应用场景

  • 在需要防止新记录被插入到某一范围时,间隙锁是必不可少的。例如,在多用户同时查询某个范围的数据时,间隙锁可以防止其他事务插入新记录,从而保证数据的隔离性。

例子

假设你有以下 products 表,你希望读取 price 在 100 到 200 之间的所有记录,并且不希望其他事务插入新的记录:

SELECT * FROM products WHERE price BETWEEN 100 AND 200 FOR UPDATE;

在执行这条 SQL 语句时,InnoDB 会在 100200 之间加上 间隙锁,防止其他事务插入 price 在该区间的新记录,避免幻读。


3. 临键锁(Next-Key Lock)

定义

临键锁是 记录锁间隙锁 的组合,它既锁定了 某个数据行,也锁定了 数据行前后的间隙。因此,临键锁能够防止数据被修改、删除或插入,并有效防止幻读。

作用

  • 临键锁是 InnoDB 最常用的锁类型,因为它不仅锁定了目标数据行,还锁定了与该行相邻的间隙。

  • 它能够防止 幻读,确保事务隔离性。

应用场景

  • 在范围查询中,临键锁可以防止其他事务在当前查询的范围内插入新的记录,确保查询结果的一致性。

例子

假设你想查询所有 price 在 100 到 200 之间的记录,并锁定这些行以避免修改:

SELECT * FROM products WHERE price BETWEEN 100 AND 200 FOR UPDATE;

在这种情况下,InnoDB 会对所有 price 在 100 和 200 之间的记录加 记录锁,并同时对 100200 之间的间隙加 间隙锁。这就形成了 临键锁,确保查询范围内的数据不被修改或插入。


总结:三类行锁的比较

锁类型

锁定范围

主要用途

防止问题

记录锁

锁定单个数据行

锁定具体的记录,防止修改或删除

防止数据修改、删除

间隙锁

锁定数据行之间的间隙

防止其他事务在范围内插入新记录

防止幻读

临键锁

锁定数据行和数据行之间的间隙

结合记录锁和间隙锁,防止数据修改、插入

防止幻读、修改、插入

  • 记录锁 适用于对某一特定数据行的操作。

  • 间隙锁 适用于防止幻读,锁定数据行之间的范围,确保没有新的数据插入。

  • 临键锁 结合了记录锁和间隙锁,防止对数据行的修改、删除和插入,确保数据的一致性和事务的隔离性。

这些锁类型共同作用,确保了 InnoDB 在高并发情况下的数据一致性、隔离性和高效的并发控制。

MYSQL索引 

问题1:何为索引?有什么作用?

索引(Index)是数据库中的一种 数据结构,它用于提高数据库查询的效率。索引类似于书籍中的目录,帮助快速定位数据。在数据库中,索引是根据某个或某些列的值,建立的一种 快速查找 数据的机制。

索引的作用:

  1. 提高查询速度
    索引能够显著 加速数据的检索。在没有索引的情况下,数据库需要扫描整个表来查找满足条件的数据(全表扫描),这在数据量大的时候非常低效。而通过索引,数据库能够更快速地定位到数据的位置,从而 减少查询的时间

  2. 减少数据库的 I/O 操作
    使用索引能够减少访问硬盘的次数,因为索引通常存储在内存中,查询数据时可以通过索引直接访问,而无需每次都去读取整个表的数据。

  3. 提高排序和分组操作的效率
    如果查询中涉及到排序(ORDER BY)或者分组(GROUP BY)操作,索引也能有效地加速这些操作。索引可以避免数据库在进行这些操作时对所有数据进行排序或分组。

  4. 加速数据的唯一性检查
    索引可以用于确保某些字段的值是唯一的。对于那些需要 唯一约束 的字段(如主键和唯一索引),索引使得插入和更新操作更加高效,同时也能确保数据的完整性。

  5. 支持范围查询
    索引特别适合于范围查询,比如查找某个范围内的数据(例如 BETWEEN>< 等)。使用索引能够大大提高这类查询的性能。

  6. 提高连接操作的效率
    当查询需要涉及多表连接(JOIN)时,索引能够加速连接操作的效率,尤其是在连接条件中使用了索引字段时,能够避免全表扫描,从而提高查询效率。

问题2: 索引的优缺点

优点缺点
提高查询速度占用额外的存储空间
加速排序和分组操作影响写操作(插入、删除、更新)的性能
加速连接操作过多的索引增加数据库的维护成本
保证数据的唯一性(主键和唯一索引)不当的索引设计可能会导致性能下降
支持范围查询增加数据库的复杂性
优化删除操作

问题3: 索引的底层数据结构

在数据库中,索引是通过特定的底层数据结构来实现的,这些数据结构决定了索引的存储方式和访问效率。常见的索引底层数据结构主要有以下几种:

1. B-Tree(平衡树)

概述

  • B-Tree 是最常见的索引底层数据结构,特别是在 关系型数据库 中,广泛应用于 聚集索引非聚集索引

  • B-Tree 是一种 自平衡的树形数据结构,具有 排序高效查找 的特性。它允许在对数时间内(O(log N))进行查找、插入、删除操作。

特点

  • B-Tree 是一棵 多路平衡查找树,每个节点可以有多个子节点。与二叉树相比,B-Tree 可以大大减少树的高度,从而提高查找效率。

  • 在 B-Tree 中,所有数据都存储在 叶子节点 中,非叶子节点仅包含键值,用于引导查询的路径。

  • B-Tree 保持 平衡性,即每一层的节点数量都尽量相等,从而确保查找时需要的时间最小化。

应用

  • MySQLInnoDB 存储引擎使用 B-Tree 来实现 聚集索引非聚集索引(对于大部分单列索引)。

优点

  • 查找、插入、删除等操作时间复杂度为 O(log N)。

  • 支持范围查询,如 BETWEEN>, < 等操作,能够按顺序遍历数据。

缺点

  • B-Tree 存储结构相对复杂,插入和删除操作需要维持平衡,可能会引发更多的磁盘写操作。


2. B+ Tree(平衡B树)

概述

  • B+ Tree 是对 B-Tree 的一种改进,它保留了 B-Tree 的平衡特性,同时改进了查询效率。

  • B+ Tree 在 数据库索引 中应用得非常广泛,特别是 MySQL 的 InnoDB 存储引擎。与 B-Tree 不同,B+ Tree 的 非叶子节点只存储键值,而所有数据存储在 叶子节点 中。

特点

  • 叶子节点 存储所有的实际数据(或指向数据的指针),而非叶子节点则仅用于导航。

  • 叶子节点通过 链表 连接,支持顺序扫描。

  • 内部节点只存储索引值,不直接存储实际数据,这使得树的结构更加紧凑。

应用

  • InnoDB 存储引擎使用 B+ Tree 来实现 聚集索引非聚集索引

优点

  • 提供更高效的 范围查询,因为所有数据都在叶子节点,且叶子节点之间通过链表连接,支持顺序遍历。

  • 由于内部节点只存储索引,树的高度较低,查找更高效。

  • 叶子节点之间链表结构的存在,使得对于大量数据的批量查询更加高效。

缺点

  • 和 B-Tree 一样,插入、删除时需要维护树的平衡,可能导致较多的磁盘 I/O 操作。


3. 哈希索引(Hash Index)

概述

  • 哈希索引是基于 哈希表 实现的索引结构,通常用于 等值查询(如 =)的情况。

  • 在哈希索引中,查询时通过 哈希函数 计算键值的哈希码,然后直接找到对应的位置。

特点

  • 哈希索引是 唯一 的,它不支持范围查询。

  • 查找操作的时间复杂度为 O(1),即常数时间。

  • 哈希表中的键值和数据位置通过哈希函数确定,查找速度非常快。

应用

  • Memory 存储引擎使用哈希索引作为其默认索引类型。

  • 在 MySQL 中,哈希索引通常用于一些内存数据库(如 MEMORY 存储引擎)。

优点

  • 对于 等值查询,查找速度极快,时间复杂度为 O(1)。

  • 哈希索引是基于哈希表的,因此在内存中操作时速度非常快。

缺点

  • 不支持 范围查询(如 BETWEEN>< 等),无法对数据进行排序。

  • 在哈希冲突较多时,性能可能下降。


4. 全文索引(Full-Text Index)

概述

  • 全文索引 是一种专门用于 文本搜索 的索引,主要用于存储和搜索大量的文本数据,如文章、产品描述等。

  • 全文索引不基于树形结构,而是使用 倒排索引 来实现。

特点

  • 通过将文本拆分成 单词短语,并为每个词创建一个索引,来支持快速的 全文搜索

  • 支持复杂的 文本匹配,如包含词、前缀匹配、模糊查询等。

应用

  • MySQL 使用 MyISAM 存储引擎提供全文索引,InnoDB 存储引擎在 MySQL 5.6 及以上版本也支持全文索引。

优点

  • 特别适用于对 文本 数据进行搜索,支持更复杂的文本匹配查询。

  • 性能高效,特别是在海量数据的全文搜索中。

缺点

  • 只适用于 全文搜索,不能用于常规的范围查询或等值查询。

  • 需要更多的内存来存储和处理倒排索引。


5. 位图索引(Bitmap Index)

概述

  • 位图索引 使用位图(bitmaps)来表示每个可能的值。这种索引特别适合于那些 数据列取值不多 的情况。

  • 它通过为每个不同的列值创建一个位图,在查询时可以通过位图快速检索。

特点

  • 位图索引对于 低基数(即列中不同值较少)的字段效果最好。

  • 使用位运算进行查询,通常能够快速判断满足条件的记录。

应用

  • 常用于多维数据分析(OLAP)系统,特别是列的数据取值较少的情况。

优点

  • 查询速度非常快,特别适合进行 位运算 的查询(如多个条件组合)。

  • 低基数列 中非常高效。

缺点

  • 不适合高基数的列(如用户ID、订单号等),因为位图可能会非常大,导致存储空间浪费。

  • 对于频繁更新的字段,位图索引不适用,因为每次更新都会导致位图的重建。


总结:

索引类型

底层数据结构

优点

缺点

B-Tree/B+ Tree

多路平衡树

高效查找、支持范围查询、存储紧凑

插入、删除时需要维持平衡,I/O 操作较多

哈希索引

哈希表

对等值查询极快,查找复杂度为 O(1)

不支持范围查询、排序等操作

全文索引

倒排索引

高效支持文本搜索

只支持文本搜索,不支持范围查询

位图索引

位图

对低基数列查询非常高效

不适合高基数列,存储空间需求大

不同类型的索引适合不同的查询需求,因此,在数据库设计时选择合适的索引类型至关重要。

问题4:MySQL 的索引结构为什么使用 B+树? 

MySQL 中的索引使用 B+ 树(一种平衡树)的原因,主要与 查询效率存储结构维护效率 有关。B+ 树是一种优化的 B 树,它具有以下特性,使其成为数据库索引的理想选择:

1. 支持高效的范围查询

B+ 树能够非常高效地进行 范围查询,这是由于其 叶子节点按顺序链表连接 的特性。B+ 树的叶子节点不仅存储数据,还通过 链表连接,使得通过索引进行顺序扫描(如 BETWEEN>, < 等范围查询)变得非常高效。

  • 示例:假设我们查询 age > 30 的记录,B+ 树可以直接跳转到符合条件的叶子节点,并顺序扫描后续的记录,避免了全表扫描。

2. 所有数据都存储在叶子节点

B+ 树 中,所有实际数据(或指向数据的指针)都存储在 叶子节点,而非叶子节点只存储索引值和指针,用于引导查询的路径。这样,非叶子节点的大小相对较小,树的高度更低,查询效率更高。

  • 示例:通过这种结构,B+ 树能够保持较低的树高,从而减少磁盘 I/O 操作次数,提高查询速度。

3. 良好的磁盘 I/O 性能

数据库中的索引常常存储在磁盘上,而 B+ 树特别适合 磁盘存储。由于 B+ 树是多路平衡树,它的每个节点可以存储多个元素,因此可以减少树的高度,降低 磁盘访问次数

  • 示例:对于大型数据表,磁盘访问延迟通常是性能瓶颈之一。B+ 树通过增加每个节点的容量(即多路性),减少了树的深度,从而降低了访问磁盘的次数。

4. 树的平衡性

B+ 树是一棵 平衡树,这意味着所有叶子节点都在同一层级,保证了 查找操作的时间复杂度 为 O(log N)。树的平衡性使得每次查找、插入、删除操作都能保持在较优的性能水平,不会出现严重的性能波动。

  • 示例:对于任何插入、删除或查找操作,B+ 树的树高变化非常小,确保查询效率不会受到影响。

5. 高效的插入与删除操作

由于 B+ 树的 自平衡性,每次插入或删除操作都能够保持树的平衡。相比于一些其他数据结构,B+ 树在插入或删除时能够较为高效地重新组织数据,减少不必要的重复操作。

  • 示例:插入新数据时,B+ 树能够通过合并或分裂节点的方式,确保树的高度不变,从而保证查询的高效性。

6. 支持范围查询的顺序遍历

B+ 树的 叶子节点通过链表连接,使得从一个叶子节点到另一个叶子节点的顺序访问变得非常高效。这对于大量的范围查询和排序操作非常有用。

  • 示例:如果查询 age > 20age < 50 的记录,B+ 树能够直接找到符合条件的叶子节点,并顺序遍历这些叶子节点,进行高效的查询。

7. 实现简单且效率高

相比于其他树形结构,B+ 树的实现相对简单,而且在 大规模数据高频查询 下,能够提供一致的查询性能,因此被广泛应用于数据库的索引实现中。


总结:

MySQL 使用 B+ 树作为索引结构,主要是因为 B+ 树具备以下几个优点:

  • 高效支持范围查询,适合处理 BETWEEN>< 等条件。

  • 平衡性,保证查询性能稳定,查找时间复杂度为 O(log N)。

  • 存储和查找效率高,减少磁盘 I/O 操作。

  • 支持顺序遍历,非常适合需要按顺序访问数据的场景。

这些特性使得 B+ 树成为数据库索引中最常用的底层数据结构之一,能够大幅提高查询性能,特别是在大数据量的环境下。

问题5:主键索引和二级索引 

特性主键索引二级索引
唯一性必须唯一且不能为空可以是唯一索引,也可以是非唯一索引
索引类型聚集索引(Clustered Index)非聚集索引(Secondary Index)
存储结构数据存储在主键索引的叶子节点中叶子节点存储主键值,数据存储在主键索引中
表中数量每个表只能有一个主键索引每个表可以有多个二级索引
查询效率查找主键索引的记录时效率非常高查找二级索引需要额外通过主键索引查找实际数据
数据存储顺序数据表的记录按照主键顺序存储数据表的记录不按索引顺序存储
适用场景用于唯一标识记录,查询主键时非常高效用于优化非主键列的查询

问题6: 聚集索引与非聚集索引

聚集索引非聚集索引是数据库索引的两种主要类型,它们在数据存储、索引结构和查询效率等方面有着显著的区别。

1. 聚集索引(Clustered Index)

定义

  • 聚集索引是指 数据存储顺序索引顺序 一致的索引。也就是说,数据表中的记录会按照聚集索引的顺序来存储,因此聚集索引是 唯一的,每个表只能有一个聚集索引。

  • 聚集索引的 叶子节点 存储的是 实际数据,而非索引值。

特点

  • 存储顺序一致:数据的存储顺序与聚集索引的顺序相同。也就是说,数据会按照聚集索引的顺序来存储。

  • 唯一性:一个表只能有一个聚集索引。

  • 主键索引:通常,表的主键列使用的就是聚集索引,因此主键的存储顺序决定了表数据的存储顺序。

  • 查询效率:由于数据存储与索引顺序一致,基于聚集索引的查询非常高效,尤其是对主键的查询。

示意图

假设我们有一个表 users,使用 id 作为主键:

+------------+------------+
| id (PK)    | name       |
+------------+------------+
| 1          | Alice      |
| 2          | Bob        |
| 3          | Charlie    |
+------------+------------+

在此示例中,表数据将按 id 排序存储,因为 id 是主键索引,即聚集索引。

优缺点

  • 优点

    • 查询主键时非常高效,查找速度快。

    • 如果查询涉及到范围查询(例如:BETWEEN> <),由于数据顺序与索引顺序一致,可以快速获取结果。

  • 缺点

    • 一个表只能有一个聚集索引,若需要使用其他列进行索引查询,则需要二级索引。

    • 如果对聚集索引列进行修改,可能需要移动整行数据,造成额外的开销。


2. 非聚集索引(Non-clustered Index)

定义

  • 非聚集索引是指 索引的存储顺序与数据表的存储顺序无关。非聚集索引的 叶子节点 存储的是 指向数据行的指针(即主键值),而不是实际的数据。

  • 一个表可以有多个非聚集索引。

特点

  • 索引与数据分离:非聚集索引的结构与数据表的结构是分开的。非聚集索引包含了索引列和指向数据表中实际数据的指针。

  • 多个索引:一个表可以有多个非聚集索引,每个索引可以根据不同的查询需求创建。

  • 查找过程:查询时,先通过非聚集索引找到索引值,再通过索引值(通常是主键值)访问数据表中的实际记录。

示意图

假设我们为 users 表创建了一个基于 name 字段的非聚集索引:

+------------+------------+--------------------------+
| id (PK)    | name       | Non-clustered Index (name)|
+------------+------------+--------------------------+
| 1          | Alice      | (id -> 1)                |
| 2          | Bob        | (id -> 2)                |
| 3          | Charlie    | (id -> 3)                |
+------------+------------+--------------------------+

在此示例中,name 列有一个非聚集索引,非聚集索引的叶子节点存储的是主键 id 的值,查询时需要通过 id 来查找实际数据。

优缺点

  • 优点

    • 一个表可以有多个非聚集索引,因此适用于多种不同查询的场景。

    • 对于频繁查询某些列的场景(如:nameage 等),非聚集索引能显著提高查询效率。

  • 缺点

    • 查询时需要多次查找,首先通过非聚集索引找到主键值,再根据主键值查找实际数据,性能相对聚集索引较低。

    • 插入、删除或更新数据时,非聚集索引可能需要进行额外的维护,导致额外的性能开销。


3. 聚集索引与非聚集索引的区别

特性

聚集索引

非聚集索引

存储结构

数据存储在索引的叶子节点中,数据表的记录按索引顺序存储

数据存储在数据表中,叶子节点存储指向数据行的指针(如主键值)

索引与数据的关系

数据表记录的顺序与索引顺序一致

索引与数据表记录的顺序无关

表中的数量

每个表只能有一个聚集索引

每个表可以有多个非聚集索引

查询效率

查询主键时非常高效

查询非主键列时需要通过索引查找主键值,再查找数据表中的数据

适用场景

适用于需要频繁查询主键数据的场景

适用于根据非主键列进行查询的场景

数据存储顺序

按照聚集索引的顺序存储数据

数据存储顺序与索引顺序无关

问题7:覆盖索引 

覆盖索引(Covering Index)是指在查询时,所需要的所有列都可以通过 索引 来直接获取,而不需要回表查找数据表中的实际数据。

1. 定义

  • 覆盖索引指的是,查询的所有列都已经被索引覆盖,也就是说,索引本身包含了查询所需的所有数据列,因此可以通过索引直接返回查询结果,而不需要访问实际的数据表。

  • 这样可以避免回表操作,从而提高查询效率。

2. 覆盖索引的特征

  • 查询列包含在索引中:查询中需要的列完全包含在索引的叶子节点中。

  • 不需要回表:通过索引本身即可满足查询,无需访问数据表的实际记录。

  • 提升查询效率:由于直接使用索引进行查询,避免了数据表的访问,从而减少了 I/O 操作,提高了查询速度。

3. 覆盖索引的使用场景

  • 当查询仅涉及索引中的列时,使用覆盖索引可以大大提高查询性能。

  • 适合 SELECT 查询以及需要检索多个列的情况,特别是当这些列已经被索引时。

问题8:联合索引 

联合索引(Composite Index)是指在一个索引中包含 多个列 的索引。与单列索引(即只包含一个列的索引)不同,联合索引可以覆盖多个列,适用于多条件查询。联合索引的创建可以提高复杂查询的效率,特别是当查询涉及多个列时。

1. 定义

  • 联合索引是由 多个列 组成的索引。

  • 在查询时,如果查询条件中涉及到多个列,联合索引可以帮助数据库快速定位到相关记录,避免全表扫描。

  • 联合索引按照 列的顺序 进行存储和检索,因此列的顺序非常重要。

问题9:最左前缀匹配原则 

最左前缀匹配原则是指,在使用 联合索引 时,索引的使用依赖于查询条件的列顺序,只有 查询条件的前缀列(从左到右的顺序)能够匹配到索引的列,数据库才会有效利用该联合索引进行优化。

简单来说,联合索引能够高效工作的前提是查询条件中的列,必须从左到右依次匹配索引列的顺序。如果查询条件不符合这个顺序,那么联合索引的效率会受到影响。

1. 最左前缀匹配的基本概念

假设有一个联合索引 idx_name_age_salary,它是由三列:nameagesalary 组成的复合索引。

  • 最左前缀匹配原则要求,查询条件中的列必须按照索引定义的顺序(从左到右)来匹配。

  • 只有查询条件中涉及索引列的 最左边部分,索引才能被充分利用。

2. 最左前缀匹配原则的工作原理

  • 如果查询条件只涉及联合索引中的 最左边的列,数据库会使用该列的索引。

  • 如果查询条件中包含 最左边的列次左边的列,数据库会继续利用联合索引,但条件需要按照索引的顺序进行匹配。

  • 如果查询条件中的列不符合索引的最左部分顺序,则联合索引将无法充分利用,可能需要回表查询或者使用其他索引。

3. 最左前缀匹配原则示例

假设有如下表结构,并创建了联合索引:

CREATE TABLE employees (id INT PRIMARY KEY,name VARCHAR(100),age INT,salary DECIMAL(10, 2)
);CREATE INDEX idx_name_age_salary ON employees(name, age, salary);

示例 1:最左前缀匹配

SELECT * FROM employees WHERE name = 'Alice' AND age = 30;
  • 由于查询条件中的 nameage 正好与索引 idx_name_age_salary 的前两个列匹配,数据库可以有效地使用该联合索引,避免全表扫描。

示例 2:部分最左前缀匹配

查询 name

SELECT * FROM employees WHERE name = 'Alice';
  • 由于查询条件仅包含 name,并且 name 是联合索引 idx_name_age_salary 的第一个列,数据库可以利用联合索引的第一列来优化查询。

  • 此时,索引只会利用 name,但无法使用后续的 agesalary 列。

示例 3:不符合最左前缀匹配

查询 agesalary

SELECT * FROM employees WHERE age = 30 AND salary > 50000;
  • 由于查询条件中 没有包含 name,联合索引的 最左列 name 不被使用,数据库将无法利用 idx_name_age_salary 索引进行查询优化。

  • 如果没有其他合适的索引,可能需要进行全表扫描或者回表查询。

示例 4:符合最左前缀部分匹配

查询 namesalary:

SELECT * FROM employees WHERE name = 'Alice' AND salary > 50000;
  • 虽然查询条件中的 namesalary 包含在索引 idx_name_age_salary 中,但由于查询条件 没有涉及 age,所以 无法完全利用索引,因为索引要求是从左到右匹配。

  • 在这种情况下,数据库可以只利用 name 列来进行部分索引查找,然后可能需要回表查找 salary,所以效率会有所降低。

4. 最左前缀匹配原则的影响

  • 查询顺序影响索引使用:联合索引只有在查询条件的列按最左顺序匹配时,才能有效使用。否则,可能会导致数据库回表查询或选择其他不那么高效的索引。

  • 索引的设计与查询优化:为了最大化索引的利用,应该设计索引时,尽量将查询中最常用的列放在联合索引的前面。

  • 范围查询的影响:当查询条件中包含范围查询(如 >, <, BETWEEN)时,如果范围查询出现在索引的列的后面部分,索引的前缀仍然会起作用,但索引后面的部分可能无法完全利用。

问题10:创建索引的注意事项有哪些? 

创建索引时,应选择合适的字段进行索引,优先考虑高选择性、经常出现在查询条件中的列,如 WHEREJOINORDER BY 子句中的字段。尽量使用 联合索引 替代多个单列索引,因为联合索引能够覆盖多个查询条件,从而提高查询效率。避免为低选择性列(如性别、状态等)创建索引,避免创建过多的索引,因为每个索引都会增加存储和维护成本。确保索引列的顺序符合查询条件的最左前缀匹配原则,以提高索引的有效性。同时,避免为频繁更新的列创建索引,以减少索引维护的开销。

MYSQL日志 

问题1: MySQL 中常见的日志有哪些?

在 MySQL 中,常见的日志主要有以下几种,它们分别用于不同的目的,帮助开发者和数据库管理员进行问题诊断、性能优化以及数据恢复等任务:

1. 错误日志(Error Log)

  • 作用:记录 MySQL 服务的启动、停止、崩溃以及相关错误信息。它对诊断数据库的问题、查看服务是否正常运行以及查找系统崩溃的原因非常有用。

  • 内容:包括启动和关闭 MySQL 的信息、错误信息、警告信息等。

  • 位置:可以通过 MySQL 配置文件中的 log_error 配置项指定错误日志的位置。

  • 例子

    2025-02-04T12:34:56.789123Z 0 [Note] mysqld: ready for connections.
    2025-02-04T12:34:58.000456Z 0 [ERROR] InnoDB: Cannot create a file, because the file already exists.
    

2. 查询日志(General Query Log)

  • 作用:记录所有的 SQL 查询请求(包括连接信息),无论是否成功执行。它对于审计、调试以及性能分析非常有用。

  • 内容:记录客户端发出的所有 SQL 查询,包括成功的和失败的查询。

  • 位置:可以通过 general_loggeneral_log_file 配置项来启用和指定日志文件路径。

  • 注意:启用该日志会显著增加 I/O 开销,因此在生产环境中通常不推荐启用。

  • 例子

    2025-02-04T12:35:00.123456Z 12345 Connect root@localhost on 
    2025-02-04T12:35:01.234567Z 12345 Query SELECT * FROM employees WHERE name = 'Alice';
    

3. 慢查询日志(Slow Query Log)

  • 作用:记录执行时间超过指定阈值的 SQL 查询(如执行时间过长的查询)。它对于识别性能瓶颈和优化查询非常有用。

  • 内容:记录执行时间超过 long_query_time 设置阈值的查询,包括查询语句和执行时间。

  • 位置:可以通过 slow_query_logslow_query_log_file 配置项来启用并指定日志文件路径。

  • 例子

    # Time: 2025-02-04T12:35:05.678901Z
    # Query_time: 3.234567  Lock_time: 0.000123  Rows_sent: 1000  Rows_examined: 50000
    SELECT * FROM employees WHERE department = 'Sales' AND salary > 50000;
    

4. 二进制日志(Binary Log)

  • 作用:记录所有对数据库进行修改的 SQL 语句,主要用于数据恢复、主从复制和数据同步。它包含了对数据库的所有变更操作(如 INSERTUPDATEDELETE 等)。

  • 内容:记录了数据库中的所有变更操作,包括修改表数据的语句,但不记录查询语句。

  • 位置:可以通过 log_bin 配置项启用二进制日志,并且可以通过 log_bin_basename 配置项指定日志文件的路径。

  • 例子

    # at 1234
    # User 'root'@'localhost' created database `test_db`
    

5. 中继日志(Relay Log)

  • 作用:用于 MySQL 主从复制架构中,从库接收并执行主库的二进制日志。它是从库的核心日志文件,记录了从主库接收到的所有 SQL 更新操作。

  • 内容:存储从主库传递过来的二进制日志内容。

  • 位置:可以通过 relay_log 配置项启用并指定日志文件路径。

  • 例子

    # at 1234
    # User 'root'@'localhost' executed query 'UPDATE employees SET salary = 70000 WHERE id = 101'
    

6. 事务日志(InnoDB Transaction Log)

  • 作用:InnoDB 存储引擎使用的日志,记录了所有对数据库进行事务性修改的操作,用于保证事务的 ACID 特性和支持崩溃恢复。

  • 内容:记录了对数据库的所有事务性修改,包括 INSERTUPDATEDELETE 等操作。

  • 位置:InnoDB 存储引擎自动生成,日志文件名为 ib_logfile0ib_logfile1 等。

  • 例子:事务日志并不直接可见,而是通过 InnoDB 的崩溃恢复和回滚操作来发挥作用。

7. 复制日志(Replication Log)

  • 作用:用于主从复制架构中的日志,用于记录复制进程中的数据传输和同步信息。

  • 内容:记录主库的所有更改操作和同步信息,以确保从库能够追踪主库的数据变更。

  • 位置:MySQL 复制使用二进制日志进行同步。

8. 性能模式日志(Performance Schema)

  • 作用:用于 MySQL 内部性能的跟踪、监控和调试,提供 MySQL 服务性能的详细信息,便于进行性能分析和优化。

  • 内容:记录了查询执行的各个方面,包括延迟、锁、内存使用等。

  • 位置:可以通过 performance_schema 配置项启用。

总结

MySQL 提供了多种日志来帮助数据库管理员进行监控、调试、故障排查、性能优化等工作。常见的日志有:

  1. 错误日志:记录启动、停止和错误信息。

  2. 查询日志:记录所有 SQL 查询语句。

  3. 慢查询日志:记录执行时间过长的查询。

  4. 二进制日志:记录所有修改数据库的操作,主要用于主从复制和数据恢复。

  5. 中继日志:主从复制架构中的日志,用于从库执行主库的更新。

  6. 事务日志:InnoDB 引擎的事务日志,支持崩溃恢复。

  7. 性能模式日志:记录 MySQL 性能信息,帮助优化。

每种日志都在不同的场景下发挥着重要的作用,帮助开发者和运维人员有效地管理和优化 MySQL 数据库。

问题2:慢查询日志有什么用?

慢查询日志的主要作用是帮助开发者和数据库管理员定位和解决 SQL 执行时间过长的问题。具体来说,慢查询日志具有以下几个关键用途:

  1. 识别性能瓶颈
    慢查询日志记录了所有执行时间超过 long_query_time(默认是 10 秒)的查询。通过分析这些日志,能够识别出哪些查询执行时间过长,从而定位系统性能的瓶颈。

  2. 优化慢查询
    对于慢查询,可以采取优化措施,如:

    • 增加索引:针对执行较慢的查询,检查是否缺少索引,必要时添加合适的索引。

    • 改进查询结构:优化查询语句,避免不必要的全表扫描、过多的嵌套查询等。

    • 重新设计数据库架构:例如通过分区、分表等方法,减小数据量,提高查询效率。

  3. 性能监控
    慢查询日志可以用作性能监控工具,通过定期查看和分析日志,了解哪些查询占用了过多的时间和资源。通过这种方式,及时发现并优化问题查询,防止性能下降。

  4. 帮助解决 SQL 执行时间过长的问题
    在出现数据库性能问题时,慢查询日志是排查问题的重要依据。通过日志,能够迅速找出执行时间过长的查询,作为解决方案的起点。

问题3:binlog 主要记录了什么?有什么用? 

Binlog(Binary Log)的作用和记录内容

1. 记录内容

Binlog 主要记录了 MySQL 数据库中所有的 数据修改操作,即数据库的写操作,通常包括以下几种类型:

  • INSERT:插入数据。

  • UPDATE:更新数据。

  • DELETE:删除数据。

它只记录对数据进行修改的 SQL 语句,而 SELECT 等查询操作不会被记录在内。记录的内容包括:

  • 执行的 SQL 语句。

  • 执行 SQL 语句的时间戳。

  • 数据修改的表和行等详细信息。

2. Binlog 的用途

Binlog 在 MySQL 中的作用主要有以下几方面:

数据恢复

  • 在发生数据库崩溃或其他意外情况时,Binlog 可以作为重要的恢复依据。通过恢复到某个特定时间点或日志位置,能够恢复丢失的数据。

  • 使用 Binlog 可以实现 "point-in-time" 恢复,即恢复到数据库崩溃前的某个特定时间点,避免数据丢失。

主从复制

  • Binlog 是 MySQL 主从复制的核心。主库将数据修改的操作记录到 Binlog 中,从库读取主库的 Binlog 并应用这些操作,保持与主库的数据同步。通过这种机制,MySQL 可以实现主从复制、负载均衡等功能。

审计和数据监控

  • Binlog 可以用来审计数据库的所有修改操作,包括何时、何人、执行了什么样的 SQL 语句。对于数据安全性、合规性检查和问题排查等具有重要意义。

分析与调试

  • 通过查看 Binlog,可以分析系统的 SQL 执行情况,排查性能瓶颈或异常操作,帮助优化数据库性能和设计。

数据同步和数据传输

  • 除了主从复制,Binlog 也可以用于其他类型的数据同步,如跨数据中心的复制、数据迁移等场景。通过解析 Binlog,可以实现高效的数据同步和数据传输。

Binlog 的格式

Binlog 支持不同的格式,通常有以下几种:

  • Statement-based Logging (SBL):记录执行的 SQL 语句,较为简洁,但有时会导致不一致性。

  • Row-based Logging (RBL):记录数据修改的行,能够精确描述每个数据变化,通常更为可靠,但会生成大量日志。

  • Mixed-based Logging (MBL):结合了 SBL 和 RBL,根据具体情况选择使用合适的格式,平衡了性能和一致性。

问题4:redo log 如何保证事务的持久性? 

事务的持久性(Durability)要求一旦事务提交成功,它对数据库的修改必须永久生效,不会丢失。Redo Log 是用来保证这一特性的关键机制。

如何保证持久性:

  • 日志写入先于数据写入:

    • 在 InnoDB 中,当一个事务执行更新操作时,数据库的实际数据页并不会立即写入磁盘,而是首先记录到 Redo Log 中。日志写入是顺序的,这样可以确保写入日志的速度远快于直接修改数据文件。

    • 一旦事务提交,MySQL 会先将事务的 Redo Log 写入磁盘(通过 Log Buffer 和 Log File 系统),然后才通知客户端事务已提交。这种机制确保了即使系统崩溃,已提交的事务的修改不会丢失。

  • 使用日志重做操作恢复数据:

    • 在系统崩溃后,MySQL 会利用 Redo Log 恢复数据。具体来说,InnoDB 会读取在崩溃前已经写入磁盘的 Redo Log,通过重做日志中记录的操作,重做事务的修改,使数据库恢复到崩溃之前的状态。即使这些修改还没有被完全写入数据页,只要它们的 Redo Log 被成功记录,就可以在恢复过程中应用。

问题5:页修改之后为什么不直接刷盘呢? 

InnoDB 不直接将数据页修改刷盘的原因主要是为了 提高性能。通过将数据页修改先写入内存,再通过后台线程批量写入磁盘,可以减少磁盘 I/O 操作,提高写入效率。同时,通过先写 Redo Log,再刷新脏页的方式,保证了 事务的持久性 和数据的 一致性。这种延迟刷盘的机制能够平衡性能和数据安全性。

问题6:binlog 和 redolog 有什么区别? 

特性BinlogRedo Log
记录内容逻辑操作(SQL 语句或事件)物理操作(数据页的修改)
主要用途主从复制、数据恢复、审计和分析保证事务的持久性、崩溃恢复、性能优化
工作机制记录 SQL 语句或事件记录数据页的物理修改
刷新时机在事务提交时刷新在事务提交时,Redo Log 刷新到磁盘
存储位置binlog 文件ib_logfile 文件
是否用于主从复制

问题7:undo log 如何保证事务的原子性? 

  • Undo Log 通过记录事务对数据的修改操作的逆操作,确保在事务回滚时,能够撤销所有修改,恢复到事务开始前的状态。
  • 在事务发生错误或用户调用 ROLLBACK 时,Undo Log 保证了事务的原子性,使得事务要么完全成功,要么完全不做任何修改。
  • Undo Log 是事务管理系统的核心组成部分,它与 Redo Log 一起合作,保证了 ACID 原则中的 原子性持久性

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/12577.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

传输层协议——TCP协议

文章目录 &#x1f34d;TCP协议谈谈可靠性TCP协议格式序号与确认序号窗口大小六个标志位 确认应答机制&#xff08;ACK&#xff09;超时重传机制连接管理机制三次握手四次挥手 流量控制滑动窗口拥塞控制延迟应答捎带应答面向字节流粘包问题TCP异常情况TCP小结基于TCP的应用层协…

pycharm(2)

conda 我下载安装conda的时候产生了各种问题&#xff0c;最终我发现&#xff0c;打开杀毒软件会有阻碍 cuda的版本问题很大&#xff0c;我尝试多个版本之后&#xff0c;发现anaconda3-2024.06.1-windows-x86_64安装了之后不会报错&#xff0c;另外pycharm的版本也一直有问题&a…

python算法和数据结构刷题[3]:哈希表、滑动窗口、双指针、回溯算法、贪心算法

回溯算法 「所有可能的结果」&#xff0c;而不是「结果的个数」&#xff0c;一般情况下&#xff0c;我们就知道需要暴力搜索所有的可行解了&#xff0c;可以用「回溯法」。 回溯算法关键在于:不合适就退回上一步。在回溯算法中&#xff0c;递归用于深入到所有可能的分支&…

【PyQt】pyqt小案例实现简易文本编辑器

pyqt小案例实现简易文本编辑器 分析 实现了一个简单的文本编辑器&#xff0c;使用PyQt5框架构建。以下是代码的主要功能和特点&#xff1a; 主窗口类 (MyWindow): 继承自 QWidget 类。使用 .ui 文件加载用户界面布局。设置窗口标题、状态栏消息等。创建菜单栏及其子菜单项&…

鼠标拖尾特效

文章目录 鼠标拖尾特效一、引言二、实现原理1、监听鼠标移动事件2、生成拖尾元素3、控制元素生命周期 三、代码实现四、使用示例五、总结 鼠标拖尾特效 一、引言 鼠标拖尾特效是一种非常酷炫的前端交互效果&#xff0c;能够为网页增添独特的视觉体验。它通常通过JavaScript和C…

Node.js与嵌入式开发:打破界限的创新结合

文章目录 一、Node.js的本质与核心优势1.1 什么是Node.js&#xff1f;1.2 嵌入式开发的范式转变 二、Node.js与嵌入式结合的四大技术路径2.1 硬件交互层2.2 物联网协议栈2.3 边缘计算架构2.4 轻量化运行时方案 三、实战案例&#xff1a;智能农业监测系统3.1 硬件配置3.2 软件架…

利用Vue和javascript分别编写一个“Hello World”的定时更新

目录 一、利用Vue编写一个“Hello World”的定时更新&#xff08;1&#xff09;vue编码在Html文件中&#xff08;2&#xff09;vue编码在js文件中 二、利用javascript编写一个“Hello World”的定时更新 一、利用Vue编写一个“Hello World”的定时更新 &#xff08;1&#xff…

排序算法--插入排序

插入排序是一种简单且稳定的排序算法&#xff0c;适合小规模数据或部分有序数据。 // 插入排序函数 void insertionSort(int arr[], int n) {for (int i 1; i < n; i) { // 从第二个元素开始int key arr[i]; // 当前需要插入的元素int j i - 1;// 将比 key 大的元素向后移…

跟李沐学AI:视频生成类论文精读(Movie Gen、HunyuanVideo)

Movie Gen&#xff1a;A Cast of Media Foundation Models 简介 Movie Gen是Meta公司提出的一系列内容生成模型&#xff0c;包含了 3.2.1 预训练数据 Movie Gen采用大约 100M 的视频-文本对和 1B 的图片-文本对进行预训练。 图片-文本对的预训练流程与Meta提出的 Emu: Enh…

CH340G上传程序到ESP8266-01(S)模块

文章目录 概要ESP8266模块外形尺寸模块原理图模块引脚功能 CH340G模块外形及其引脚模块引脚功能USB TO TTL引脚 程序上传接线Arduino IDE 安装ESP8266开发板Arduino IDE 开发板上传失败上传成功 正常工作 概要 使用USB TO TTL&#xff08;CH340G&#xff09;将Arduino将程序上传…

游戏引擎 Unity - Unity 下载与安装

Unity Unity 首次发布于 2005 年&#xff0c;属于 Unity Technologies Unity 使用的开发技术有&#xff1a;C# Unity 的适用平台&#xff1a;PC、主机、移动设备、VR / AR、Web 等 Unity 的适用领域&#xff1a;开发中等画质中小型项目 Unity 适合初学者或需要快速上手的开…

AIGC(生成式AI)试用 20 -- deepseek 初识

>> 基本概念 Ollama -- 运行大模型&#xff0c;管理运行AI大模型的工具&#xff0c;用来安装布置DeepSeek https://ollama.com/ , Get up and running with large language models. AnythingLLM -- 大模型增强应用&#xff0c;GUI大模型交互程序 Download AnythingLLM …

STM32 DMA+AD多通道

接线图 代码配置 ADC单次扫描DMA单次转运模式 uint16_t AD_Value[4]; //DMAAD多通道 void DMA_Config(void) {//定义结构体变量 GPIO_InitTypeDef GPIO_InitStructure;//定义GPIO结构体变量 ADC_InitTypeDef ADC_InitStructure; //定义ADC结构体变量 DMA_InitTypeDef DMA_In…

【Java】位图 布隆过滤器

位图 初识位图 位图, 实际上就是将二进制位作为哈希表的一个个哈希桶的数据结构, 由于二进制位只能表示 0 和 1, 因此通常用于表示数据是否存在. 如下图所示, 这个位图就用于标识 0 ~ 14 中有什么数字存在 可以看到, 我们这里相当于是把下标作为了 key-value 的一员. 但是这…

【工欲善其事】利用 DeepSeek 实现复杂 Git 操作:从原项目剥离出子版本树并同步到新的代码库中

文章目录 利用 DeepSeek 实现复杂 Git 操作1 背景介绍2 需求描述3 思路分析4 实现过程4.1 第一次需求确认4.2 第二次需求确认4.3 第三次需求确认4.4 V3 模型&#xff1a;中间结果的处理4.5 方案验证&#xff0c;首战告捷 5 总结复盘 利用 DeepSeek 实现复杂 Git 操作 1 背景介绍…

BGP路径属性

公认必遵循 BGP必须都能识别&#xff0c;且必须发送报文必须包含 Origin&#xff1a;起源属性&#xff0c;I,E,&#xff1f;三种&#xff0c;I是BGP通过IGP协议学到的路由&#xff08;比如ospf&#xff0c;isis&#xff0c;rip&#xff09;&#xff0c;E是从EGP协议学到的&am…

Vue 图片引用方式详解:静态资源与动态路径访问

目录 前言1. 引用 public/ 目录2. assets/ 目录3. 远程服务器4. Vue Router 动态访问5. 总结6. 扩展&#xff08;图片不显示&#xff09; 前言 &#x1f91f; 找工作&#xff0c;来万码优才&#xff1a;&#x1f449; #小程序://万码优才/r6rqmzDaXpYkJZF 在 Vue 开发中&#x…

【网络编程】Java高并发IO模型深度指南:BIO、NIO、AIO核心解析与实战选型

​​ 目录 一、引言1.1 本文目标与适用场景1.2 什么是IO模型&#xff1f;阻塞 IO 模型非阻塞 IO 模型IO 多路复用模型信号驱动 IO 模型异步 IO 模型 二、基础概念解析2.1 IO模型的分类与核心思想IO模型的分类核心思想分类对比与选择依据技术示意图 2.2 同步 vs 异步 | 阻塞 vs…

基序和纯度分数的计算

以下对这两个概念的详细解释&#xff1a; 基序 纯度分数 PWM矩阵的来源 为什么会有PWM矩阵&#xff1f; 一个特定的转录因子&#xff08;TF&#xff09;的结合位点的基序&#xff08;motif&#xff09;并不是唯一的。实际上&#xff0c;TF结合位点通常具有一定的序列变异性&a…

算法日记11:SC63(离散化)

一、题目 二、题解 法一&#xff1a;前缀和&#xff08;会炸&#xff09; 对于这道题目&#xff0c;我们的第一个朴素想法就是用前缀和来进行简化操作&#xff0c;这个思路非常简单&#xff0c;就是前缀和的标准模板题&#xff0c;代码如下 void solve() {int n,q;cin>&g…