MySQL

发布于:2025-08-01 ⋅ 阅读:(26) ⋅ 点赞:(0)

NOSQL和SQL的区别

关系型数据库:以行列二维表格的形式存在,每一列代表数据的一种属性,每一行代表一个数据实体。
在这里插入图片描述
NOSQL是非关系型数据库。数据库逻辑上提供了不同于二维表的存储方式,存储方式可以是JSON文档、哈希表或者其他方式。
在这里插入图片描述

  • 对于需要保证ACID的应用,优先考虑SQL,反之优先考虑NoSQL。
  • NoSQL数据之间无关系,非常容易扩展。

数据库的三大范式

  • 第一范式(1NF)要求数据库的每一行都是不可分割的原子数据项
  • 第二范式(2NF)在1NF的基础上,非码属性必须完全依赖于候选码(在1NF基础上消除非主属性对主码的部分函数依赖)第二范式需要确保数据库表中的每一行都和主键相关,而不是只与主键的某个部分相关(主要针对联合主键而言)
  • 第三范式(3NF)在2NF基础上,任何非主属性不依赖于其它非主属性(在2NF基础上消除传递依赖)第三范式需要确保数据表格中的每一列数据都和主键直接相关,而不是间接相关

MySQL怎么联表查询?

在这里插入图片描述

  • 内连接(INNER JOIN):返回两个表中有匹配关系的行。
  • 左外连接(LEFT JOIN):返回左表中的所有行,即使在右表中没有匹配的行,未匹配的行会包含NULL。
  • 右外连接(RIGHT JOIN):返回右表的所有行。
  • 全外连接(FULL JOIN):返回所有表中的行。MySQL不直接支持FULL JOIN,需要使用UNION来实现。

MySQL如何避免重复插入数据?

  1. 使用UNIQUE约束:在表中的相关列上添加UNIQUE约束,确保每个值在该列中唯一。
  2. 使用INSERT…ON DUPLICATE KEY UPDATE:如果插入的记录与现有记录冲突,可以选择更新现有记录。
  3. 使用INSERT IGNORE:该语句会在插入记录时忽略那些因重复键而导致的插入错误。
  • 如果需要保证全局唯一性,使用UNIQUE约束是最佳做法。

  • 如果需要插入和更新结合可以使用ON DUPLICATE KEY UPDATE。

  • 对于快速忽略重复插入,INSERT IGNORE是合适的选择。

CHAR 和 VARCHAR 有什么区别?

  • CHAR是固定长度的字符串类型,定义时需要指定固定长度,存储时会在末尾补足空格。
  • VARCHAR是可变长度的字符串类型,定义时需要指定最长长度,实际存储时根据实际长度占用存储空间。

int(1) int(10) 在mysql中有什么区别?

区别主要在于显示宽度,而不是存储范围或数据类型本身的大小。

外键约束

外键约束的作用是维护表与表之间的关系,确保数据的完整性和一致性。

SQL查询语句的执行顺序是怎么样的?

在这里插入图片描述
所有的查询语句都是从FROM开始执行的,在执行过程中,每个步骤都会生成一个虚拟表,这个虚拟表将作为下一个执行步骤的输入,最后一个步骤产生的虚拟表即为输出结果。

