MySQL基础巩固


MySQL索引,以及数据结构是什么?

由于数据库的索引是保存到磁盘上的,因此当我们通过索引查找某行数据的时候,就需要先从磁盘读取索引到内存,再通过索引从磁盘中找到某行数据,然后读入到内存,也就是说查询过程中会发生多次磁盘 I/O,而磁盘 I/O 次数越多,所消耗的时间也就越大。

索引的目的

加速查询:索引允许数据库快速定位到特定的行,显著提高了查询的效率,避免全表扫描。

影响更新操作:索引需要在数据插入、更新、删除时进行维护,这可能会影响写操作的性能。

索引的优势和劣势

优势

  1. 快速定位数据
  2. 优化排序和分组操作,因为B+树索引是有序的
  3. 提高大型表的查询效率

劣势

  1. 维护成本:影响数据的插入、更新、删除性能。
  2. 存储开销:索引需要额外的存储空间。

索引的适用场景

  • 频繁作为查询条件的字段,特别是当查询返回的结果集为表中较小的子集时。

MySQL索引的数据结构:

  1. B+Tree(B+ 树)索引:

    1. MySQL主要的索引类型。用于 InnoDB、MyISAM、Memory 等存储引擎。
    2. 支持全值匹配、范围查询和前缀查找。
    3. 主键索引是聚簇索引,数据直接存储在索引的叶子节点上。
  2. Hash(哈希)索引:

    1. 基于哈希表,适用于等值查询。
    2. 适用于 Memory 存储引擎。当某个索引值被频繁访问时,InnoDB会在B-Tree索引上再创建一个Hash 索引,这使得某些查找操作更加高效
    3. 查找速度非常快,但缺点是数据插入和删除的速度较慢,且占用更多的内存

SQL语句可能不使用索引的情况

  1. 使用函数或者算术运算
  2. 在 WHERE 子句中,如果在 OR 前的条件列是索引列,而在 OR 后的条件列不是索引列,那么索引会失效。
  3. 查询优化器评估全表扫描比索引扫描更高效
  4. 表的数据量小,全表扫描会更快
  5. like 查询中,使用前置通配符,即like在前。(like ‘%value’)
    • 当%在前时:由于通配符在前,查询需要检查所有的记录来查找匹配项,因为索引是基于字段值的顺序构建的,所以当查询的开始部分是不确定的,数据库无法利用索引快速定位数据,只能执行全表扫描来查找匹配的行,导致索引失效。
    • 当%在后时:通配符在后,查询只需要匹配以关键字开头的任意字符,这种情况下,数据库可以利用索引快速定位到关键词开始的第一条记录,然后顺序扫描,直到不再匹配为止。
  6. 数据类型不匹配:
    • WHERE column = '123' 而不是 WHERE column = 123,这可能导致索引不被使用。
  7. NULL 值查询:
    • 查询涉及 IS NULLIS NOT NULL 时,索引的使用可能会受到限制,尤其是在某些数据库配置下。

Untitled

下面是对不同 COUNT 用法的性能分析和排序:

  1. COUNT(*)

    • COUNT(*) 统计表中的行数,不忽略任何行(包括含有 NULL 值的行)。它不关注表中的任何特定列,只是简单地计数所有行。
    • 在多数数据库系统中,COUNT(*) 被特别优化,因为它只需要遍历索引或数据行来计算总行数,不需要查看列值。
    • 统计所有行,包括包含 NULL 的行。
  2. COUNT(1)

    • COUNT(1) 实质上与 COUNT(*) 功能相同,因为 1 是一个常量表达式,表示“对每行计数”,与 COUNT(*) 一样,它不涉及任何列的内容。
    • 在大多数现代数据库中,COUNT(1)COUNT(*) 的性能非常接近,因为优化器识别这种计数模式并执行相同的操作。
    • 统计所有行,包括包含 NULL 的行。
  3. COUNT(主键字段)

    • COUNT(主键字段) 统计主键字段非 NULL 的行数。由于主键字段不允许有 NULL 值,因此 COUNT(主键字段) 实际上与 COUNT(*) 一样,都是统计所有行。
    • 这种方式的性能通常与 COUNT(*) 接近,但如果数据库优化器不充分识别这一点,可能略微有额外开销,因为它需要检查主键字段。
    • 由于主键字段不能为 NULL,所以其结果和 COUNT(*) 相同,即统计所有行。
  4. COUNT(字段)

  • COUNT(字段) 统计指定字段非 NULL 的行数。这需要访问具体的列数据,并检查每个列值是否为 NULL。
    • 如果该字段不是索引的一部分,这可能导致较慢的性能,因为数据库需要加载每一行的实际数据来检查该字段。
  • 只统计那些指定字段不为 NULL 的行。

