mysql 数据库 innodb(引擎) 学习浅析 (一)

文章目录

  • 前言
  • 一、mysql 架构
    • 1.连接池
    • 2.SQL接口&SQL解析器
    • 3.SQL优化器
    • 4.缓存器
    • 5.存储引擎(innodb)
    • 6. 文件系统层
  • 二、写入原理(innodb)
  • 三、存储结构(innodb-磁盘结构-表空间-独立表空间)
    • 1.基本存储结构-页(page)
      • 1.1 page header
      • 1.2 页目录(page directory)
    • 2.区、区组、段
      • 2.1 区(Extents)
      • 2.2 段(Segments)
    • 3.索引原理(B+tree,b-tree)
      • 3.1 B+tree
      • 3.2 B+tree,b-tree 的主要区别
  • 四、事务(锁、mvcc)
    • 1. 事务(TRANSACTION)
    • 2. 隔离性
    • 2.1 隔离级别,MVCC+LOCK的实现
      • 2.1.1 InnoDB Row Formats - 隐式字段
      • 2.1.2 undo log 链表(头插)
      • 2.1.3 read view
      • 2.1.4 锁(innodb lock)
      • 2.1.5 MVCC 判断机制
      • 2.1.6 总结
  • 五、sql语句的优化&注意事项(明天面试使用)
    • 1. 数据库配置优化
    • 2. 表结构优化
    • 3. select 语句优化
      • 3. 1 执行顺序
      • 3. 2 查询优化
    • 4. 索引优化
  • 总结
  • 参考文章


前言

mysql是一个关系型数据库,它的数据架构大致分为四层 连接层、服务层(解析、优化)、引擎层、存储层。

下面我将围绕 Mysql 写入过程的原理,逐步浅析 mysql 的架构、索引&存储结构以及事务相关的知识。


一、mysql 架构

mysql 数据库 innodb(引擎) 学习浅析 (一)_第1张图片

1.连接池

验证权限,管理、保持连接(避免频繁连接)。

2.SQL接口&SQL解析器

SQL接口,用来接受用户的SQL命令,并返回需要的结果。

SQL解析器,SQL语句会在解析器中被解析,分为语法解析、语义解析、创建语法树并根据数据字典丰富查询语法树,在解析器里还会去验证客户端是否具有执行该语句的权限。

3.SQL优化器

根据数据以及索引选择最佳执行方案重组sql。

4.缓存器

8.0版本被取消。

5.存储引擎(innodb)

负责 MySQL 中数据的存储和提取。

因为在关系数据库中,数据的存储是以表的形式存储的,所以存储引擎也可以称为表类型(即存储和操作此表的类型)。

6. 文件系统层

文件系统层主要是将数据库的数据存储在操作系统的文件系统之上,并完成与存储引擎的交互。


二、写入原理(innodb)

buffer pool ,数据库执行更新或插入操作时,会将写入数据放在buffer pool缓冲内进行操写入磁盘.ibd文件中(在对数据库执行增删改操作的时候,实际上主要都是针对内存里的Buffer Pool中的数据进行的)。
undo log ,MySQL 会先记录更新前的数据到 undo log 日志文件里面,当事务回滚时可以利用 undo log 来进行回滚。
redo log ,准备提交事务时,通过redo log buffer 将更新(插入、修改、删除)操作记录到redo log中,当buffer pool提交事务后io写入.ibd文件发生故障时,在故障恢复后通过redo log恢复更新数据(图步骤错误:3由2这里开始)。
bin log , 恢复、备份、主从。

mysql 数据库 innodb(引擎) 学习浅析 (一)_第2张图片
redo log 刷盘策略:

完全符合 ACID 需要默认设置 1 ,日志在每次提交事务时写入并刷新到磁盘。

设置为 0 时,日志每秒写入并刷新到磁盘一次。尚未刷新日志的事务可能会在崩溃中丢失(redo buff -> 1s-> page cache(操作系统)->磁盘)。

设置为 2 时,日志在每次事务提交后写入并每秒刷新到磁盘一次。尚未刷新日志的事务可能会在崩溃中丢失(redo buff -> page cache(操作系统)->1s->磁盘)。

对于设置 0 和 2,不能 100% 保证每秒刷新一次。由于 DDL 更改和其他InnoDB 导致日志独立于 innodb_flush_log_at_trx_commit 设置刷新的内部活动,刷新可能更频繁地发生,有时由于调度问题而不太频繁。如果每秒刷新一次日志,则在崩溃中可能会丢失最多一秒的事务。如果日志刷新频率高于或低于每秒一次,则可能丢失的事务量会相应变化。


