MySQL

MySQL

1.基础篇

1.1 关系型和非关系型数据库的区别你了解多少?

  • 关系型数据库的优点
    • 容易理解。因为它采用了关系模型来组织数据。
    • 可以保持数据的一致性。
    • 数据更新的开销比较小。
    • 支持复杂查询(带where子句的查询)
  • 非关系型数据库的优点
    • 不需要经过SQL层的解析,读写效率高。
    • 基于键值对,数据的扩展性很好。
    • 可以支持多种类型数据的存储,如图片,文档等等。

1.2 说一说三个范式

  • 第一范式(1NF):
    数据库表中的字段都是单一属性的,不可再分。这个单一属性由基本类型构成,包括整型、实数、字符型、逻辑型、日期型等。

  • 第二范式(2NF):
    在第一范式的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分

  • 第三范式(3NF):
    在第二范式的基础上,非主键列只依赖于主键,不依赖于其他非主键

数据库设计的三大范式旨在确保数据库的规范性、减少冗余、提高数据完整性和可维护性。第一范式确保每一列都是原子值;第二范式消除部分依赖;第三范式消除传递依赖。遵循这些范式有助于创建结构合理、易于维护的数据库。

1.3 事务的四大特性(ACID)介绍一下?

  • 原子性(atomicity,或称不可分割性):事务的所有操作要么全部成功,要么全部回滚。

  • 一致性(consistency):事务在执行过程中不会破坏数据的完整性和约束条件,无论事务成功还是失败,数据都应该保持在一个一致的状态。

  • 隔离性(isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行

  • 持久性(durability):已被提交的事务对数据库的修改应该永久保存在数据库中。

1.4 存储引擎如何选择?

如果没有特别的需求,使用默认的Innodb即可。
InnoDB 引擎由于其对事务参照完整性,以及更高的并发性等优点开始逐步的取代 MyISAM

  • 事务和数据完整性:如果需要事务、数据完整性、外键支持,应选择 InnoDB。
  • 高并发:如果数据库应用需要高并发处理,行级锁的优势使 InnoDB 更合适。
  • 只读应用:如果应用主要是读取数据,且不需要事务,MyISAM 可能具有性能优势。
  • 全文搜索:如果需要全文搜索功能,MyISAM 是一种选择,但 InnoDB 在某些版本中也支持全文搜索。

1.5 事务的隔离性

脏读、不可重复读和幻读是数据库事务隔离级别中可能出现的问题。它们与数据库事务的隔离性有关,指在不同事务中读写数据时可能遇到的异常行为。以下是这些问题的详细解释和示例。

  • 脏读(Dirty Read)

    • 脏读发生在一个事务读取了另一个未提交的事务所写的数据。这可能导致读取到不一致或无效的数据。

    • 示例:
      事务 A 修改了某个表中的数据,但还没有提交。此时,事务 B 读取了该数据。如果事务 A 决定回滚,则事务 B 读取的数据可能是错误的。这种情况就是脏读。

  • 不可重复读(Non-Repeatable Read)

    • 不可重复读是指一个事务在不同时间多次读取同一行数据时,结果可能不同,因为另一事务在这期间修改了该行。

    • 示例:
      事务 A 读取了某个表中的数据,然后事务 B 修改并提交了该数据。接下来,事务 A 再次读取该数据,发现数据已经改变。这种情况就是不可重复读。

  • 幻读(Phantom Read)

    • 幻读是指一个事务在不同时间多次读取数据时,结果可能不同,因为另一事务在这期间插入或删除了数据行。

    • 示例:
      事务 A 读取了满足某个条件的所有记录,然后事务 B 插入了一行满足相同条件的数据。接下来,事务 A 再次读取相同的条件,发现多了一个记录。这种情况就是幻读。

事务隔离级别

这些问题的发生与事务的隔离级别有关。SQL 数据库通常提供四个隔离级别,每个隔离级别对这些问题的处理不同。

  • 读未提交(Read Uncommitted):
    最低的隔离级别,允许脏读、不可以重复读和幻读。

  • 读已提交(Read Committed):
    中级隔离级别,防止脏读,但可能出现不可重复读和幻读。

  • 可重复读(Repeatable Read):
    较高的隔离级别,防止脏读和不可重复读,但可能出现幻读。

  • 序列化(Serializable):
    最高的隔离级别,防止脏读、不可重复读和幻读,但可能降低并发性能。

这里需要注意的是:
Mysql 默认采用的 REPEATABLE_READ隔离级别
Oracle 默认采用的 READ_COMMITTED隔离级别

1.6 服务器日志

常见的 MySQL 服务器日志

  • 错误日志(Error Log):
    记录了 MySQL 服务器启动、运行和关闭过程中遇到的错误、警告和通知。错误日志对于诊断 MySQL 服务器的运行问题非常重要。

    • 用途:排查数据库启动失败、崩溃或运行时遇到的错误。
    • 配置:使用 –log-error 参数或 log_error 配置选项来设置错误日志的位置。
  • 查询日志(General Query Log):
    记录了 MySQL 服务器接收到的所有 SQL 查询和命令。查询日志可以帮助你了解数据库中发生的所有操作。

    • 用途:监控数据库的活动,调试查询操作,排查问题。
    • 配置:使用 –general-log 参数或 general_log 配置选项启用查询日志。
    • 注意:查询日志可能导致大量日志文件,影响性能,通常仅在调试或诊断时启用。
  • 慢查询日志(Slow Query Log)
    记录了执行时间超过指定阈值的查询。慢查询日志可以帮助你检测和优化慢查询。

    • 用途:识别慢查询,优化数据库性能。
    • 配置:使用 –slow-query-log 参数或 slow_query_log 配置选项启用慢查询日志,并使用 long_query_time 设置阈值。
    • 优化:通过分析慢查询日志,可以优化索引、简化查询逻辑等。

主从复制相关:

  • 二进制日志(Binlog):

    • 记录所有改变数据的语句。
    • 用于复制和数据恢复。
    • 可以在数据库崩溃后用于数据恢复。

中继日志(Relay Log)是 MySQL 主从复制中用于从服务器(Slave)的一种日志文件。它在复制过程中起到缓冲和中转的作用。具体来说,中继日志用于存储从主服务器(Master)接收到的二进制日志事件,然后从服务器会从中继日志中读取这些事件并应用到自己的数据库中。

  • 重做日志(Redolog):

    • 记录所有对数据库的修改。
    • 用于事务的崩溃恢复,确保数据库的一致性。
    • 在数据库崩溃后,可以用于恢复未完成的事务。
  • 撤销日志(Undolog):

    • 记录数据修改前的原始值。
    • 用于事务回滚和MVCC。
    • 在事务回滚时,可以撤销未提交的事务。
  • 日志的配置和管理
    在 MySQL 中,日志的配置和管理需要考虑性能和存储空间。以下是一些常见的日志管理技巧:

    • 启用必要的日志:仅在需要时启用查询日志和慢查询日志,以减少性能影响。
    • 定期清理日志:定期清理二进制日志和中继日志,防止占用过多存储空间。
    • 日志轮换:使用日志轮换策略,确保日志文件不会过大。
    • 监控日志:定期查看日志,及时发现和解决问题。

1.7 B 树和 B+ 树的区别

  • 数据存储位置:B 树的内部节点和叶子节点都可以存储数据,而 B+ 树只有叶子节点存储数据,内部节点只用于索引。
  • 顺序遍历:B+ 树的叶子节点按顺序链接,可以快速进行顺序遍历和范围查询,而 B 树没有这样的顺序链接。
  • 树的高度:由于 B+ 树的内部节点不存储数据,内部节点数量相对较少,通常比 B 树更扁平。
  • 性能:B+ 树通常在数据库和文件系统中表现更好,特别是顺序遍历和范围查询。

所以 B+ 树在数据库索引和文件系统中广泛应用,因为它提供了快速查找、顺序遍历和范围查询的能力。

1.8 视图 和 游标

视图(View)和游标(Cursor)是数据库中用于数据处理和查询的两种不同概念,它们在用途和功能上有显著差异。视图是一种虚拟表,它提供了对数据的自定义视角,而游标是一种用于遍历结果集的数据库对象。以下是视图和游标的详细解释以及它们的区别。

  • 视图是一种虚拟表,它是基于 SQL 查询的结果生成的。视图可以为用户提供自定义的数据视角,而无需直接操作底层表。

    • 示例:
      创建一个视图来显示特定部门的员工信息:
    CREATE VIEW sales_employees AS
    SELECT id, name, department_id
    FROM employees
    WHERE department_id = 1;
    
  • 游标是一种用于逐行遍历数据库结果集的数据库对象。游标允许你在结果集中逐行移动,并对每一行执行操作。

    • 示例:
      使用游标遍历员工表,计算每个员工的奖金:
    DELIMITER //
    CREATE PROCEDURE calculate_bonuses()
    BEGIN
    DECLARE done INT DEFAULT 0;
    DECLARE emp_id INT;
    DECLARE emp_bonus DECIMAL(10, 2);
    DECLARE emp_cursor CURSOR FOR SELECT id FROM employees;
    DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;
    
    OPEN emp_cursor;
    read_loop: LOOP
        FETCH emp_cursor INTO emp_id;
        IF done THEN
        LEAVE read_loop;
        END IF;
        -- 计算奖金的逻辑
        SET emp_bonus = ...;
        -- 更新奖金
        UPDATE employees SET bonus = emp_bonus WHERE id = emp_id;
    END LOOP;
    CLOSE emp_cursor;
    END //
    DELIMITER ;
    

1.9 乐观锁 和 悲观锁

乐观锁(Optimistic Locking)和悲观锁(Pessimistic Locking)是数据库中用于处理并发操作的一种机制。它们旨在确保多用户环境中的数据一致性和完整性。在选择使用哪种锁机制时,考虑并发性、性能需求和可能发生的冲突是关键。以下是乐观锁和悲观锁的详细解释以及它们的区别和应用场景。

  • 乐观锁(Optimistic Locking)
    乐观锁假设数据更新时冲突的概率较低,因此在更新数据前不对数据进行加锁。相反,它通过检测数据在读和写之间是否发生变化,来确保数据的正确性。如果数据在此期间发生变化,乐观锁会阻止更新,确保数据不被无意中覆盖。

    • 特点:
      • 不进行显式加锁,避免了锁竞争。
      • 通过检查机制(如版本号或时间戳)来确保数据一致性。
      • 当发生并发冲突时,操作可能被拒绝,并需要重新尝试。
    • 实现方式:
      • 版本号:在表中增加一个版本号列,每次更新时增加版本号。更新操作时检查版本号,如果版本号不一致,则拒绝更新。
      • 时间戳:类似于版本号,但使用时间戳来检测数据变化。
    • 应用场景:
      乐观锁适用于并发冲突概率较低的场景,尤其是在读多写少的环境中。它避免了锁的竞争,通常性能更高。
      示例:
      使用版本号实现乐观锁的更新操作:
      -- 读取数据和版本号
      SELECT name, version FROM employees WHERE id = 1;
      
      -- 更新数据时检查版本号
      UPDATE employees
      SET name = 'John', version = version + 1
      WHERE id = 1 AND version = current_version;
      
  • 悲观锁(Pessimistic Locking)
    悲观锁假设数据更新时可能会发生冲突,因此在进行操作前对数据进行显式加锁。这样,其他用户无法同时访问被锁定的数据,避免了并发冲突。

    • 特点:
      • 通过加锁来确保数据一致性,防止数据被同时修改。
      • 锁定期间,其他操作可能被阻塞,等待锁释放。
      • 当锁竞争严重时,可能导致性能问题和死锁。
    • 实现方式:
      • 行级锁:锁定特定的行,其他操作无法修改该行,直到锁释放。
      • 表级锁:锁定整个表,其他操作无法对表进行修改。
    • 应用场景:
      悲观锁适用于并发冲突概率较高的场景,尤其是在写多读少的环境中。悲观锁可以确保数据的一致性,但可能导致性能问题和锁竞争。
      示例:
      使用行级锁进行悲观锁的更新操作:
      -- 开启事务
      START TRANSACTION;
      
      -- 显式加锁
      SELECT * FROM employees WHERE id = 1 FOR UPDATE;
      
      -- 执行更新操作
      UPDATE employees SET name = 'John' WHERE id = 1;
      
      -- 提交事务
      COMMIT;
      
  • 乐观锁和悲观锁的区别

    • 加锁方式:
      • 乐观锁不进行显式加锁,而是通过版本号或时间戳来确保数据一致性。
      • 悲观锁通过显式加锁,防止数据被同时修改。
    • 性能和并发:
      • 乐观锁通常性能较高,因为不进行显式加锁,适用于读多写少的场景。
      • 悲观锁可能导致性能问题,适用于写多读少的场景。

2.0 MySQL 索引类型

MySQL 索引的最主要数据结构包括 B+ 树、哈希表、R 树和全文索引等。这些数据结构各有特点和适用场景,在 MySQL 中用于提高查询速度和优化数据库性能。以下是 MySQL 索引最主要的数据结构及其用途的详细介绍。

  1. B+ 树(B+ Tree)
    B+ 树是一种平衡树结构,用于支持快速查找和顺序遍历。在 MySQL 中,B+ 树通常用于存储索引,并保持树的平衡,以确保快速的插入、删除和查找操作。
  • 特点:
    • B+ 树的内部节点只存储键和指向子节点的指针,所有数据存储在叶子节点中。
    • B+ 树的叶子节点按顺序链接,支持快速顺序遍历和范围查询。
    • B+ 树能够保持平衡,确保操作的时间复杂度较低。
  • 应用场景:
    • B+ 树索引常用于 MySQL 的 InnoDB 存储引擎,用于主键索引、唯一索引和一般索引。它适用于大部分查询操作,如等值查询、范围查询和排序操作。
  • 实现方式:
    • MySQL 使用 B+ 树来实现 InnoDB 的聚簇索引和辅助索引。
    • 聚簇索引:B+ 树的叶子节点存储表中的完整行数据,叶子节点按顺序链接。
    • 辅助索引:B+ 树的叶子节点存储键值和指向聚簇索引的指针。
  1. 哈希表(Hash Table)
    哈希表是一种基于哈希函数的索引结构,用于快速查找和等值查询。在 MySQL 中,哈希索引用于特定场景,适用于快速的等值查询。

  2. R 树(R-Tree)
    R 树是一种用于空间数据索引的树结构,适用于地理信息系统和空间数据库。在 MySQL 中,R 树用于 MyISAM 存储引擎的空间索引。

  3. 全文索引(Full-Text Index)
    全文索引用于快速搜索和检索文本数据,适用于全文搜索操作。在 MySQL 中,全文索引用于支持文本内容的搜索和检索。

2.1 聚集索引 和 非聚集索引

聚集索引非聚集索引是关系型数据库中的两种索引结构。

  • 聚集索引将数据行按索引顺序存储,适用于范围查询和排序等场景;
  • 非聚集索引只存储索引键和指向数据行的指针,不改变数据行的存储顺序。

形象的例子:

  • 聚集索引就像你按照字母顺序整理好的书,每一页都按照某个特定的顺序排列,比如按照书的内容排列。这样,你要找到某一页,只需翻开书的第一页,然后顺着页码找到对应的内容。这种方式很适合按照顺序查找或者按照范围查找。

  • 非聚集索引就像是另外一本独立的目录,它并不按照书的内容排列,而是按照关键词(比如主题)来排序,然后指向书的实际位置。这样,如果你要查找某个关键词,就先查找这个独立目录,然后再根据指向的位置去找到实际的内容。虽然查找速度快,但它不改变书的内容排列顺序。

选择合适的索引类型取决于应用场景、查询需求和数据组织方式。聚集索引在 InnoDB 等存储引擎中通常是主键索引,而非聚集索引用于加速特定查询。

2.SQL篇

2.1 简述下SQL及分类?

结构化查询语言(Structured Query Language)简称SQL,是一种数据库查询语言。
作用:用于存取数据、查询、更新和管理关系数据库系统。
基本的分类如下:

  1. 数据查询语言(Data Query Language, DQL)
    负责进行数据查询而不会对数据本身进行修改的语句,这是最基本的SQL语句。

  2. 数据定义语言 (Data Definition Language, DDL)
    负责数据结构定义与数据库对象定义的语言,由CREATEALTERDROP三个语法所组成

  3. 数据操纵语言(Data Manipulation Language, DML)
    负责对数据库对象运行数据访问工作的指令集,以INSERTUPDATEDELETE三种指令为核心,
    分别代表插入、更新与删除。

  4. 数据控制语言 (Data Control Language)
    它可以控制特定用户账户对数据表、查看表、预存程序、用户自定义函数等数据库对象的控制权。
    GRANTREVOKE两个指令组成。

2.2 drop、delete与truncate的区别是什么?

三者都表示删除,但是三者有一些差别:

  • 在不再需要一张表的时候,用drop;
  • 在想删除部分数据行时候,用delete;
  • 在保留表而删除所有数据的时候用truncate。

2.3 关联查询

关联查询(Join Query)是一种将两个或多个表中的数据根据特定条件组合在一起的方法。通过关联查询,你可以从不同表中提取相关数据,并将它们结合到一个结果集中。以下是 MySQL 中常见的关联查询类型,以及每种关联查询的详细解释、应用场景和示例。

  1. INNER JOIN(内连接)
    内连接是最常见的关联查询类型,返回两个表中都存在匹配记录的行。

    • 用途:
      用于提取两个或多个表中符合连接条件的记录。如果某个表中没有匹配的记录,则该行不会出现在结果集中。
    • 应用场景:
      内连接适用于需要提取两个表中匹配数据的场景,例如,员工与部门的对应关系。
    SELECT employees.name, departments.name
    FROM employees
    INNER JOIN departments
    ON employees.department_id = departments.id;
    
  2. LEFT JOIN(左连接)
    左连接返回左表中的所有记录,即使右表中没有匹配的记录。在右表中没有匹配的数据时,结果集中的对应列将为 NULL。

    • 用途:
      用于确保左表中的所有记录都被包含在结果集中,即使右表没有匹配的记录。
    • 应用场景:
      当需要保留左表中的所有数据时,左连接是理想的选择。例如,找出没有分配部门的员工。
    SELECT employees.name, departments.name
    FROM employees
    LEFT JOIN departments
    ON employees.department_id = departments.id;
    
  3. RIGHT JOIN(右连接)
    右连接与左连接相似,但它返回右表中的所有记录,即使左表中没有匹配的数据。在左表中没有匹配的数据时,结果集中的对应列将为 NULL。

    • 用途:
      用于确保右表中的所有记录都被包含在结果集中。
    • 应用场景:
      当右表中的数据需要保留时,例如,找出没有员工的部门。
    SELECT employees.name, departments.name
    FROM employees
    RIGHT JOIN departments
    ON employees.department_id = departments.id;
    
  4. CROSS JOIN(交叉连接)
    交叉连接生成两个表的笛卡尔积,每个表中的每一行与另一个表中的每一行组合。

    • 用途:
      用于生成所有可能的组合,通常在没有明确的连接条件时使用。
    • 应用场景:
      交叉连接用于生成所有可能的组合,例如,生成所有产品与所有客户的组合。
    SELECT products.name, customers.name
    FROM products
    CROSS JOIN customers;
    
  5. SELF JOIN(自连接)
    自连接是将一个表与自身进行连接,用于查找自引用关系。

    • 用途:
      用于处理表中自引用关系的查询,例如员工与他们的经理。
    • 应用场景:
      当需要找到自引用关系时,自连接是有效的解决方案。
    SELECT a.name AS employee_name, b.name AS manager_name
    FROM employees a
    JOIN employees b
    ON a.manager_id = b.id;
    
  • 关联查询的注意事项
    • 连接条件:
      关联查询通常需要指定连接条件,以确保结果的准确性。
    • 性能考虑:
      在处理大量数据或复杂连接时,关联查询可能导致性能问题。适当的索引和优化可以改善性能。
    • 空值处理:
      在 LEFT JOIN 和 RIGHT JOIN 中,右表或左表中没有匹配数据时,结果集中的对应列将为 NULL。因此,查询中应考虑空值处理。

2.4 IN 和 EXISTS

INEXISTS是 MySQL 中的两种子查询操作符,通常用于检查某个值是否在特定集合中。它们在用法上有一定的重叠,但在性能和应用场景上存在一些关键区别。以下是 IN 和 EXISTS 的详细解释,以及它们的区别和适用场景。

  • IN 操作符
    IN 用于检查某个值是否在给定的集合中,通常用于简单的子查询和静态集合。

    • 用法:
      IN 可以用于将一个值与一组值进行比较,常用于子查询或固定列表。
    SELECT * FROM employees
    WHERE department_id IN (SELECT id FROM departments WHERE name = 'Sales');
    
    • 特点:
      • IN 会一次性计算子查询中的所有结果,然后与外部查询进行比较。
      • 在子查询结果较小时,IN 的性能通常较好。
      • 如果 IN 中有重复值,MySQL 会自动去重。
  • EXISTS 操作符
    EXISTS 用于检查子查询是否返回至少一行结果,通常用于复杂的条件检查。

    • 用法:
      EXISTS 通常用于确定子查询是否有结果,适用于检查是否存在相关记录的场景。
    SELECT * FROM employees
    WHERE EXISTS (SELECT 1 FROM departments WHERE department_id = employees.department_id AND name = 'Sales');
    
    • 特点:
      • EXISTS 会逐行检查子查询,如果子查询有结果,则 EXISTS 返回 true。
      • EXISTS 在子查询较大且仅需检查存在性时性能较好。
      • EXISTS 在处理复杂条件和多表连接时更有效。
  • IN 与 EXISTS 的区别

    • 性能:
      • IN 通常在子查询结果较小时性能较好,因为它一次性计算所有结果。
      • EXISTS 在子查询结果较大且只需检查存在性时性能较好,因为它一旦找到匹配结果可以停止计算。
    • 使用场景:
      • IN 适用于与静态集合或小型子查询进行比较。
      • EXISTS 适用于检查子查询是否存在结果,尤其是涉及复杂条件或多表连接的情况下。
    • 可读性:
      • IN 通常在需要比较一个值与集合时更直观。
      • EXISTS 在需要检查条件是否满足时更合适。

2.5 基于MySQL简述下SQL的生命周期

  1. 应用服务器与数据库服务器建立一个连接
  2. 数据库进程拿到请求sql
  3. 解析并生成执行计划,执行
  4. 读取数据到内存并进行逻辑处理
  5. 通过步骤一的连接,发送结果到客户端
  6. 关掉连接,释放资源

3.高可用篇

3.1 主从复制的作用及解决的问题是?

  1. 主从复制的作用
    • 主数据库出现问题,可以切换到从数据库。
    • 可以进行数据库层面的读写分离。
    • 可以在从数据库上进行日常备份。
  2. 解决的问题
    • 数据分布:随意开始或停止复制,并在不同地理位置分布数据备份
    • 负载均衡:降低单个服务器的压力
    • 高可用和故障切换:帮助应用程序避免单点失败
    • 升级测试:可以用更高版本的MySQL作为从库

3.2 分库分表的常见类型

分库分表的常见类型包括水平分割和垂直分割,具体如下:

  • 水平分表(Sharding)
    水平分表是将数据行按某个规则拆分到不同的表或库中。这种方式通常用于数据量大、行数多的场景。
  • 垂直分表(Vertical Partitioning)
    垂直分表是将不同的列拆分到不同的表或库中。这种方式通常用于表中的列数过多,或数据结构过于复杂的场景。

4.性能优化篇

4.1 主键使用自增ID还是UUID?

推荐使用自增ID,不要使用UUID。

因为在InnoDB存储引擎中,主键索引是作为聚簇索引存在的,

也就是说,主键索引的B+树叶子节点上存储了主键索引以及全部的数据(按照顺序),

如果主键索引是自增ID,那么只需要不断向后排列即可,如果是UUID,
由于到来的ID与原来的大小不确定,会造成非常多的数据插入,数据移动

然后导致产生很多的内存碎片,进而造成插入性能的下降。

总之,在数据量大一些的情况下,用自增主键性能会好一些。 关于主键是聚簇索引,

如果没有主键,InnoDB会选择一个唯一键来作为聚簇索引,
如果没有唯一键,会生成一个隐式的主键。

4.2 如何优化子查询(嵌套查询)

  1. 关联查询(内连接,外连接,自连接)替代
  2. 优化GROUP BY和DISTINCT 这两种查询据可以使用索引来优化,是最有效的优化方法
  3. 关联查询中,使用标识列分组的效率更高 如果不需要ORDER BY,
    进行GROUP BY时加ORDER BY NULL,MySQL不会再进行文件排序。
  4. WITH ROLLUP超级聚合,可以挪到应用程序处理

4.3 对慢查询都怎么优化?

  1. 首先分析语句,看看是否load了额外的数据,可能是查询了多余的行并且抛弃掉了,
    可能是加载了许多结果中并不需要的列,对语句进行分析以及重写
  2. 分析语句的执行计划,然后获得其使用索引的情况,之后修改语句或者修改索引,
    使得语句可以尽可能的命中索引。
    如果对语句的优化已经无法进行,可以考虑表中的数据量是否太大,如果是的话可以进行横向或者纵向的分表。

4.4 一道场景题:假如你所在的公司选择MySQL数据库作数据存储,一天五万条以上的增量,预计运维三年,你有哪些优化手段?

  1. 设计良好的数据库结构,允许部分数据冗余,尽量避免join查询,提高效率。

  2. 选择合适的表字段数据类型和存储引擎,适当的添加索引。

  3. MySQL库主从读写分离。

  4. 找规律分表,减少单表中的数据量提高查询速度。

  5. 添加缓存机制,比如Memcached,Apc等。

  6. 不经常改动的页面,生成静态页面。

  7. 书写高效率的SQL。比如SELECT * FROM TABEL 改为SELECT field_1, field_2 FROM TABEL。

4.5 覆盖索引

覆盖索引(Covering Index)是指一种索引,其中包含了查询所需的所有数据,因此查询可以直接从索引中获取结果,而无需访问表中的实际数据。由于覆盖索引能够减少 I/O 操作并提高查询效率,因此在数据库优化中具有重要作用。

  • 覆盖索引的工作原理
    覆盖索引的关键在于索引包含了查询所需的所有列。传统的索引通常只包含索引列以及指向表中相应数据行的指针,但覆盖索引不仅包含索引列,还包括查询所需的其他列。当查询可以完全通过索引获取数据时,就不需要访问表的数据行,这可以显著减少 I/O 操作,提高查询性能。

  • 覆盖索引的应用场景
    覆盖索引在以下场景中非常有用:

    • 快速查询:
      覆盖索引可以显著加快查询速度,因为它减少了读取表数据的必要。
    • **避免回表**:
      **传统索引需要通过指针访问表的数据,这称为回表**。覆盖索引不需要回表,因为所有数据都在索引中。
    • 查询优化:
      覆盖索引可以用于优化查询性能,特别是在读取数据量大的情况下。
    • 减少 I/O:
      覆盖索引减少了对表数据的访问,从而减少了 I/O 操作,提高了效率。
  • 覆盖索引的示例
    假设有一个员工表,其中包含员工的 ID、姓名、部门 ID 和工资。如果你想查询某个特定部门的所有员工姓名和工资,则可以创建一个覆盖索引,包含 department_id、name 和 salary 列。这种情况下,查询可以直接从索引中获取数据,而无需访问表中的行。

    -- 创建覆盖索引
    CREATE INDEX idx_department_name_salary
    ON employees (department_id, name, salary);
    
    -- 使用覆盖索引进行查询
    SELECT name, salary
    FROM employees
    WHERE department_id = 1;
    

在这个示例中,查询所需的所有列都包含在索引中,因此不需要回表,可以显著提高查询性能。

  • 覆盖索引的注意事项
    • 索引大小:
      覆盖索引可能会增加索引的大小,因为它包含了查询所需的所有列。在设计覆盖索引时,需要平衡索引大小与查询性能。
    • 索引维护:
      覆盖索引需要维护,这可能会增加数据库的负担,特别是在高频率的插入、更新、删除操作中。
    • 选择合适的列:
      在创建覆盖索引时,选择包含查询所需的列,同时避免过多不必要的列,以减少索引大小。

4.6 既然索引有那么多优点,为什么不对表总的每一列创建一个索引呢?

  • 当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。
  • 索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立簇索引,那么需要的空间就会更大。
  • 创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加

4.7 设置多个索引有效吗

在一个特定的查询中,通常只有一个索引能够生效。这是因为数据库优化器会根据查询条件、索引选择性和表的大小等因素选择最适合的索引。为了确保查询效率,数据库管理员需要选择和设置合适的索引,并定期检查和优化索引。通过使用EXPLAIN语句,你可以查看查询计划,了解哪个索引生效以及优化器选择索引的方式。

4.8 你知道哪些数据库结构优化的手段?

  • 范式优化:比如消除冗余(节省空间。。

  • 反范式优化:比如适当加冗余等(减少join)限定数据的范围:务必禁止不带任何限制数据范围条件的查询语句。
    比如:我们当用户在查询订单历史的时候,我们可以控制在一个月的范围内。

  • 读/写分离:经典的数据库拆分方案,主库负责写,从库负责读;

  • 拆分表:分区将数据在物理上分隔开,不同分区的数据可以制定保存在处于不同磁盘上的数据文件里。
    这样,当对这个表进行查询时,只需要在表分区中进行扫描,而不必进行全表扫描,明显缩短了查询时间,另外处于不同磁盘的分区也将对这个表的数据传输分散在不同的磁盘1/O,一个精心设置的分区可以将数据传输对磁盘1/0竞争均匀地分散开。对数据量大的时时表可采取此方法。可按月自动建表分区。

4.9 数据库高并发解决方案

应对数据库高并发,关键在于优化数据库设计、查询、锁机制、缓存策略和系统配置。

  1. 数据库设计:
  • 分库分表:将数据分布到多个表或库,减少单一表的负载。
  • 读写分离:将读操作分配给从库,写操作集中在主库。
  1. 查询优化:
  • 索引优化:使用合适的索引,加快查询速度。
  • 简化查询:减少复杂查询和嵌套子查询。
  1. 缓存策略:
  • 应用层缓存:使用 Redis、Memcached 等缓存,减少数据库压力。
  • 数据库缓存:利用数据库查询缓存,缓存常用查询结果。
  1. 锁机制优化:
  • 行级锁:避免表级锁,减少锁竞争。
  • 乐观锁:减少锁的持有时间。
  1. 数据库配置和硬件:
  • 配置优化:调整数据库参数,确保高并发性能。
  • 硬件资源:确保足够的 CPU、内存和存储,避免瓶颈。
  1. 监控和调整:
  • 监控工具:实时监控数据库性能。
  • 分析慢查询:找出并解决性能瓶颈。

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1430797759@qq.com

文章标题:MySQL

字数:8.7k

本文作者:花落阁

发布时间:2023-04-13, 23:58:34

最后更新:2024-05-14, 10:14:00

原始链接:https://hualog.dns.navy/2023/04/13/MySQL/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。