MySQL的索引结构为什么要选B+树?

  1. 二分查找树是一个天然的二分结构,能很好的利用二分查找快速定位数据,但是它存在一种极端的情况,每当插入的元素都是树内最大/最小的元素,就会导致二分查找树退化成一个链表,此时查询复杂度就会从O(logn)降低为O(n)。

  2. 自平衡二叉树保证了查询操作的时间复杂度就会一直维持在O(logn)。但是它本质上还是一个二叉树,每个节点只能有2个子节点,随着元素的增多,树的高度会越来越高。

    树的高度通常决定了查询时潜在的磁盘I/O操作次数,因为树结构是存储在磁盘上的,并且访问树中的每个节点都可能对应一次磁盘I/O操作,尤其是当节点数据不在内存中时。因此,树的高度越高,每次查询可能涉及的磁盘I/O次数越多,从而可能影响查询的性能。然而,使用内存缓冲等技术可以减少这种影响。

    而B树和B+树都是通过多叉树的方式,会将树的高度变矮,所以这两个数据结构非常适合检索存于磁盘中的数据。

    但是MySQL默认的存储引擎InnoDB采用的是B+树作为索引的数据结构,原因有:

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

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

聚簇索引 VS 非聚簇索引

  1. 聚簇索引
    • 将数据行直接存储在索引的叶子节点上。
    • 一个表只能有一个聚簇索引,通常是主键。
  2. 非聚簇索引
    • 索引和数据行分开存储,索引项包含指向数据行的指针。
    • 一个表可以有多个非聚簇索引。
    • 查询可能会导致回表操作,即通过索引找到数据行的实际位置。

MySQL的全表扫描

全表扫描是指数据库执行查询操作时,不通过索引,逐一读取表中的每一行来确定哪些行符合查询条件

何时会触发全表扫描

  1. 表中没有建立索引或者查询的时候没有使用到索引;
  2. 使用了函数或表达式;
  3. 查询优化器觉得全表扫描更高效。

全表扫描的优势

  1. 简单:不需要考虑索引维护和构建的复杂性。
  2. 适用于小表:对于非常小的表,全表扫描很可能比建立和维护索引更快。
  3. 避免索引的开销:没有索引,就没有更新索引的开销。

全表扫描的劣势

  1. 对于大表来讲,效率较低
  2. I/O开销大,全表扫描会导致大量的磁盘I/O

MySQL 回表

场景:当通过非聚簇索引查询数据时,从返回的数据里面,无法获取所需的全部数据(即查询的列不是索引的一部分),系统就需要再次通过主键索引(聚簇索引)去查询剩下这部分数据。这个过程被称为”回表“。回表是一个附加的步骤,可能会导致额外的I/O操作。

性能影响:回表操作涉及至少两次磁盘访问,第一次是通过非聚簇索引查询数据时,第二次是通过聚簇索引(即主键索引)查询数据时。在数据行分散或索引不是完全覆盖查询需求的情况下,回表操作可能成为性能瓶颈,尤其在数据量大或查询复杂时。

发生回表的场景

  • 当查询的列不在索引中时,即查询的列超出了索引所覆盖的范围。
  • 使用 select * 查询时,因为表示所有列,而非聚簇索引只包含索引列和主键列,所有需要回表获取其他列的数据。
  • 当查询条件(WHERE子句)使用了非聚簇索引进行筛选,但返回的结果集(SELECT子句)中包含了非索引列。

避免和解决回表问题

  • 优化索引设计,使用覆盖索引:覆盖索引是非聚簇索引,它包含了查询所有列。当查询只涉及索引中的列时,就可以直接从索引中获取数据,无需回表。例如,如果你经常查询name和age,你可以创建一个包含name、age和id(主键)的索引。