三、存储结构(innodb-磁盘结构-表空间-独立表空间)

Mysql innodb的磁盘结构由系统表空间、独立表空间、通用表空间、撤消表空间、临时表空间等组成。
独立表空间(.ibd),存储索引和数据,结构主体是页(page)。

mysql 数据库 innodb(引擎) 学习浅析 (一)_第3张图片

1.基本存储结构-页(page)

MySQL使用页来存储表数据和索引,每个表和索引都由许多页组成,是磁盘上存储数据的最小单位 。页的大小通常是16KB,这意味着每个页最多可以存储16KB的数据,在 innodb 中页是内存(buffer pool)与磁盘交互的最小存储单元。当MySQL需要访问表或索引中的数据时,它会先加载相应的页到内存中,然后进行操作。 页之间可以不在物理结构上相连,通过双向链表相关联。页内的记录按主键大小排序构成单向链表。

MySQL支持四种不同类型的页,它们分别是数据页、索引页、系统页和未分配页。

  1. 数据页:存储表中的数据行,可以被分为不同的碎片或页片。

  2. 索引页:存储用于加速数据访问的索引结构,可以是B树、B+树或哈希索引。

  3. 系统页:存储关于数据库和表的元数据信息,包括表结构、列定义、索引定义等。

  4. 未分配页:专门用于存储未分配的磁盘空间,在需要时可以分配给其他页使用。

每种页都有其特定的作用和用途。在MySQL中,这些页可以以不同的方式组合在一起来构建表并支持各种操作。

mysql 数据库 innodb(引擎) 学习浅析 (一)_第4张图片

1.1 page header

在MySQL InnoDB中,Page Header是存储在每个数据页(或称为数据块)的开头的一部分信息。它包含了如下信息:

  1. Checksum:该页数据的CRC校验和,用于保证数据的完整性。

  2. LSN(Log Sequence Number):该页对应的最后一个修改的事务的LSN,用于支持事务的回滚和恢复操作。

  3. Page Type:该页的类型,包括数据页、索引页、undo页等。

  4. Page Level:该页在B+树中的层级。

  5. Free Space:该页中未被使用的空间大小,用于动态调整页的大小。

  6. Next Page:该页的下一页的地址,用于支持数据页的组织和链式遍历。

Page Header的作用包括:保证数据的完整性、支持事务的回滚和恢复、支持数据页的组织和遍历、支持动态调整页的大小等。

mysql 数据库 innodb(引擎) 学习浅析 (一)_第5张图片

1.2 页目录(page directory)

每个数据页都有自己的页目录上面页结构中的Page Directory,这个页目录的作用实际上就是用来进行数据行定位的。数据页中的数据实际上是按组分配的,页目录中的不同的槽位,其实是对应了数据页中的不同的分组,查询数据时,通过id找到对应的槽,再根据对应的槽来知道对应在数据页中的数据行分组,遍历数据行分组中的数据直到找到对应的数据。

mysql 数据库 innodb(引擎) 学习浅析 (一)_第6张图片

2.区、区组、段

2.1 区(Extents)

MySQL InnoDB中的Extents是由64个连续的物理页(每个物理页大小通常为16KB)组成的。 Extent是InnoDB中管理空间的基本单位之一,用于将多个连续的物理页组合成一个逻辑块,以便更有效地管理磁盘空间。

2.2 段(Segments)

在MySQL InnoDB存储引擎中,Segments是指表空间内部的逻辑结构,用于管理数据页的物理存储。每个Segment由多个连续的数据页组成,这些数据页的大小通常为16KB。
在InnoDB存储引擎中,有多种类型的Segment,包括:

  1. 数据段(Data Segment):存储InnoDB表中的所有数据,包括聚集索引和非聚集索引。
  2. 索引段(Index Segment):这是一个专门用于存储索引的数据结构,它包含了所有表中的索引信息,可以帮助MySQL快速定位到表中的具体数据,从而提高查询效率。
  3. Undo段(Undo Segment):存储事务的回滚信息,以便在回滚操作时撤销对数据页的修改。
  4. System段(System Segment):存储InnoDB的一些系统信息,例如系统表、缓冲池管理信息等。
  5. 事务段(Transaction Segment):存储正在进行的事务信息,包括事务的ID、状态、锁定对象等。