(9) SELECT
(10) DISTINCT <column>,
(6) AGG_FUNC(<column> or <expression>, ... 
(1) FROM <left_table>
(3) <join_type>JOIN<right_table>
(2) ON<join_condition>
(4) WHERE <where_condition>
(5) GROUP BY <group_by_list>
(7) WITH <CUBE|ROLLUP>
(8) HAVING <having_condition>
(11) ORDER BY <order_by_list>
(12) LIMIT <limit_number>;

执行一条SQL请求的过程是什么?

在这里插入图片描述

  • 连接器:建立连接,管理连接、检验用户身份。
  • 查询缓存:查询语句如果命中查询缓存则直接返回,否则继续往下执行。MySQL8.0已删除该模块。
  • 解析SQL:通过解析器对SQL查询语句进行词法分析、语法分析,然后构建语法树,方便后续模块读取表名、字段、语句类型。
  • 执行SQL:执行SQL共有三个阶段:
    • 预处理阶段:检查表或字段是否存在;将 select * 中的 * 符号扩展为表上的所有列。
    • 优化阶段:基于查询成本的考虑,选择查询成本最小的执行计划。
    • 执行阶段:根据执行计划执行SQL查询语句,从存储引擎读取记录,返回给客户端。

mysql的引擎

存储引擎 特性 适用场景 不支持特性
InnoDB MySQL默认存储引擎,具备ACID事务支持、行级锁、外键约束 高并发的读写操作,对数据完整性和并发控制要求高的场景 无(相对MyISAM、Memory而言,它支持事务等关键特性 )
MyISAM 存储空间和内存消耗较低 大量读操作的场景 事务、行级锁、外键约束
Memory 数据存储在内存中,读操作性能高 对性能要求较高的读操作场景 事务、行级锁、外键约束,服务器重启或崩溃时数据丢失

MySQL为什么InnoDB是默认引擎?

  • 事务支持:InnoDB引擎提供了对事务的支持,可以进行ACID(原子性、一致性、隔离性、持久性)属性的操作。
  • 并发性能:InnoDB引擎采用了行级锁定的机制,可以提供更好的并发性能。
  • 崩溃恢复:InnoDB引擎通过redolog日志实现了崩溃恢复,可以在数据库发生异常情况时,通过日志文件进行恢复,保证数据的持久性和一致性。

mysql中的innodb和MyISAM的区别

对比维度 InnoDB MyISAM
事务支持 支持 不支持
索引结构 聚簇索引,文件存于主键索引叶子节点,必须有主键,辅助索引需两次查询 非聚簇索引,数据文件分离,索引存数据文件指针,主、辅索引独立
锁粒度 行锁 表锁,更新语句锁整张表,并发访问受限
count(*) 效率 不保存表行数,执行需全表扫描 用变量保存表行数,执行语句直接读变量,速度快

索引有什么好处?

索引类似书籍的目录,可以减少扫描的数据量,提高查询效率。

  • 如果查询的时候,没有用到索引就会全表扫描,这个时候查询的事件复杂度是On。
  • 如果用到了索引,那么查询的时候,可以基于二分查找算法,通过索引快速定位到目标数据,mysql索引的数据结构一般是b+树,其搜索复杂度为O(logdN),其中d表示节点允许的最大节点个数为d个。

索引的分类

分类维度 具体分类
按数据结构 B+tree索引、Hash索引、Full - text索引
按物理存储 聚簇索引(主键索引)、二级索引(辅助索引)
按字段特性 主键索引、唯一索引、普通索引、前缀索引
按字段个数 单列索引、联合索引

MySQL聚簇和非聚簇索引的区别

在这里插入图片描述

对比维度 聚簇索引 非聚簇索引
数据存储 数据行按索引键值顺序存储,叶子节点包含实际数据行,索引结构是数据物理存储结构 叶子节点不包含完整数据行,包含指向数据行的指针或主键值,数据行存储在聚簇索引中
索引与数据关系 通过聚簇索引查找数据,可直接从索引中获得数据行 通过非聚簇索引查找数据时,先在非聚簇索引中找到对应主键值,再通过主键值回溯到聚簇索引查找实际数据行(回表过程 )
唯一性 基于主键构建,每个表只能有一个聚簇索引(数据只有一种物理排序方式 ) 一个表可以有多个非聚簇索引(不直接影响数据物理存储位置 )
效率 对于范围查询和排序查询更有效率,避免额外寻址开销 使用覆盖索引查询时效率更高(无需读取完整数据行 );需回表时效率较低(有额外回表操作 )

MySQL主键是聚簇索引吗?

在这里插入图片描述
InnoDB 在创建聚簇索引的时候,会根据不同的场景选择不同的列作为索引:

  • 如果有主键,默认会使用主键作为聚簇索引键。
  • 如果没有主键,选择第一个不包含NULL值的唯一列作为聚簇索引的索引键。
  • 在上面两个都没有的情况下,InnoDB将自动生成一个隐式自增的id列作为聚簇索引键。

什么字段适合当做主键?

要点 内容
字段特性 字段具有唯一性,且不能为空
字段趋势建议 最好有递增趋势,值随机无序可能引发页分裂,影响性能
主键选用禁忌 不建议用业务数据(如会员卡号、订单号等 ),因业务可能导致字段重复或重用
主键常规方案及局限 常选用自增字段,单机适用;多服务器录入数据需合并时,可能主键重复,需考虑分布式 id 方案

B+树的特点

  • 所有叶子节点都在同一层:这是B+树的一个重要特性,确保了所有数据项的检索都具有相同的I/O延迟,提高了搜索效率。每个叶子节点都包含指向相邻叶子节点的指针,形成一个链表,由于叶子节点之间的链接,B+树非常适合进行范围查询和排序扫描。可以沿着叶子节点的链表顺序访问数据,而无需进行多次随机访问。
  • 非叶子节点存储键值:非叶子节点仅存储键值和指向子节点的指针,不包含数据记录。这些键值用于指导搜索路径,帮助快速定位到正确的叶子节点。并且,由于非叶子节点只存放键值,当数据量比较大时,相对于B树,B+树的层高更少,查找效率也就更高。
  • 叶子节点存储数据记录:与B树不同,B+树的叶子节点存储实际的数据记录或指向数据记录的指针。这意味着每次搜索都会到达叶子节点,才能找到所需数据。
  • 自平衡:B+树在插入、删除和更新操作后会自动重新平衡,确保树的高度保持相对稳定,从而保持良好的搜索性能。每个节点最多可以有M个子节点,最少可以有ceil(M/2)个子节点(除了根节点),这里的M是树的阶数。

B+树和B树的区别

对比维度 B+树 B树
数据存储位置 数据仅存储在叶子节点,非叶子节点只存索引信息 非叶子节点既存索引信息,也存部分数据
叶子节点连接方式 叶子节点用链表相连 叶子节点无链表连接
查找性能特点 查找需到叶子节点,性能稳定 可能在非叶子节点找到数据,性能相对不稳定

B+树的好处

在这里插入图片描述

  • B+树的非叶子节点不存放实际的记录数据,仅存索引,因此数据量相同的情况下,相比存储即存索引又存记录的B树,B+树的非叶子节点可以存放更多的索引,因此B+树可以比B树更「矮胖」,查询底层节点的磁盘I/O次数会更少。
  • B+树有大量的冗余节点(所有非叶子节点都是冗余索引),这些冗余索引让B+树在插入、删除的效率都更高,比如删除根节点的时候,不会像B树那样会发生复杂的树的变化;
  • B+树叶子节点之间用链表连接了起来,有利于范围查询,而B树要实现范围查询,因此只能通过树的遍历来完成范围查询,这会涉及多个节点的磁盘I/O操作,范围查询效率不如B+树。

联合索引的实现原理

联合索引:将多个字段组合成一个索引。
在这里插入图片描述

  • 联合索引查询的B+Tree是先按product_no进行排序,然后再按name排序。
  • 使用联合索引的时候,存在最左匹配原则,也就是按照最左优先的方式进行索引的匹配。

创建联合索引时需要注意什么?

建立联合索引时,要把区分度大的字段排在前面,这样区分度大的字段越有可能被更多的SQL使用到。
在这里插入图片描述

索引失效有哪些?

  • 当使用左或者左右模糊匹配的时候(like %xx或者like %xx%)。
  • 查询条件中对索引使用函数。
  • 查询条件中对索引进行计算。
  • 在字符串和数字进行比较的时候,会进行隐式类型转换,会用到函数,索引失效。
  • 联合索引不遵循最左匹配原则,索引会失效。
  • 在WHERE子句中,如果在OR前的条件列是索引列,而在OR后的条件列不是索引列,那么索引会失效。

什么情况下会回表查询?

如果查询的数据不在二级索引里,就会先检索二级索引,找到对应的叶子节点,获取到主键值后,然后再检索主键索引,就能查询到数据了,这个过程就是回表查询。

什么是覆盖索引

覆盖索引是指一个索引包含了查询所需的所有列,因此不需要访问表中的数据行就能完成查询(不需要回表)。

如果一个列即是单列索引又是联合索引,单独查它的话走哪个?

mysql优化器会分析每个索引的查询成本,然后选择成本最低的方案来执行sql。

索引已经建好了,再插入一条数据,索引会有哪些变化?

插入新数据可能导致B+数结构的调整和索引信息的更新,以保持B+树的平衡性和正确性,这些变化通常由数据库系统自动处理,确保数据的一致性和索引的有效性。
如果插入的数据导致叶子节点已满,可能会触发叶子节点的分裂操作,以保持B+树的平衡性。

索引字段是不是建的越多越好?

不是,建的越多会占用越多的空间,而且再写入频繁的场景下,对于B+树的维护所付出的性能消耗也会越大。

索引的优缺点?

分类 索引相关要点 具体内容
好处 索引的好处 提高查询速度
缺点 索引的缺点1 需要占用物理空间,数量越大,占用空间越大
缺点 索引的缺点2 创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增大
缺点 索引的缺点3 会降低表的增删改的效率,因为每次增删改索引,B + 树为了维护索引有序性,都需要进行动态维护
使用原则 索引的使用原则 不是万能钥匙,根据场景来使用

怎么决定建立索引?

分类 具体情况 说明
适用索引场景 字段有唯一性限制 如商品编码
适用索引场景 常用于 WHERE 查询条件的字段 可提高查询速度,多字段条件可建联合索引
适用索引场景 常用于 GROUP BY 和 ORDER BY 的字段 查询时无需再排序,因 B + Tree 中记录已排好序
不适用索引场景 WHERE、GROUP BY、ORDER BY 用不到的字段 索引价值是快速定位,起不到定位则无需创建,且索引会占物理空间
不适用索引场景 字段存在大量重复数据 如性别字段,记录分布均匀时,查询优化器可能忽略索引进行全表扫描
不适用索引场景 表数据太少 无需创建索引
不适用索引场景 经常更新的字段 如电商项目用户余额,索引字段频繁修改,维护成本高

索引优化

优化/注意事项分类 具体内容
前缀索引优化 使用前缀索引减小索引字段大小,增加索引页存储索引值数量,提升查询速度;大字符串字段作索引时,可帮减小索引项大小
覆盖索引优化 SQL 中 query 所有字段能在索引 B + Tree 叶子节点找到的二级索引,查询得记录无需通过聚簇索引,可避免回表操作
主键索引建议 最好自增,插入新数据按顺序添加到当前索引节点位置,无需移动已有数据,页面写满自动开辟新页面,插入效率高;非自增主键插入时可能插入到现有数据页中间,需移动数据,造成“页分裂”,产生内存碎片,影响查询效率
防止索引失效情况1 使用左或左右模糊匹配(like %xx、like %xx% )会造成索引失效
防止索引失效情况2 查询条件中对索引列做计算、函数、类型转换操作,会造成索引失效
防止索引失效情况3 联合索引需遵循最左匹配原则,否则导致索引失效
防止索引失效情况4 WHERE 子句中,OR 前条件列是索引列,OR 后条件列不是索引列,索引失效

前缀索引

使用前缀索引是为了减小索引字段大小,可以增加一个索引页中存储的索引值,有效提高索引的查询速度。在一些大字符串的字段作为索引时,使用前缀索引可以帮助减小索引项的大小。

事务的特性是什么?

  • 原子性:一个事务中的所有操作,要么全部完成,要么全部不完成。通过 undo log实现。
  • 一致性:事务操作前和操作后,数据满足完整性约束,数据库保持一致性状态。通过 持久性+原子性+隔离性来保证
  • 隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力。MVCC(多版本并发控制) 或锁机制来保证的
  • 持久性:事务结束后,对数据的修改是永久的。通过redo log保证。

mysql可能出现什么和并发相关的问题?

  • 脏读:如果一个事务读到了另一个未提交事务修改过的数据,就意味者发生了脏读现象。
  • 不可重复读:在一个事务内多次读取同一个数据,如果出现前后两次读到的数据不一样的情况,就意味着发生了不可重复读现象。
  • 幻读:在一个事务内多次查询某个符合查询条件的记录数量,如果出现前后两次查询到的记录数量不一样的情况,就意味着发生了幻读现象。

mysql是怎么解决并发问题的?

  • ** 锁机制**:通过锁机制,可以在读写操作时进行加锁,确保同时只有一个操作能够访问或修改数据。
  • 事务隔离级别:Mysql提供了多个事务隔离级别,包括读未提交读已提交可重复读串行化。通过设置合适的事务隔离级别,可以在多个事务并发执行时,控制事物之间的隔离程度,以避免数据不一致的问题。
  • MVCC:通过在数据库中保存不同版本的数据来实现不同事务之间的隔离。在读数据时,Mysql会根据事务的隔离级别来选择合适的数据版本,从而保证数据的一致性。

事务的隔离级别

  • 读未提交:一个事务还没提交时,它做的变更就能被其他事务看到。
  • 读提交:一个事务提交之后,它做的变更才能被其他事务看到。
  • 可重复读:一个事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,Mysql InnoDB 引擎的默认隔离级别
  • 串行化:会对记录加上读写锁,在多个事务对这条记录进行读写操作时,如果发生了读写冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。
隔离级别 可能发生的现象 不可能发生的现象
读未提交 脏读、不可重复读、幻读 -
读提交 不可重复读、幻读 脏读
可重复读 幻读 脏读、不可重复读
串行化 - 脏读、不可重复读、幻读

这四个隔离级别是如何实现的

隔离级别 实现方式/特点
读未提交 可读取未提交事务修改的数据,直接取最新数据
串行化 通过加读写锁避免并行访问
读提交 通过 Read View 实现,每个语句执行前重新生成 Read View
可重复读 通过 Read View 实现,启动事务时生成 Read View,事务期间复用

滥用事务的弊端

  • 如果一个事务特别多 sql,锁定的数据太多,容易造成大量的死锁和锁超时。
  • 回滚记录会占用大量存储空间,事务回滚时间长。在 MySQL 中,实际上每条记录在更新的时候都会同时记录一条回滚操作。记录上的最新值,通过回滚操作,都可以得到前一个状态的值,sql 越多,所需要保存的回滚数据就越多。
  • 执行时间长,容易造成主从延迟,主库上必须等事务执行完成才会写入 binlog,再传给备库。所以,如果一个主库上的语句执行 10 分钟,那个事务很可能就会导致从库延迟 10 分钟 。

mysql里的锁

在这里插入图片描述

锁类型 具体说明
全局锁 通过flush tables with read lock语句使整个数据库只读,其他线程增删改、表结构修改会阻塞,用于全库逻辑备份,避免备份期间数据/表结构更新致备份文件数据不符
表级锁 - 表锁 通过lock tables语句对表加锁,限制其他线程读写,也限制本线程后续读写操作
表级锁 - 元数据锁(MDL) 对表操作时自动加锁,CRUD 加 MDL 读锁,结构变更加 MDL 写锁,保证用户 CRUD 时表结构不被其他线程变更
表级锁 - 意向锁 执行插入、更新、删除操作前,先对表加“意向独占锁”,再对记录加独占锁,用于快速判断表里是否有记录被加锁
行级锁 InnoDB 引擎支持,MyISAM 引擎不支持
记录锁 锁住一条记录,分 S 锁和 X 锁,满足读写互斥、写写互斥
间隙锁 仅存在于可重复读隔离级别,解决该级别下幻读问题
Next - Key Lock(临键锁) 是 Record Lock + Gap Lock 的组合,锁定一个范围且锁定记录本身

数据库的表锁和行锁有什么作用

表锁的作用:

  • 整体控制:表锁可以用来控制整个表的并发访问,当一个事务获取了表锁时,其他事务无法对该表进行任何读写操作,从而确保数据的完整性和一致性。
  • 粒度大:表锁的力度比较大,在锁定表的情况下,可能会影响到整个表的其他操作,可能会引起锁竞争和性能问题。
  • 适用于大批量操作:表锁适用于需要大批量操作表中数据的场景,例如表的重建、大量数据的加载等。

行锁的作用:

  • 细粒度控制:行锁可以精确控制对表中某行数据的访问,使得其他事务可以同时访问表中的其他行数据,在并发量大的系统中能够提高并发性能。
  • 减少锁冲突:行锁不会像表锁那样造成整个表的锁冲突,减少了锁竞争的可能性,提高了并发访问的效率。
  • 适用于频繁单行操作:行锁适用于需要频繁对表中单独进行操作的场景,例如订单系统中的订单修改、删除等操作。

日志的文件分为哪几种?

  • redo log 重做日志:Innodb存储引擎层生成的日志,实现了事务的持久性,主要用于掉电等故障恢复
  • undo log 回滚日志:Innodb存储引擎层生成的日志,实现了事务的原子性,主要用于事务回滚和MVCC
  • bin log 二进制日志:Server层生成的日志,主要用于数据备份和主从复制
  • relay log中继日志:用于主从复制场景下,slave通过io线程拷贝master的bin log后生成的日志。
  • 慢查询日志:用于记录执行时间过长的sql,需要设置阈值后手动开启。

以下是简化提取的文字内容,保留核心要点:

MySQL 的 binlog

  1. 生成与写入:更新操作后 Server 层生成 binlog,事务提交时统一写入文件;属于 Server 层日志,所有存储引擎可用。
  2. 写入与用途:追加写模式,写满建新文件,不覆盖旧日志;用于备份恢复、主从复制,记录全量变更。
  3. 记录内容:仅记库表结构变更、数据修改(如增删改),不记查询(SELECT、SHOW 等)。
  4. 格式类型
    • STATEMENT(默认):记修改数据的 SQL 逻辑操作,主从复制用 SQL 重现;但含 uuid/now 等动态函数时,主从数据可能不一致。
    • ROW:记每行数据最终变更结果,解决动态函数问题;但批量更新会因逐行记录,导致 binlog 体积大。
    • MIXED:自动结合前两种模式,按场景选 STATEMENT 或 ROW 记录。

UndoLog日志的作用是什么

undo log是一种用于撤销回退的日志,它保证了事务的ACID特性中的原子性
在事务没提交之前,Mysql会先记录更新前的数据到undo log日志文件里面,当事务回滚时,可以利用undo log来进行回滚。
在这里插入图片描述

有了undolog为什么还要redolog?

WAL技术:MySQL的写操作并不是立刻写到磁盘上,而是先写日志,然后在合适的时间再写到磁盘上。
在这里插入图片描述
redo log是物理日志,记录了某个数据页做了什么修改,每当执行一个事务就会产生这样的一条或者多条物理日志。

redo log 记录了此次事务完成后的数据状态,记录的是更新之后的值
undo log记录了此次事务开始前的数据状态,记录的是更新之前的值

事务提交之前发生了崩溃,重启后会通过undo log回滚事务,事务提交后发生了崩溃,重启后会通过redo log恢复事务
在这里插入图片描述
redo log保证了事务的持久性。


  • 实现事务的持久性,让MySQL有crash-safe(崩溃恢复)的能力,能够保证MySQL在任何时间段突然崩溃,重启之后已提交的记录都不会丢失。
  • 将写操作从随机读变成了顺序写,提升MySQL写入磁盘的性能。

redo log怎么保证持久化的

  1. Write-ahead logging(WAL):在事务提交之前,将事务所做的修改操作记录到redo log中,然后再将数据写入磁盘。这样即使在数据写入磁盘之前发生了宕机,系统可以通过redo log中的记录来恢复数据。
  2. Redo log的顺序写入:redo log采用追加写入的方式,将redo日志记录追加到文件末尾,而不是随机写入。这样可以减少磁盘的随机I/O操作,提高写入性能。
  3. Checkpoint机制:MySQL会定期将内存中的数据刷新到磁盘,同时将最新的LSN(Log Sequence Number)记录到磁盘中,这个LSN可以确保redo log中的操作是按顺序执行的。在恢复数据时,系统会根据LSN来确定从哪个位置开始应用redo log。

binlog两阶段提交过程是怎么样的?

当客户端执行commit语句或者在自动提交的情况下,MySQL内部开启一个XA事务,分两个阶段来完成XA事务的提交
在这里插入图片描述
redo log的写入拆分成了两个步骤:prepare和commit,中间再穿插写入binlog。

  • prepare 阶段:将 XID(内部 XA 事务的 ID) 写入到 redo log,同时将 redo log 对应的事务状态设置为
    prepare,然后将 redo log 持久化到磁盘(innodb_flush_log_at_trx_commit = 1 的作用);

  • commit 阶段:把 XID 写入到 binlog,然后将 binlog 持久化到磁盘(sync_binlog = 1
    的作用),接着调用引擎的提交事务接口,将 redo log 状态设置为 commit,此时该状态并不需要持久化到磁盘,只需要 write 到文件系统的 page cache 中就够了,因为只要 binlog 写磁盘成功,就算 redo log 的状态还是 prepare也没有关系,一样会被认为事务已经执行成功;
    在这里插入图片描述

  • 如果 binlog 中没有当前内部 XA 事务的 XID,说明 redolog 完成刷盘,但是 binlog 还没有刷盘,则回滚事务。对应时刻 A 崩溃恢复的情况。

  • 如果 binlog 中有当前内部 XA 事务的 XID,说明 redolog 和 binlog 都已经完成了刷盘,则提交事务。对应时刻 B 崩溃恢复的情况。

处于prepare阶段的redo log,既可以提交事务,也可以回滚事务,这取决于是否能够在binlog中查找到与redo log相同的XID。

update语句的具体执行过程是怎样的?

具体更新一条记录 UPDATE t_user SET name = 'xiaolin' WHERE id = 1; 的流程如下:

  1. 执行器负责具体执行,会调用存储引擎的接口,通过主键索引树搜索获取 id = 1 这一行记录:
    • 如果 id=1 这一行所在的数据页本来就在 buffer pool 中,就直接返回给执行器更新;
    • 如果记录不在 buffer pool,将数据页从磁盘读入到 buffer pool,返回记录给执行器。
  2. 执行器得到聚簇索引记录后,会看一下更新前的记录和更新后的记录是否一样:
    • 如果一样的话就不进行后续更新流程;
    • 如果不一样的话就把更新前的记录和更新后的记录都当作参数传给 InnoDB 层,让 InnoDB 真正的执行更新记录的操作;
  3. 开启事务,InnoDB 层更新记录前,首先要记录相应的 undo log,因为这是更新操作,需要把被更新的列的旧值记下来,也就是要生成一条 undo log,undo log 会写入 Buffer Pool 中的 Undo 页面,不过在内存修改该 Undo 页面后,需要记录对应的 redo log。
  4. InnoDB 层开始更新记录,会先更新内存(同时标记为脏页),然后将记录写到 redo log 里面,这个时候更新就算完成了。为了减少磁盘 I/O,不会立即将脏页写入磁盘,后续由后台线程选择一个合适的时机将脏页写入到磁盘。这就是 WAL 技术,MySQL 的写操作并不是立刻写到磁盘上,而是先写 redo 日志,然后在合适的时间再将修改的行数据写到磁盘上。
  5. 至此,一条记录更新完了。
  6. 在一条更新语句执行完成后,然后开始记录该语句对应的 binlog,此时记录的 binlog 会被保存到 binlog cache,并没有刷新到硬盘上的 binlog 文件,在事务提交时才会统一将该事务运行过程中的所有 binlog 刷新到硬盘。
  7. 事务提交(为了方便说明,这里不说组提交的过程,只说两阶段提交):
    • prepare 阶段:将 redo log 对应的事务状态设置为 prepare,然后将 redo log 刷新到硬盘;
    • commit 阶段:将 binlog 刷新到磁盘,接着调用引擎的提交事务接口,将 redo log 状态设置为 commit(将事务设置为 commit 状态后,刷入到磁盘 redo log 文件);
  8. 至此,一条更新语句执行完成。

为什么要写redo log,而不是直接写到B+树里面

  • Redo Log 可提升事务提交效率,因它顺序写磁盘,比 B + 树数据页随机写性能好。
  • 具备故障恢复能力,记录物理级别修改(如数据页地址、修改内容 )。
  • 数据库重启时,依据 Redo Log 检查并重做已提交但数据页未完全写入磁盘的事务,保障事务修改生效

MySQL两次写

在这里插入图片描述
Doublewrite的出现是解决页数据损坏的问题。

在这里插入图片描述
Doublewrite Buffer 作用是,在把页写到数据文件之前,InnoDB先把它们写到一个叫doublewrite buffer(双写缓冲区)的共享表空间内,在写doublewrite buffer完成后,InnoDB才会把页写到数据文件的适当的位置。如果在写页的过程中发生意外崩溃,InnoDB在稍后的恢复过程中在doublewrite buffer中找到完好的page副本用于恢复,所以本质上是一个最近写回的页面的备份拷贝。
在这里插入图片描述

  • 页数据先通过memcpy函数拷贝至内存中的Doublewrite Buffer(大小为约 2MB)中,Doublewrite Buffer分为两个区域,每次写入一个区域(最多 1MB 的数据)。
  • Doublewrite Buffer的内存里的数据页,会fsyncDoublewrite Buffer的磁盘上,写两次到共享表空间中(连续存储,顺序写,性能很高),每次写 1MB;
  • 写入完成后,再将脏页刷到数据磁盘存储.ibd文件上(随机写);

给你张表,发现查询速度很慢,你有那些解决方案

  • 分析查询语句:使用EXPLAIN命令分析SQL执行计划,找出慢查询的原因,比如是否使用了全表扫描,是否存在索引未被利用的情况等,并根据相应情况对索引进行适当修改。
  • 创建或优化索引:根据查询条件创建合适的索引,特别是经常用于WHERE子句的字段、Orderby排序的字段、Join连表查询的字典、group by的字段,并且如果查询中经常涉及多个字段,考虑创建联合索引,使用联合索引要符合最左匹配原则,不然会索引失效
  • 避免索引失效:比如不要用左模糊匹配、函数计算、表达式计算等等。
  • 查询优化:避免使用SELECT *,只查询真正需要的列;使用覆盖索引,即索引包含所有查询的字段;联表查询最好要以小表驱动大表,并且被驱动表的字段要有索引,当然最好通过冗余字段的设计,避免联表查询。
  • 分页优化:针对 limit n,y 深分页的查询优化,可以把Limit查询转换成某个位置的查询:select * from tb_sku where id>20000 limit 10,该方案适用于主键自增的表,
  • 优化数据库表:如果单表的数据超过了千万级别,考虑是否需要将大表拆分为小表,减轻单个表的查询压力。也可以将字段多的表分解成多个表,有些字段使用频率高,有些低,数据量大时,会由于使用频率低的存在而变慢,可以考虑分开。
  • 使用缓存技术:引入缓存层,如Redis,存储热点数据和频繁查询的结果,但是要考虑缓存一致性的问题,对于读请求会选择旁路缓存策略,对于写请求会选择先更新db,再删除缓存的策略。

MySQL的主从复制

在这里插入图片描述

  • MySQL 主库在收到客户端提交事务的请求之后,会先写入 binlog,再提交事务,更新存储引擎中的数据,事务提交完成后,返回给客户端“操作成功”的响应。
  • 从库会创建一个专门的 I/O 线程,连接主库的 log dump 线程,来接收主库的 binlog 日志,再把 binlog 信息写入 relay log 的中继日志里,再返回给主库“复制成功”的响应。
  • 从库会创建一个用于回放 binlog 的线程,去读 relay log 中继日志,然后回放 binlog 更新存储引擎中的数据,最终实现主从的数据一致性。

分库和分表有什么区别

  • 分库是一种水平扩展数据库的技术,将数据根据一定规则划分到多个独立的数据库中。每个数据库只负责存储部分数据,实现了数据的拆分和分布式存储。分库主要是为了解决并发连接过多,单机 mysql扛不住的问题。
  • 分表指的是将单个数据库中的表拆分成多个表,每个表只负责存储一部分数据。这种数据的垂直划分能够提高查询效率,减轻单个表的压力。分表主要是为了解决单表数据量太大,导致查询性能下降的问题。
    在这里插入图片描述
  • 垂直分库:一般来说按照业务和功能的维度进行拆分,将不同业务数据分别放到不同的数据库中,核心理念 专库专用。按业务类型对数据分离,剥离为多个数据库,像订单、支付、会员、积分相关等表放在对应的订单库、支付库、会员库、积分库。垂直分库把一个库的压力分摊到多个库,提升了一些数据库性能,但并没有解决由于单表数据量过大导致的性能问题,所以就需要配合后边的分表来解决。
  • 垂直分表:针对业务上字段比较多的大表进行的,一般是把业务宽表中比较独立的字段,或者不常用的字段拆分到单独的数据表中,是一种大表拆小表的模式。数据库它是以行为单位将数据加载到内存中,这样拆分以后核心表大多是访问频率较高的字段,而且字段长度也都较短,因而可以加载更多数据到内存中,减少磁盘IO,增加索引查询的命中率,进一步提升数据库性能。
  • 水平分库:是把同一个表按一定规则拆分到不同的数据库中,每个库可以位于不同的服务器上,以此实现水平扩展,是一种常见的提升数据库性能的方式。这种方案往往能解决单库存储量及性能瓶颈问题,但由于同一个表被分配在不同的数据库中,数据的访问需要额外的路由工作,因此系统的复杂度也被提升了。
  • 水平分表:是在同一个数据库内,把一张大数据量的表按一定规则,切分成多个结构完全相同表,而每个表只存原表的一部分数据。水平分表尽管拆分了表,但子表都还是在同一个数据库实例中,只是解决了单一表数据量过大的问题,并没有将拆分后的表分散到不同的机器上,还在竞争同一个物理机的CPU、内存、网络IO等。要想进一步提升性能,就需要将拆分后的表分散到不同的数据库中,达到分布式的效果。