在使用覆盖索引时,列的顺序很重要,它影响了索引的使用方式和效率。如果你为 agename 建立了一个复合索引,索引的创建顺序会影响查询的性能。这里是几个关键点:

  1. 索引的顺序:如果索引是按 (age, name) 的顺序建立的,那么在查询时首先使用 age 作为过滤条件的查询将能高效地利用这个索引。例如,SELECT age, name FROM table WHERE age = 30 能够充分利用该索引。但如果查询主要基于 name 而非 age,如 SELECT age, name FROM table WHERE name = 'John',那么这个索引可能不会被优化器选择,因为 name 是索引的第二部分。
  2. 最左前缀原则:在使用复合索引时,数据库遵循最左前缀原则,这意味着查询条件需要从索引的最左边开始匹配。如果索引是 (age, name),那么只有当 age 部分在 WHERE 子句中被使用时,索引才能被有效地使用。如果查询直接使用 name 而忽略 age,则这个索引的效用会大大降低。(又或者age和name同时使用时,age必须在第一个where条件中,否者同样不会生效)
  3. 多条件查询:如果查询涉及多个条件且这些条件对应于索引中的列,那么索引的效率取决于这些条件是如何使用索引列的。例如,SELECT age, name FROM table WHERE age = 30 AND name = 'John' 可以完美地利用 (age, name) 索引。
  • 限制查询的列:避免使用 select * ,而是明确指定所需的列,并且如果这些列都包含在索引中,就可以避免回表。
  • 使用聚簇索引查询:如果可能,直接使用主键(聚簇索引)进行查询,这样可以直接获取所有列的数据,无需回表。

注:在 InnoDB 存储引擎中,每个表都有一个聚簇索引,通常是主键。如果你没有明确指定一个主键,InnoDB 会选择一个唯一的非空索引作为聚簇索引。如果表中没有这样的索引,InnoDB会为表生成一个隐藏的、包含6字节长度的聚簇索引。

MySQL 页

在 MySQL 中,“页” 通常是指数据库存储引擎中数据存储的基本单位。在 InnoDB 存储引擎中,页的默认大小是 16KB。数据页用于存储表中的行数据,索引页用于存储索引数据。

  1. 数据页和事务日志:

    • InnoDB 存储引擎将数据存储在数据页中。每当数据被修改(如通过 insert、update或delete操作)时,InnoDB 不仅会更改磁盘上的数据页,而且还会在事务日志(称为redo log)中记录这些更改。这是为了确保在系统崩溃后能够恢复数据。
  2. 回滚段(Undo Logs):

    • 当事务进行修改时,原始数据会存储在所谓的回滚段中。这些回滚段实际上也存储在数据页中。它们允许数据库在事务失败或明确地回滚时恢复到原始状态。同时,回滚段支持MVCC,使得不同的事务可以看到数据的不同历史版本。
  3. 事务的一致性视图:

    • InnoDB 通过在每个事务开始时创建一个一致性视图来实现 MVCC。这意味着每个事务可以看到在其启动时刻之前已经提交的数据的快照。这个机制依赖于回滚段中的数据来提供旧版本的数据,允许对同一数据的并发读写操作。
  4. 锁定和并发控制:

    • 页面级的锁定(虽然 InnoDB 主要使用行级锁)可以用于管理对共享数据的并发访问。这有助于处理死锁和提高并发事务的性能。

MySQL 的 InnoDB 和 MyISAM 的区别

区别点 InnoDB MyISAM
事务支持 支持ACID事务,有提交和崩溃回滚功能 不支持
锁定机制 采用行级锁,进行数据修改时只锁定所涉及的行 采用表级锁,进行写操作时会锁定整个表
外键 支持外键 不支持
崩溃恢复 由于其事务日志、双写缓冲区和自动崩溃恢复功能,InnoDB在系统崩溃后可以恢复到一致的状态 没有事务日志,因此在崩溃后可能需要修复或重建表
性能 通常在需要高并发写入的应用中表现更好,因为它使用行级锁定 在只读或大量读的应用中可能会更快
MVCC (多版本并发控制) 支持 不支持

总结:因为MyISAM相对简单所以在效率上要优于InnoDB,如果系统读多写少,同时表数据量不是很大,并对原⼦性要求低,那么MyISAM最好的选择。且MyISAM恢复速度快。可直接⽤备份覆盖恢复。如果系统读少,写多的时候,尤其是并发写⼊⾼的时候。InnoDB就是⾸选了。