每个Segment在存储空间中都有一个唯一的编号,称为Segment ID。InnoDB存储引擎会通过Segments的管理,实现对表空间内部物理存储的高效管理和优化,提高数据读写的速度和性能。在MySQL InnoDB存储引擎中,Segments是指表空间内部的逻辑结构,用于管理数据页的物理存储。

mysql 数据库 innodb(引擎) 学习浅析 (一)_第7张图片

3.索引原理(B+tree,b-tree)

Mysql中由索引段与数据段构成构成B+tree的非叶子节点,叶子节点,主要作用是降低高度,范围查询以及减少随机io次数。

索引键针对不同数据类型的排序问题:

MySQL会根据数据类型的不同而采用不同的排序算法,以在B+树结构中进行索引键的排序。下面是MySQL对不同数据类型排序的处理方式:

  1. 整数类型:MySQL会采用二进制比较进行排序,因为整数类型的数据是固定长度的,这样可以更快地进行比较和排序。

  2. 字符串类型:MySQL会采用字符集编码的比较规则进行排序,例如,对于UTF-8编码的字符串,MySQL会按照Unicode字符集的规则进行排序。

  3. 日期和时间类型:MySQL会将日期和时间类型转换为数字类型,然后采用数值比较方法进行排序。

  4. 浮点数类型:MySQL会采用IEEE标准的浮点数比较规则进行排序,因为浮点数类型的数据是固定长度的。

总的来说,MySQL会根据不同数据类型的特点采用最有效的排序算法进行索引键的排序,以提高查询效率。需要注意的是,对于联合索引的排序,MySQL会使用所有列中最左侧的列进行排序。如果最左侧的列相同,则使用第二列进行排序,以此类推。因此,在设计联合索引时,应该考虑到查询常用的列,并将其放在联合索引的左侧,以获得更好的查询性能。

关于mysql的聚集索引原理详细,参考文章:https://mp.weixin.qq.com/s/ok2o0wOFNeaNAQIj83GZAQ
关于树的结构,参考文章:https://mp.weixin.qq.com/s/HQe8g1M5ytZYLx5vvaAYoQ。

b+tree 结构:

mysql 数据库 innodb(引擎) 学习浅析 (一)_第8张图片

3.1 B+tree

B树是一种自平衡的数据结构,它的最大特点是可以在不增加过多的树的深度的前提下,快速地进行增删改查等操作。其它特点包括:

  1. 每个节点可以存储多个关键字/数据项,而不是只有两个子节点;
  2. 所有的叶子节点都在同一层上,这使得查找操作非常高效;
  3. 每个节点中的关键字都是有序的;
  4. 每个非叶节点都会保存一个关键字,这个关键字用来确定该节点子树中所有关键字的范围。

3.2 B+tree,b-tree 的主要区别

其实二者最主要的区别是(查询效率稳定,层高,范围):

  1. B+树改进了B树, 让内结点只作索引使用, 去掉了其中指向data record的指针, 使得每个结点中能够存放更多的key, 因此能有更大的出度. 这有什么用? 这样就意味着存放同样多的key, 树的层高能进一步被压缩,使得检索的时间更短。
  2. 当然了,由于底部的叶子结点是链表形式, 因此也可以实现更方便的顺序遍历, 但是这是比较次要的,最主要的的还是第(1)点。

四、事务(锁、mvcc)

1. 事务(TRANSACTION)

MySQL事务是指 一组有序的数据库操作 ,以原子方式提交或回滚,以确保数据库中数据的一致性。

需要注意的是,MySQL默认情况下自动提交每个事务。这意味着如果没有使用START TRANSACTION或BEGIN语句,每一条语句都会自动提交为一个独立的事务。

MySQL事务具有ACID属性:

  1. 原子性(Atomicity):事务中的所有操作要么全部成功,要么全部失败,回滚至初始状态。

  2. 一致性:事务执行前和执行后数据库中数据的状态应该保持一致。

  3. 隔离性:并发的事务之间应该相互隔离,互不影响。

  4. 持久性:事务提交后,对数据库的改变应该是永久的。


ACID 总结:
为了保证一组有序的数据库操作中数据库数据的可靠及一致性,会在缓冲池(bufferr pool)与独立表空间(file-per-table)交互的过程前、中、后,也就是执行事务的前、中、后,分别使用undo log 机制保证事务的原子性,锁(写+写隔离)+mvcc(写+读隔离)机制解决保证事务的隔离性,使用redo log保证数据的持久性。

2. 隔离性

