《高性能MySQL》之MySQL架构与历史

ppgo8 于 2023-02-27 发布

MySQL架构与历史

​ MySQL最重要、最与众不同的特性:存储引擎架构,这种架构的设计将查询处理及其他系统任务数据的存储/提取相分离。这种处理和存取相分离的特性,可以让开发者根据需求选择数据存取方式。

MySQL逻辑架构

img

MySQL分为两层:

连接管理与安全性

​ 每个客户端连接都会在服务器进程中拥有一个线程,这个连接的查询只会在这个单独的线程中执行,该线程只能轮流在某个CPU或CPU核心中运行。服务器会缓存线程。

优化与执行

​ MySQL解析器创建解析树后,优化器会进行优化,如:重写查询、决定表的读取顺序、选择合适索引等。

​ 优化器并不关心存储引擎,但是存储引擎对于优化查询有影响。

SELECT语句执行过程

  1. 连接器,和客户端建立了连接

  2. 查询缓存,命中返回结果。若无,到2

  3. 解析器,词法解析、语法解析

  4. 优化器,制定决策过程,选择索引

  5. 执行器,执行建立的决策

  6. 存储引擎:提供读写接口,返回数据集。

并发控制

​ 只要有多个查询需要在同一时刻修改数据,都会发生并发控制的问题。这里讨论MySQL在两个层面的并发控制:

​ 这里只简单讨论MySQL如何控制并发读写

读写锁

情况:

解决方法:并发控制。通过两种类型的锁解决问题,共享锁(读锁)和排他锁(写锁)

读锁:互不干扰,多个用户可以同时读取

写锁:排他,会阻塞其他的读锁和写锁

锁粒度

​ 提高共享资源并发性的方式:让锁定对象更有选择性。尽量只锁定需要修改的部分数据,而不是所有资源。

​ 锁也会消耗资源。如果系统花费大量时间来管理锁,而不是存取数据,会影响性能。

​ 策略锁,就是在锁的开销和数据安全性之间寻求平衡。MySQL提供了多种选择,每种MySQL存储引擎都可以实现自己的策略所和锁粒度。下面介绍两种重要的锁策略

  1. 表锁 (table lock)

    最基本锁策略、开销最小的策略。控制整个表。

  2. 行级锁(row lock)

    行级锁可以最大程度支持并发处理(同时也带来了最大的锁开销)。在InnoDB和XtraDB中,实现了行级锁。

    行级锁 只能在存储引擎层实现,而在MySQL服务层没有实现。

事务

​ 事务就是一组原子性的SQL查询,或者说一个独立的工作单元。事务内的语句,要么全部执行成功,要么全部执行失败。

​ 银行应用是解释事物必要性的一个经典例子。

image-20230227182923453

​ 事务处理需要数据库系统做更多的安全工作,需要更大内存、CPU等。MySQL的存储引擎架构就发挥优势,用户可以根据业务是否需要事务处理,来选择合适的引擎。

隔离级别(I 隔离性)

​ SQL标准中定义了4种隔离级别,每一种级别都规定了一个事务中所做的修改,哪些在事务内部和事务间是可见的,哪些是不可见的。较低级别的隔离通常可以执行更高的并发,系统开销也更低。

  1. read uncommited(未提交读)

    事务中的修改,即使没有提交,对其他事物也可见。

    问题:脏读,事物可以读取未提交的数据。

    实际中较少使用,安全性差。

  2. read commited(提交读)

    解决的问题:

    • 脏读

    一个事物开始时,只能看见已经提交的事物的修改。换句话说,一个事物从开始到提交之前,所做的修改其他事物不可见。

    问题:不可重复读。因为两次执行相同的查询,可能会得到不一样的结果。

    大多数数据库的隔离级别都是read commited(但是MySQL不是)。

  3. ppepeatable read(可重复读)

    解决的问题:

    • 脏读
    • 不可重复度

    该级别可以保证同一个事务中多次读取同样的记录的结果使用一样的。

    问题:幻读,当某个事物在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录。

    可重复读是MySQL的默认事务隔离级别。InnoDB和XtraDB存储引擎通过多版本并发控制解决了幻读问题。

  4. seralizable(可串行化)

    解决的问题:

    • 脏读
    • 不可重复度
    • 幻读

    这是最高的隔离级别。实际中较少使用,开销大。

死锁

​ 死锁是指,两个或多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环的现象。