MySQL 的主备同步(主从复制)

主从复制是一种常用的数据备份和读取扩展策略。它允许数据从一个 MySQL 数据库服务器(主服务器)复制到一个或多个MySQL 数据库服务器(从服务器)。

MySQL 主备复制实现分成三个步骤:

  1. master 将所有数据更改(如 INSERT、UPDATE、DELETE 命令)都会记录在二进制日志中(binary log)中
  2. slave 将 master 的binary log,简称binlog,拷贝到它的中继日志(relay log)
  3. salve 重做中继日志的事件,将改变反映它自己的数据

img

SQL 语句执⾏顺序

记忆法:FOJW GHSD OL 佛教我更合适的欧拉

  1. FROM 子句

    • 首先执行FROM子句,确定查询的数据源,可能是一张表、多张表的连接或子查询。
  2. ON 子句

    • 如果涉及JOIN操作,接下来将执行ON子句以确定如何连接表。
  3. JOIN 子句

    • 执行JOIN操作,根据ON子句的条件确定如何连接表。
  4. WHERE 子句

    • 接着执行WHERE子句,对FROM子句确定的数据源进行行级过滤。
  5. GROUP BY 子句

    • 执行GROUP BY子句,将数据分组以供聚合函数使用。
  6. HAVING 子句

    • 在分组后执行HAVING子句,对分组后的数据进行过滤,HAVING子句通常用于聚合函数的条件过滤。
  7. SELECT 子句

    • 然后执行SELECT子句,选取特定的列。如果使用了聚合函数,此时将计算聚合值。
  8. DISTINCT 子句

    • 如果使用了DISTINCT关键字,将在此阶段对结果进行去重。
  9. ORDER BY 子句

    • 执行ORDER BY子句,对结果集进行排序。
  10. LIMIT / OFFSET 子句

    • 最后执行LIMIT/OFFSET子句,限制返回的结果数量或跳过一定数量的行。
    SELECT DISTINCT column1
    FROM table1
    JOIN table2 ON table1.id = table2.id
    WHERE column2 > 10
    GROUP BY column1
    HAVING COUNT(*) > 1
    ORDER BY column1
    LIMIT 10
    

    执行顺序为:

    1. FROM table1
    2. JOIN table2 ON table1.id = table2.id
    3. WHERE column2 > 10
    4. GROUP BY column1
    5. HAVING COUNT(*) > 1
    6. SELECT DISTINCT column1
    7. ORDER BY column1
    8. LIMIT 10

SQL优化方法

  1. 避免使用 select * ,只选择需要查询的列
  2. 避免在列上使用函数或者算术运算
  3. 正确使用索引
    • 为经常用于查询条件的列创建索引
    • 定期检查并优化索引,删除不再需要的索引
    • 避免过度索引,因为索引会增加写操作的开销
  4. 限制查询的数目以及返回的结果条数
  5. 使用合适的数据类型,字段尽量使⽤not null,因为NULL值列表会占用 1 字节空间。
  6. 尽量避免在查询中使用子查询,考虑使用连接查询(JOIN)
  7. 调整数据库配置,当表过大时,及时做好数据归档或分库分表
  8. 使用 EXPLAIN 分析查询
    • 使用 EXPLAIN 命令可以查看MySQL如何执行特定的SQL查询
    • 它会查询的执行计划,包括哪些索引被使用、表的扫描方式、估计的行数等
    • 通过分析EXPLAIN的输出,你可以确定哪些部分的查询可能导致性能问题
  9. 监控慢查询日志,找出需要优化的查询。

MySQL的分库分表

分库分表是数据库水平扩展的常用策略,用于解决单一数据服务器承载压力过大的问题。

分库分表的场景

  1. 数据量巨大:当单表的数据量达到数千万、数亿甚至更多时,查询性能可能会下降,此时需要考虑分表
  2. 高并发请求:当数据库的读写请求较高,超出了单一数据库服务器的处理能力,可能需要分库来分散读写压力
  3. 业务扩展
  4. 数据存储限制
  5. 提高数据安全性