MySQL的隔离性主要解决的是并发访问数据库时出现的数据不一致的问题。在多用户并发访问数据库的情况下,如果不进行隔离处理,会发生以下情况:

  1. 脏读(Dirty Read):一个事务读取了另一个事务还未提交的数据,如果此时另一个事务回滚了,读取的数据就是脏数据。

  2. 不可重复读(Non-Repeatable Read):在同一个事务中,同一条数据多次读取得到的结果不一样。

  3. 幻读(Phantom Read):一个事务在某个条件下查询到一些数据,此时另一个事务插入了符合该条件的新数据,从而使得第一个事务再次查询时得到了新的数据结果。

    这三种情况的本质是内存与磁盘在交互的过程,进行写+读、写+写的操作产生的。


通过设置不同的隔离级别,MySQL可以保证每个事务之间不会相互干扰,从而避免了上述问题的发生。

需要注意的是隔离级别越高,数据的一致性和安全性就越高,但性能会受到影响。

MySQL事务的四种隔离级别如下:

  1. 读未提交(Read Uncommitted):最低的隔离级别,允许一个事务读取另一个事务未提交的数据。这种隔离级别可能会导致脏读(Dirty Read,即读取到其他事务还没有提交的,被覆盖或撤销的数据。)、不可重复读(Non-Repeatable Read)、幻读(Phantom Read)问题。

  2. 读已提交(Read Committed):允许一个事务只能读取其他事务已经提交的数据。这种隔离级别避免了脏读问题,但可能会出现不可重复读(Non-Repeatable Read,即同一个事务在读取同一行数据时,可能会读取到不同的值。)问题、幻读(Phantom Read)问题。

  3. 可重复读(Repeatable Read):在同一个事务内,多次读取同一行数据时,保证读取到的是相同的值。这种隔离级别避免了脏读和不可重复读问题,但可能会导致幻读(Phantom Read,即在同一个事务内,多次查询时发现结果集不一致。)问题。

  4. 串行化(Serializable):最高的隔离级别,根据事务提交的顺序依次执行事务,避免了所有并发问题,但也带来了性能开销。

mysql 数据库 innodb(引擎) 学习浅析 (一)_第9张图片

2.1 隔离级别,MVCC+LOCK的实现

官方文档参考地址:https://dev.mysql.com/doc/refman/8.0/en/innodb-multi-versioning.html

在MySQL中,当前读和快照读是两种不同的事务读取模式:

  1. 当前读(Locking Reads):是指在事务中读取的数据是事务开始时的最新数据版本,而非历史版本。当前读是默认的读取模式,也是最常用的读取模式。在当前读的情况下,数据行被加锁,其他事务无法修改该数据行,直到当前事务完成。

  2. 快照读(Consistent Nonlocking Reads):是指在事务中读取的数据是在事务开始时的数据版本,而非最新版本(因为在快照读的情况下,数据行不会被锁定,其他事务可以修改它)。


注:在MySQL中,事务默认使用当前读取模式。可以使用SET TRANSACTION语句更改读取模式,例如:SET TRANSACTION ISOLATION LEVEL READ COMMITTED;

2.1.1 InnoDB Row Formats - 隐式字段

https://dev.mysql.com/doc/refman/8.0/en/innodb-row-format.html#innodb-compact-row-format-characteristics

在InnoDB存储引擎中,以下是对数据行中的隐式字段描述:

  1. DB_TRX_ID(事务ID):指示插入或更新该行的最后一个事务的事务标识符。此外,删除在内部被视为更新,其中行中的特殊位被设置为将其标记为已删除。每个数据行的修改都会被记录在undo log中,并带有对应的事务ID。

  2. DB_ROLL_PTR(回滚指针):回滚指针指向一条写入回滚段的undo log。回滚指针指向一条写入回滚段的撤销日志记录。如果该行已更新,则撤消日志记录包含在更新之前重建该行内容所需的信息。

  3. DB_ROW_ID(行标识符):该行 ID 在插入新行时单调增加(6 字节)。回滚指针指向一条写入回滚段的撤销日志记录。如果该行已更新,则撤消日志记录包含在更新之前重建该行内容所需的信息。

总之,DB_TRX_ID、DB_ROLL_PTR和DB_ROW_ID都是InnoDB用于实现事务隔离和数据一致性的关键组件。通过记录事务ID、回滚指针和行标识符,InnoDB能够确保每个事务的修改不会互相影响,同时保持数据的一致性和唯一性。