image-20230227191509180

​ 只要事务进行,没有提交结束,遇到的资源就会被锁定。上述情况中,双方都在等待对方释放锁,同时又持有对方的锁。

​ 锁的行为和顺序和存储引擎相关。以同样的顺序执行语句,有些存储引擎会产生死锁,有些则不会。死锁的产生有双重原因:

​ 死锁发生后,只有部分或者完全回滚其中一个事物,才能打破死锁。对于事务型的系统,这是无法避免的。

事务日志

​ 事务日志可以帮助提高事务的效率。使用事务日志,存储引擎在修改表数据时的运作方式:

  1. 修改数据内存拷贝

  2. 修改行为记录到持久的磁盘上的事务日志中

    事物日志采用追加的方式,写日志的操作是一块连续的I/O,而不是随机I/O,不需要移动多个磁头。

  3. 在合适的空闲的时候,内存中被修改的数据在后台慢慢写回磁盘

​ 如果修改数据已经完成1,2步骤,但没完成3,此时系统崩溃,存储引擎重启后能够自动回复这部分修改的数据。

MySQL中的事务

​ MySQL提供了两种事务型的存储引擎:InnoDB,NDB Cluster。还有一些第三方存储引擎也支持事务,如XtraDB,PBXT。

多版本并发控制(MVCC)

​ 可以认为MVCC是行级锁的一个变种,但是它在很多情况下避免了加锁操作,因此开销更低。大多数的实现机制为:非堵塞的读操作,写操作只锁定必要的行。

​ MVCC的实现,通过保存数据在某个时间点的快照来实现。不同的存储引擎的MVCC实现不同,以InnoDB的简化版行为说明MVCC的工作。

MySQL存储引擎

​ 概要的描述MySQL的存储引擎,而不涉及太多细节。

使用show table status命令显示表的相关信息

该的各个参数含义如下:

image-20230227214947600

image-20230227215021531

image-20230227215031911

InnoDB存储引擎

​ InnoDB是MySQL默认的事务型存储引擎,也是最重要,使用最广泛的存储引擎。它被设计用来处理大量的短期事务,短期事务大部分是正常提交的,很少会被回滚。

​ InnoDB的性能和自动崩溃回复的特性,使得它在非事务型存储的需求中也很流行。除非有特别需求,应该优先考虑使用InnoDB引擎。

MyISAM存储引擎

​ 在MySQL 5.1及之前的版本,MyISAM是默认的存储引擎。MyISAM不支持事务和行级锁,而且崩溃后无法安全恢复。对于只读数据、或者表比较小、可以忍受修复操作,则依然可以继续使用MyISAM。

存储

​ MyISAM将表存储在两个文件中:数据文件和索引文件,分别以.MYD.MYI为拓展名。

MyISAM特性

​ MyISAM是MySQL最早的存储引擎之一,有一些特性可以满足用户的实际需求。

MyISAM压缩表

​ 如果表在创建并导入数据后,不会再进行修改操作,那么这样的表适合采用MyISAM压缩表。

myisampack压缩表。压缩表不能修改(除非先解压、然后修改、再压缩)。

​ 压缩表可以极大减少磁盘空间占用,也会减少I/O。

MyISAM性能

​ MyISAM最典型的性能问题:表锁。

MySQL内建的其他存储引擎

​ MySQL还有一些特殊用途的存储引擎。有些可能已经不再支持,有些会继续支持,但是需要明确地启用后才能使用。

第三方存储引擎

OLTP类引擎

面向列的存储引擎

​ MySQL默认是面向行的,每一行的数据是一起存储的,服务器的查询也是以行为单位。而在面对大量数据时,面向列的方式可能效率更高。

社区存储引擎

​ 社区存储引擎很多,但是大部分影响力有限,或者很少有人使用。

选择合适的存储引擎

​ 对于如何选择存储引擎,简单归纳为一句话:除非需要用到某些InnoDB不具备的特性,并且没有其他办法可以替代,否则都应该优先选择InnoDB引擎。例如,要使用全文索引,建议优先InnoDB+Spnhinx的组合,而不是使用支持全文索引的MyISAM.

​ 除非万不得已,不建议混合使用多种存储引擎。如果应用需要不同的存储引擎,请先考虑一下几个因素。

转换表的引擎

​ 有很多方法可以将表的存储引擎转换成另外一种引擎。

总结