分库分表的策略

  1. 垂直分库/分表
    • 根据业务功能将表分组,每个组放在不同的数据库中
    • 例如,用户相关的表放在一个数据库中,订单相关的表放在另一个数据库中
    • 这种方式可以将不同的业务负载分散在不同的数据库服务器上
  2. 水平分库/分表
    • 将表中的数据按某种规则分散到多个相同结构的表中
    • 常见的分表键包括:用户ID、订单ID、时间等
    • 例如,根据用户ID的范围将数据分散到不同的表中
  3. 基于范围的分库
    • 根据分表键的范围将数据分散到不同的数据库或表中
    • 例如,根据时间范围将数据分散到不同的表中

事务的ACID属性是什么?

A:原子性,原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生

C:一致性,事务前后数据的完整性必须保持一致。

I:隔离性,事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务所干扰,多个并发事务之间数据要相互隔离

D:持久性,事务一旦被提交,它对数据库中数据的改变就是永久性,接下来即使数据库发生故障也不应该对其有任何影响。

原子性和一致性有点类型,但是两者的侧重点不同,原子性关注状态,要么全部成功,要么全部失败,
不存在部分成功的状态。而一致性关注数据的可见性,中间状态的数据对外部不可见,只有最初状态和
最终状态的数据对外可见。

什么是死锁?如何预防和解决死锁?

当涉及到数据库时,死锁通常是指两个或多个事务在等待对方释放资源,从而导致它们都无法继续执行的情况。

死锁的四个必要条件

  1. 互斥条件:资源不能被多个事务同时占用。
  2. 请求与保持条件:一个事务在请求新的资源时保持对其他资源的占用。
  3. 不剥夺条件:已经分配给一个事务的资源不能被其他事务强行剥夺,只有该事务使用完毕后才释放资源。
  4. 循环等待条件:存在一个等待循环,即事务A等待事务B释放资源,事务B等待事务C释放资源,事务C又等待事务A释放资源,形成一个闭环。

如何预防死锁

  1. 超时:为事务设置一个超时时间。
  2. 死锁检测:数据库系统可以定期检查是否存在死锁。
  3. 预先分配:在事务开始时请求所有必需的资源。

如何解决死锁

  1. 手动干预:DBA可以手动终止或回滚某些事务,从而释放资源并打破死锁。
  2. 使用死锁检测工具:许多数据库系统都提供了死锁检测工具,可以帮助DBA找到并解决死锁问题。

描述数据库的隔离级别以及它们之间的区别

  1. 未提交读:事务最低的隔离级别,它允许另外一个事务可以看到另一个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻读
  2. 已提交读:保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。可能避免脏读,但可能出现不可重复读和幻读。
  3. 可重复读:可以防止脏读和不可重复读。但是可能出现幻读。除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免不可重复读。
  4. 可串行化:最高事务隔离级别。事务被处理为顺序执行,除了防止脏读,不可重复读,还避免了幻读。
隔离级别 脏读 不可重复读 幻读
未提交读 可能 可能 可能
已提交读 不可能 可能 可能
可重复读 不可能 不可能 可能
可串行化 不可能 不可能 不可能

MySQL 的MVCC 是什么

MVCC 是一种多版本并发控制机制,通过事务的可见性看到自己预期的数据,能降低其系统开销。(RC和RR工作)

InnoDB 的MVCC,是通过在每行记录后面保存系统版本号(可以理解为事务的ID),每开始一个新的事务,系统版本号就会自动递增,事务开始时刻的系统版本号会作为事务的ID。这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的,防止幻读的产生。

  1. MVCC 手段只适用于MySQL隔离级别中的读已提交(Read commited)和可重复读(Repeatable Read)。
  2. Read uncommited 由于存在脏读,即能读到未提交事务的数据行,所以不适用MVCC。
  3. 简单的select快照读不会加锁,删改及select for update 等需要当前读的场景会加锁。

原因是MVCC的创建版本和删除版本只要在事务提交后才会产生。客观上,MySQL使用的是乐观锁的一种实现方式,就是每行都有版本号,保存时根据版本号觉得是否成功。InnoDB的MVCC使用到快照存储在Undo日志中,该日志通过回滚指针把一个数据行所有快照连接起来。

什么是视图(View)?它与普通表有什么不同?