2.1.2 undo log 链表(头插)

图片来源参考文章:https://juejin.cn/post/7214665617311088700

当 InnoDB 存储引擎执行 INSERT、UPDATE、DELETE 操作时,会先在内存中生成一条新的 undo log 记录,然后再写入到磁盘上。这个过程中,undo log 中的 DB_TRX_ID 字段值会被设置为当前执行事务的 ID。这样,在rollback 操作时,根据 DB_TRX_ID 的值就可以快速地找到和恢复到该数据行最初的值,而不需要扫描整个 redo log。 在并发执行多个事务的情况下,每个事务都会有独立的 undo log 链,记录该事务对数据行所做的修改。在事务提交之前,这些修改并不会影响到其他事务。在事务提交后,InnoDB 存储引擎会将当前事务对数据行所做的修改应用到磁盘上,同时将该事务生成的 undo log 记录删除。

mysql 数据库 innodb(引擎) 学习浅析 (一)_第10张图片

2.1.3 read view

参考文档:https://dev.mysql.com/doc/dev/mysql-server/latest/classReadView.html

在InnoDB存储引擎中,每个事务都会创建一个read view,用于提供事务执行期间的数据快照和一致性视图,确保读取的数据和事务启动时的快照相同,从而保证了数据的一致性。同时,任何未提交的修改都不会被包含在read view中,保证了读取数据的正确性。read view的重要属性如下:

  • m_creator_trx_id:这是引用read view的事务ID。这个属性指示了哪个事务创建了read view。

  • m_ids:这是一个数组,其中包含每个活动事务的ID。对于每个活动的事务,都会在read view中创建一个唯一的标识符,这个数组包含当前系统中所有正在进行的读写事务的ID。

  • m_up_limit_id:这是一个指向m_ids数组中一个元素的指针,该指针指向的元素是read view的最大ID。这个属性指示了read view所能读取的最新事务。(待续,找资料中

  • m_low_limit_id:这是一个指向m_ids数组中一个元素的指针,该指针指向的元素是可重复读隔离级别下read view的最小ID。这个属性指示了read view所能读取的最旧事务。

这些属性一起定义了InnoDB中read view的范围和属性。它们是保证InnoDB实现MVCC(多版本并发控制)的重要组成部分。

2.1.4 锁(innodb lock)

官方文档参考地址:https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html

MySQL InnoDB存储引擎中锁类型,它们分别是:

  1. 共享锁(Shared Locks): 共享锁又称为读锁,简称S锁。当一个事务对某个数据行进行读取时,会加上共享锁,其他事务也可以对该数据行加上共享锁进行读取,但是不能进行写操作。共享锁之间不会互相阻塞。可以通过SELECT语句和事务的隔离级别来实现。在InnoDB中,共享锁被用于防止脏读和不可重复读。( 官方文档参考地址:https://dev.mysql.com/doc/refman/8.0/en/innodb-locking-reads.html)

  2. 排他锁(Exclusive Locks): 排他锁又称为写锁,简称X锁。当一个事务对某个数据行进行写操作时,会加上排他锁,其他事务不能对该数据行进行读取或写操作。排他锁之间会互相阻塞。可以通过UPDATE、DELETE等语句和事务的隔离级别来实现。

  3. 意向锁(Intention Locks):意向锁是表级锁,指示事务稍后对表中的行需要哪种类型的共享锁(S锁)或排它锁。不同的事务可以在同一个表上获取不同类型的意向锁,但是第一个在表上获取意向排他 (IX) 锁的事务会阻止其他事务在表上获取任何 S 或 X 锁。相反,第一个获取表上意向共享 (IS) 锁的事务会阻止其他事务获取表上(全表)的任何 X 锁(即:如果请求事务与现有锁兼容,则将锁授予请求事务,但如果它与现有锁冲突,则不会授予该锁。事务等待,直到释放冲突的现有锁。如果锁定请求与现有锁定冲突并且由于会导致死锁而无法授予 ,则会发生错误。意向锁不会阻塞除全表请求之外的任何内容。意向锁的主要目的是表明有人正在锁定一行,或者将要锁定表中的一行。)。
    意向锁有两种类型:

    1. 意向共享锁(Intention Shared Locks, 简称IS) :表示事务打算在表中的各个行上设置共享锁。
    2. 意向排他锁(Intention Exclusive Locks, 简称IX):表示事务打算在表中的各个行上设置排他锁。

    意向锁定协议如下:

    1. 若事务要获取共享锁,则该事务必须先获取意向共享锁(IS)。
    2. 若事务要获取排他锁,则该事务必须先获取意向排他锁(IX)。


    使用意向锁可以减少锁定冲突和提高并发性。同时,意向锁只占用少量资源,因为它们只是指示事务的意图,而没有锁定任何行或资源。

  4. 记录锁(Record Locks): 记录锁又称行锁(索引记录上的锁),它是针对数据表中的某一行进行锁定,*它可以是共享锁或排他锁,是一种锁的模式*,用于控制并发读写。例如:当一个事务获取了记录锁时,其他事务不能修改该记录、不能获取该记录的排他锁或共享锁。

  5. 间隙锁(Gap Locks): 间隙锁是在索引记录之间的间隙上的锁,或者是在第一条索引记录之前或最后一条索引记录之后的间隙上的锁,它可以防止其他事务在间隔内更新(增删改)数据,从而保证事务的隔离性。另外间隙锁可以共存,即一个事务获取的间隙锁不会阻止另一个事务在同一间隙上获取间隙锁。

  6. 临键锁(Next-Key Locks): 临键锁是一种特殊类型的间隙锁,它由索引记录锁和 Gap Locks 两部分组成,用于保护当前索引记录和当前记录之前的间隙

  7. 插入意向锁(Insert Intention Locks):插入意图锁是一种INSERT间隙锁,是在数据行插入之前的操作设置。这种类型的锁表示插入的意图,这样插入到同一索引间隙的多个事务如果不在间隙内的同一位置插入,则无需等待彼此。例如:事务A在开启事务未提交状态下 insert id value(90),(102) ,此时启动一个事务B select id > 100 无需等待,若是 insert id value (100),则会等待事务提交之后执行。

  8. 自增锁(Auto-Increment Locks): 自增锁是一种特殊的表级锁,由插入到具有 AUTO_INCREMENT列的表中的事务获取。在最简单的情况下,如果一个事务正在向表中插入值,则任何其他事务必须等待将它们自己插入到该表中,以便第一个事务插入的行接收连续的主键值。

  9. 空间索引谓词锁 (Predicate Locks for Spatial Indexes): 是一种用于空间索引的锁定机制。

下表总结了表级锁类型兼容性:

mysql 数据库 innodb(引擎) 学习浅析 (一)_第11张图片

2.1.5 MVCC 判断机制

MVCC版本链的判断机制如下():
判断逻辑,undo log db_trx-id < live_min_trx_id & not in live_ live_min_trx;需要值得注意的是很多回答是使用记录行db-trx_id来作判断依据的,那么就会出现但数据行被事务提交更改的情况,也就是脏读。

  1. 读取数据时,首先会查找undo log 最新版本 db_trx_id(事务id)。

  2. 如果 db_trx_id 等于 m_creator_trx_id,说明该版本是当前事务,可以直接使用。

  3. 如果 db_trx_id 小于 m_low_limit_id,说明该版本已经提交,可以直接使用。否则,
    如果 db_trx_id 大于 m_up_limit_id,说明该版本在当前事务之后开启,不能被使用,重新遍历下个undo log版本链,继续上述流程。

  4. 如果 db_trx_id 不在 m_ids 中,则说明该版本已经提交,可以直接使用。否则,重新遍历下个undo log版本链,继续上述流程。

2.1.6 总结

MySQL使用MVCC(多版本并发控制 Multi-Version Concurrency Control )、 LOCK 来实现四种隔离级别,具体实现如下:

  1. 读未提交(Read Uncommitted):可以读取未提交的数据(允许脏读),所以允许事务直接读取(不需要加锁) 最新数据即可。

  2. 可重复读(Read Committed):读,MVCC 。更新,lock_x锁。

  3. 读已提交(Repeatable Read):读,MVCC 。更新,next-key lock。

  4. 串行化(Serializable):共享表锁(Shared Table Lock),独占表锁(Exclusive Table Lock)。

五、sql语句的优化&注意事项(明天面试使用)

1. 数据库配置优化

数据库参数优化是提高数据库性能和稳定性的重要手段。以下是一些常见的数据库参数优化方案:

  1. 内存配置优化:将数据库所使用的内存调整到合适的范围内对数据库性能有很大的影响。适当的内存配置可以有效避免频繁的磁盘 I/O 操作,从而提高数据库性能。可以通过调整参数 max_connections 和 innodb_buffer_pool_size 来控制内存配置。

  2. 磁盘配置优化:磁盘的读写速度也是影响数据库性能的重要因素之一。 可以通过调整以下参数来优化磁盘性能:

    • innodb_io_capacity:设置每秒 I/O 操作的最大数目;
    • innodb_flush_method:设置刷新数据到磁盘的方法;
    • innodb_flush_log_at_trx_commit:设置日志刷新的时间点。
  3. 网络配置优化:网络带宽和延迟也会对数据库的性能产生影响。可以通过以下方式来进行网络配置优化:

    • 将数据库与应用程序放在同一台机器上,以便加快通信;
    • 使用高速网络适配器,以便减少延迟;
    • 为应用程序和数据库分配独立的网络端口。
  4. 服务线程配置优化:调整服务线程的数量可以提高数据库的并发处理能力。可以通过以下参数进行服务线程配置优化:

    • innodb_thread_concurrency: 用于设置并发访问MySQL服务时,InnoDB 线程所允许的最大数量;
    • thread_cache_size:调整线程缓存的大小以处理连接的管理。

需要特别注意的是,在进行数据库参数优化时,需要根据数据库本身的特点、应用程序的需求、硬件设备的性能和数据库访问负载等多种因素进行综合分析和考虑,以制定一套全面的数据库参数优化方案。同时,数据库参数优化也需要持续不断的监测和改进。

2. 表结构优化

表结构的优化可以从以下几个方面入手:

  1. 指定主键和索引:为表指定主键和索引能够提高查询和更新操作的性能,同时还可以保证表的数据完整性。但是,在创建表时不应为每个列都创建索引,应该根据查询需求来确定需要建立的索引。

  2. 使用合适的数据类型:使用最小的数据类型可以降低磁盘空间的占用和 I/O 操作的次数,进而提高表创建的性能。例如,应尽量使用 TINYINT、SMALLINT、MEDIUMINT、INT 等整数类型来存储整数数据,而不是使用 BIGINT。如果存储的数据范围非常小,则可以使用 ENUM 或 SET 类型来存储,避免使用 TEXT 和 BLOB 类型。

  3. 数据行类型、按需分配空间:在创建表时使用合适的存储引擎和分配空间的方式可以降低磁盘空间的占用和 I/O 操作的次数,提高表创建的性能。例如,在 InnoDB 引擎中,可以使用 CREATE TABLE 语句的 ROW_FORMAT 子句来指定行的存储格式,例如 COMPACT、REDUNDANT 和 DYNAMIC,以及使用 innodb_file_per_table 参数来将表存储在单独的文件中,提高表的管理灵活性和性能。

3. select 语句优化

3. 1 执行顺序

MySQL的查询语句执行顺序如下:

  1. FROM:指定数据表;
  2. JOIN:用于将两个表格进行连接;
  3. WHERE:筛选符合条件的行;
  4. GROUP BY:根据指定的列对查询结果进行分组;
  5. HAVING:筛选分组后符合条件的组;
  6. SELECT:选择需要返回的列;
  7. DISTINCT:去重;
  8. ORDER BY:对结果进行排序;
  9. LIMIT:限制返回结果的数量。

需要注意的是,这是大致的执行顺序,实际上MySQL会根据查询的具体情况进行优化和调整,以提高查询效率。

3. 2 查询优化

针对 MySQL 中的 SELECT 查询,常见的优化方案有以下几种:

  1. 使用索引:合理地创建和使用索引可以大大提高查询性能。在查询时,MySQL 会优先考虑使用索引来搜索数据,因此应该为经常查询的列创建索引,并确保查询中使用的列在索引中排在前面。

  2. 避免全表扫描:尽量避免使用 SELECT * 或不带 WHERE 子句的查询语句,这样会导致 MySQL 扫描整张表,效率较低。应该只查询需要的列,并在查询中使用 WHERE 子句来限制结果集大小。

  3. 优化子查询:子查询通常会导致性能下降,因此应该尽量避免多层嵌套的子查询,或者将子查询转换为 JOIN 操作来提高性能。

  4. 使用 EXPLAIN 分析查询计划:通过使用 MySQL 的 EXPLAIN 命令,可以分析查询语句的执行计划,找到性能瓶颈并进行优化。

  5. 分解大查询:将复杂查询拆分成多个小查询,以降低单次查询的数据量和查询时的锁定时间,从而提高性能。

  6. 使用缓存:通过使用 MySQL 的查询缓存,可以避免重复查询相同的数据,提高查询性能。但是需要注意,缓存的过期和失效会影响结果的正确性,因此需要根据具体情况进行缓存的配置和管理。

根据 MySQL 官方文档,对 SELECT 语句优化-关键字:

  1. 使用索引:MySQL 使用索引可以加速查询,需要在 WHERE 和 JOIN 子句中使用索引。可以通过使用 EXPLAIN 命令来查看 MySQL 如何执行查询,以便了解查询是否使用了索引。

  2. 限制查询返回的结果集:只选择需要的列,避免查询过多列导致查询变慢。可以通过使用 SELECT column1, column2, column3 来只查询需要的列。

  3. 避免使用通配符 %:当使用通配符 % 进行模糊查询时,MySQL 很难使用索引来加速查询。应该尽量减少使用通配符 %。

  4. 使用 INNER JOIN 和 LEFT JOIN 代替子查询:使用 INNER JOIN 和 LEFT JOIN 可以代替一些复杂的子查询,在性能方面更有优势。

  5. 使用 UNION ALL 代替 UNION:当使用 UNION 时,MySQL 会对结果进行排序和去重,而使用 UNION ALL 可以避免这个问题,提高查询性能。

  6. 避免使用 ORDER BY 和 GROUP BY:ORDER BY 和 GROUP BY 子句会使整个查询慢下来,特别是在大表中。如果可以不使用这两个子句,可以提高查询性能。

  7. 优化查询语句的 WHERE 子句:使用索引、避免使用函数和使用存在的索引能够加速查询。应该尽量避免在 WHERE 子句中使用函数,因为这会导致 MySQL 无法使用索引。

  8. 避免使用COUNT():COUNT()会对整个表执行计数操作,非常耗费资源。建议使用COUNT(column)或COUNT(1)代替。

总之,以上几个方面都是 MySQL 官方文档中提到的 SELECT 语句优化的重点,可以针对具体业务场景和数据量进行分析和优化。

4. 索引优化

针对MySQL索引的优化方案主要有如下几种:

  1. 添加索引:可以通过ALTER TABLE语句添加索引来提高查询效率。一般来说,可以对经常用于查询的字段添加索引,包括主键、外键、经常用于WHERE、ORDER BY和GROUP BY的字段等。

  2. 删除索引:如果没有使用的索引可以考虑删除,因为索引会占用一定的存储空间,而且会增加查询的时间。

  3. 调整索引顺序:在联合索引中,索引的顺序也会影响查询效率。一般来说,将被筛选的字段放在左侧,可以提高查询效率。

  4. 压缩索引:对于大型表格,索引也会占用大量存储空间,可以通过压缩索引来减少占用空间,从而提高查询效率。

  5. 统计索引信息:通过统计分析查询日志,可以确定哪些索引常被使用,哪些索引没用或很少使用,从而调整索引的使用情况,提高查询效率。

  6. 使用覆盖索引:通过使用覆盖索引,可以避免查询的结果集过大,从而提高查询效率。覆盖索引指的是查询结果可以完全从索引中获取,而不需要加载数据表。

  7. 修改查询方式:有些查询可以通过修改查询方式来提高查询效率,比如优化子查询、避免使用不必要的连接、使用UNION ALL代替UNION等。

待续

总结

通过上述mysql indodb的内存结构,磁盘结构(page,segments ,b+tree等)、写入过程、事务(锁+mvcc)的描述。 可知,mySQL数据库的核心是存储引擎(innodb),在buffer pool(内存)与 磁盘文件数据的交互过程。而事务,是为了保证内存与磁盘交互的数据可靠及一致性(ACID特性)。

最后通过上述的了解,mysql数据库对于sql语句查询优化、以及数据库系统的根据参数(my.ini)调优、备份、恢复会有更好的判断理论依据

ps:关于事务与锁,后面还会继续补充内容。

参考文章

  1. https://dev.mysql.com/doc/refman/8.0/en/innodb-storage-engine.html
  2. https://dev.mysql.com/doc/dev/mysql-server/latest/PAGE_INNODB_REDO_LOG.html#sect_redo_log_general
  3. https://www.bilibili.com/video/BV1Kg411z7RS/?spm_id_from=333.788&vd_source=070f559dbb383e179353dcfa61b7557f
  4. https://mp.weixin.qq.com/s/ok2o0wOFNeaNAQIj83GZAQ
  5. https://juejin.cn/post/7214665617311088700
  6. https://www.zhihu.com/question/66320138

你可能感兴趣的:(mysql,mysql,数据库)