视图是数据库中的一个虚拟表,是基于一个或多个实际表的结果集的表现形式。视图并不存储数据,只是保存了一个SQL查询。当视图被引用时,该查询会被执行,从而生成视图的数据。

区别 视图 普通表
数据存储 不存储数据。它只是一个基于实际表的查询的定义。每次查询视图时,都会执行其定义的查询 实际存储数据。数据在物理磁盘上有实际的存储位置
更新性 不是所有的视图都可以更新。只有当视图基于一个单一的表,并且没有使用某些聚合函数、DISTINCT关键字等时,才可以更新。 可以直接更新
复杂性和灵活性 可以基于多个表,并使用复杂的SQL逻辑,如连接、过滤和聚合。这使得视图可以提供一个简化或特定的数据视图,隐藏复杂的SQL细节 表示实际的数据结构,通常不包含复杂的逻辑或计算
用途 抽象和隐藏复杂的SQL查询 用于持久存储数据
性能 由于视图是在每次查询时动态生成的,所以可能会有性能问题,特别是当视图基于多个表或包含复杂逻辑时 直接查询普通表通常更快,因为数据是实际存储的

如何备份和恢复MySQL数据库?以及什么是数据库的冷备份和热备份?

如何备份MySQL 数据库或表

使用 mysqldump 工具,mysqldump 是MySQL提供的一个命令行工具,用于导出数据库为SQL文件。

示例:备份数据库

mysqldump -u [username] -p[password] [database_name] > [dump_file.sql]

示例:备份特定的表

mysqldump -u [username] -p[password] [database_name] [table_name] > [dump_file.sql]

如何恢复MySQL数据库

可以使用MySQL命令行客户端直接执行SQL文件来恢复数据库

mysql -u [username] -p[password] [database_name] < [dump_file.sql]

冷备份与热备份

冷备份(Code Backup)

特点

  1. 冷备份是在数据库处于关闭状态时进行的备份。
  2. 在备份期间,数据库不可用,不能进行读写操作。
  3. 冷备份通常更简单,因为它不需要处理并发的数据修改。备份的数据是一致的,不需要额外的日志或恢复步骤。

优点:数据一致性得到保证,恢复过程简单快速。

缺点:需要停机,可能导致业务中断。

实现方式:冷备份的实现相对简单。通常只需停止数据库服务,然后复制数据库文件(例如数据文件、日志文件等)到备份位置。完成后,再重新启动数据库服务。

热备份(Hot Backup)

  1. 热备份是在数据库处于运行状态时进行的备份,不需要停机。
  2. 在备份期间,数据库仍然可用,可用进行读写操作。
  3. 热备份通常需要更复杂的策略,因为需要确保备份的数据一致性。这通常涉及到使用日志文件来记录备份期间的所有更改,以便在恢复时应用这些更改。
  4. MySQL的InnoDB存储支持热备份。

优点:不需要停机,数据库始终可用。可以实现近实时或增量备份,减少备份时间和存储需求。

缺点:可能需要额外的存储和计算资源。恢复过程可能比冷备份复杂,特别是当需要应用大量的日志更改时。

实现方式:热备份的实现较为复杂。通常需要专门的备份工具或软件。这些工具可以在数据库运行时捕获数据的一致性快照,并记录备份期间的所有更改。恢复时,首先恢复数据快照,然后应用备份期间的所有更改。

MySQL的InnoDB存储引擎能够支持热备份的原因主要与其内部的设计和特性有关

  1. 事务日志(Redo Logs):InnoDB 使用事务日志来确保数据的持久性和恢复能力。当数据发生变化时,这些变化首先被记录在redo日志中,然后再异步地写入磁盘。在热备份期间,即使数据正在变化,也可以使用这些日志来捕获备份开始后的所有更改,确保数据的一致性。
  2. 多版本并发控制(MVCC):InnoDB 使用MVCC来支持高并发。这意味着每个事务都看到一个数据的“快照”,而不是实时数据。这使得在热备份期间可以为备份创建一个数据快照,而不影响正在运行的事务。
  3. 一致性非锁定读取:InnoDB允许在不锁定整个表或数据库的情况下进行一致性读取。这意味着备份过程可以读取数据,而不会堵塞其他事务。

总的来说,选择冷备份还是热备份取决于业务需求。如果可以承受短时间的停机,冷备份可能是一个简单的选择。如果需要24/7的可用性,热备份可能是更好的选择,但可能需要复杂的备份和恢复策略。

如何确保数据库的高可用性和灾难恢复

高可用性是确保数据库在出现故障时仍然可用的能力。

策略

  1. 主从复制:一个主数据库处理写操作,一个或多个从数据库处理读操作。如果主数据库出现故障,一个从数据库可以被提升为新的主数据库。
  2. 负载均衡:使用负载均衡器分发数据库请求,确保没有单点故障。

灾难恢复是在发生灾难性事件后恢复数据和服务的能力。

策略

  1. 定期备份:定期将数据库备份到安全的位置。
  2. 异地备份:将备份存储在与生产数据库不同的地理位置。
  3. 备份验证:定期验证备份的完整性和可恢复性。

慢查询

含义:慢查询是数据库中查询时间超过指定阈值的SQL,指的是在日志中记录运行比较慢的SQL语句。

启用慢查询日志

  • 确保MySQL 数据库的慢查询日志(slow query log)被启用。可以通过设置 slow_query_log 和 long_query_time 参数来开启和配置慢查询日志。

分析慢查询日志

  • 日志文件分析:定期检查慢查询日志文件,查找执行时间长、频率高的SQL语句。
  • 使用工具:使用 mysqldumpslow 或 pt-query-digest 这样的工具来分析MySQL 的慢查询日志。

实时性能分析

  • 使用 show processlist:在MySQL中,可以使用 show processlist 命令来查看当前执行的查询及其运行时间。

导致慢查询的原因

  1. 缺乏适当的索引:没有为查询中的条件列创建索引,导致数据库执行全表扫描。
  2. 索引不当:虽然存在索引,但可能不是最优的,所以优化器没有走索引查询。
  3. 查询设计不当:使用了低效的SQL语句,如使用子查询代替连接。
  4. 数据量巨大:随着数据的增长,未优化的查询可能会变得越来越慢。
  5. 硬件限制:如磁盘I/O、CPU或内存瓶颈。
  6. 使用了大量的JOIN操作:特别是在大表上。
  7. 使用了复杂的聚合函数:如GROUP BY、COUNT、SUM等。
  8. 网络延迟:尤其是在分布式数据库系统中。

如何避免慢查询

  1. 优化查询:确保SQL语句尽可能简单高效。
  2. 使用索引:为经常用于查询条件的列创建索引。
  3. 避免在查询中使用‘’ **:只选择需要的列。
  4. 减少JOIN操作的数据:尤其是在大表上。
  5. 使用数据库的查询优化工具:如MySQL的EXPLAIN
  6. 定时维护数据库:如优化表、更新统计信息等。

如何解决慢查询

  1. 分析慢查询日志:找出最慢的查询。
  2. 使用EXPLAIN命令:查看查询的执行计划,找出瓶颈。
  3. 优化查询:根据EXPLAIN的结果,重写SQL语句。
  4. 添加或调整索引:确保查询使用了最优的索引。
  5. 考虑硬件升级:如增加内存、使用更快的磁盘或升级CPU。
  6. 考虑使用缓存:考虑在程序中使用缓存,如redis或memcached,减少对数据库的请求。

为什么JOIN会影响查询缓慢,导致慢查询(基于大表的情况下)

首先 JOIN 操作是关系型数据库中的一个核心操作,它允许从两个或多个表中组合数据。

  1. 数据量增加:当两个表进行 JOIN 操作时,结果集的大小可能会显著增加,特别是在进行笛卡尔积(每个表中的每一行与另一个表中的每一行组合)时。
  2. 全表扫描:如果没有适当的索引支持JOIN操作,数据库可能需要对一个或两个表进行全表扫描,这在大表上是非常耗时的。
  3. 复杂的数据处理:JOIN操作可能需要数据库进行复杂的数据处理,如排序、过滤和聚合,这会增加查询的执行时间。
  4. I/O开销:JOIN操作可能导致大量的磁盘I/O,特别是当数据不在内存中时。
  5. 临时表:某些JOIN操作可能需要数据库创建临时表来处理查询结果,这回增加额外的I/O开销和处理时间。
  6. 多表连接:连接多个表会增加查询的复杂性和处理时间。每增加一个表,都可能导致性能下载。

为了避免由于JOIN操作导致的性能问题,可以采取以下策略

  1. 使用适当的索引:确保JOIN操作的列都有索引,并且这些索引是最优的
  2. 减少结果集的大小:在JOIN操作之前使用WHERE子句过滤数据,以减少需要处理的数据量。
  3. 避免笛卡尔积:始终在JOIN操作中使用明确的连接条件,以避免不必要的数据组合。
  4. 只连接所需的表:避免不必要的表连接,只连接真正需要的表。
  5. 使用EXPLAIN命令分析

笛卡尔积:简单的说就是两个集合相乘的结果。

假设集合A={a, b},集合B={0, 1,2},则两个集合的笛卡尔积为{(a, 0),(a, 1), (a, 2), (b, 0), (b, 1), (b, 2)}。

分布式数据库与传统数据库的主要区别

区别点 分布式数据库 传统数据库
数据存储位置 数据被分布在多个物理位置上。 数据通常存储在单一的物理位置上。
可扩展性 可以通过添加更多的节点来水平扩展,以支持更大的数据量和更高的查询负载。 通常依赖于垂直扩展,即增加单一服务器的硬件资源。
可用性和容错性 由于数据在多个节点上有多个副本,即使某些节点出现故障,数据仍然可用。 如果单一的数据库服务器出现故障,可能会导致数据不可用。
数据一致性 可能使用不同的数据一致性模型,如最终一致性,这意味着在某些情况下,不同的节点可能会看到数据的不同版本。 通常使用严格的ACID事务模型,确保数据的强一致性。
复杂性 由于需要处理节点之间的通信、数据同步和故障恢复等问题,所以通常比传统数据库更复杂。 由于所有数据都在一个地方,所以管理和维护相对简单。
查询性能 可以并行处理查询,因为数据分布在多个节点上。但跨节点的查询可能会受到网络延迟的影响。 查询性能取决于单一服务器的硬件和数据库的优化。
事务处理 处理跨多个节点的事务可能会更复杂,并可能需要特殊的协议,如两阶段提交。 事务处理通常更简单和直接,因为所有数据都在同一个地方。

Explain关键字详解

EXPLAIN是MySQL中的一个关键字,用于显示MySQL如何执行SQL查询。通过Explain,可以了解查询的执行计划,从而找出可能的性能瓶颈并进行优化。

使用方法:你只需在你的SELECT、INSERT、UPDATE或DELETE查询前加上EXPLAIN关键字:

EXPLAIN SELECT * FROM _table WHERE _column = ‘some_value’;

Explain 返回的参数及其意义:

  1. id:查询的标识符。如果查询中包含子查询,每个子查询都会有一个不同的id。
  2. select_type:查询的类型。常见的值有:
    • SIMPLE:简单的SELECT查询,不包含子查询或JOIN。
    • PRIMARY:查询中最外层的SELECT。
    • SUBQUERY:在SELECT或WHERE子句中的子查询。
    • DERIVED:在FROM子句中的子查询。
  3. table:输出结果集的表。
  4. type:连接类型。常见的值有:(自上到下,优化程度为从最优到最差)
    • system:表只有一行(等于const)
    • const:通过主键或者唯一索引查找一行。
    • eq_ref:通过主键或者唯一索引查找多行。
    • ref:通过非唯一索引查找。
    • range:通过索引查找某个范围的行。
    • index:全索引扫描。
    • ALL:全表扫描。
  5. possible_keys:可能使用的索引。
  6. key:实际使用的索引。
  7. key_len:使用的索引的长度。
  8. ref:显示索引的哪一列被使用。
  9. rows:估计要检查的行数。
  10. Extra:其他信息。常见的值有:
    1. Using index:表示只使用了索引,没有读取实际的行数据。
    2. Using where:表示使用了WHERE子句来过滤结果。
    3. Using temporary:表示使用了临时表来存储中间结果。
    4. Using filesort:表示使用了外部排序。

通过理解EXPLAIN的输出,开发者可以更好地了解查询的执行方式,找出可能的性能瓶颈,并进行相应的优化。例如,如果type列显示为ALL,这意味着进行了全表扫描,这通常是一个性能瓶颈,可能需要添加或调整索引来改善性能。


  目录