高性能MySQL之MySQL架构篇

311 阅读7分钟

1.1 MySQL逻辑架构

image.png

最上层的服务大多数是基于网络的客户端、服务端工具,比如连接处理、授权认证、安全等。

第二层是MySQL的核心功能,包括查询解析、分析、优化、缓存以及所有内置函数,所有跨存储引擎的功能都在这一层实现:存储过程、触发器、视图等。

第三层包括存储引擎,负责MySQL中数据的存储和提取,不会解析SQL。

MySQL处理请求主要分为:

  • 连接管理与安全性: 每个客户端连接都在服务器进程中拥有一个线程,当客户端连接到MySQL服务器时,对其进行认证。
  • 优化与执行: MySQL查询前会先检查查询缓存,否则会解析查询,并创建内部数据结构(解析树),然后对其进行各种优化,包括重新查询、决定表读取顺序、选择合适索引等,用户可以通过特殊关键字提示优化器,影响其决策过程。

1.2 并发控制

1.2.1 读写锁

处理并发读或写操作时,通过实现一个由两种类型的锁组成的锁系统来解决问题,分别时共享锁(读锁)和排他锁(写锁)。

隐示加锁

  • InnoDB会根据隔离级别在需要的时候自动加锁
  • 锁只有在执行commit或者rollback的时候才会释放,并且所有的锁都是在同一时刻被释放

显示加锁

  • 共享锁:select...lock in share mode
  • 排他锁:select...for update

1.2.2 锁粒度

提高共享资源并发性需要在锁的开销和数据安全性之间寻求平衡。每个存储引擎可以实现自己的锁策略和锁粒度。

表锁

是开销最小的策略,会锁定整张表,服务器会为alter table之类的语句使用表锁,而忽略存储引擎的锁机制。意向锁是一种特殊的表级锁,取得行的共享锁和排他锁之前,必须先获取意向共享锁和意向排他锁,用于判断表锁和行锁冲突。

行级锁

最大程度的支持并发处理,同时会带来最大的开销。

1.3 事务

事务是一组原子性的SQL,事务内的语句,要么全部执行成功,要么全部执行失败。事务的四大特性:

  • 原子性(atomicity): 整个事务中的所有操作要么全部提交成功,要么全部失败回滚,不可能只执行其中的一部分操作,避免脏读。
  • 一致性(consistency): 数据库总是从一个一致性状态转换到另外一个一致性状态,可以解决不可重复读问题。
  • 隔离性(isolation): 一个事务所做的修改在最终提交之前,对其他事务是不可见的。
  • 持久性(durability): 一旦事务提交,则其所做的修改会永久保存到数据库中,实际情况中不可能做到100%的持久性保证策略。

1.3.1 隔离级别

  • 未提交读(READ UNCOMMITTED): 事务可以读取未提交的数据,造成脏读。实际应用中很少使用。
  • 提交读(READ COMMITTED): 大多数数据库系统的默认隔离级别是提交读,一个事务开始和提交前,所做的修改对其他事务都是不可见的,可能会造成不可重复读。两个事务A和B,
  • 可重复读(REPEATABLE READ): 保证在同一个事务中多次读取同样记录的结果是一致的,但是会造成幻读,幻读指当某个事务在读物某个范围内的记录时,另外一个事务又在该范围内插入或删除记录,当之前的事务再次读取该范围时,会产生幻行。可重复读是MySQL默认的隔离级别。
  • 串行化(SERIALIZABLE): 最高的隔离级别,通过强制事务串行执行,会在读取的每一行数据上加锁,可能会导致大量的超时和锁争用问题。

1.3.2 死锁

死锁指两个或多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶行循环。为了解决死锁,数据库系统实现了各种死锁检测和死锁超时机制,InnoDB目前处理死锁,将持有最少行级排他锁的事务进行回滚。锁的行为和顺序是和存储引擎相关的。

1.3.3 事务日志

使用事务日志,存储引擎在修改表的数据时只需要修改其内存拷贝,再把修改行为记录到持久在硬盘上的事务日志中,而不用每次都将修改的数据本身持久到磁盘。事务日志采用的是追加的方式,顺序I/O。

1.3.4 MySQL中的事务

MySQL提供了两种事务型的存储引擎:InnoDB和NDB Cluster

  • 自动提交(AUTOCOMMIT)
  • 在事务中混合使用存储引擎:事务是由下层的存储引擎实现的,在同一个事务中,使用多种存储引擎是不可靠的。
  • 隐式和显示锁定:InnoDB采用两阶段锁定协议,在事务执行过程中,随时可以锁定,只有在commit或rollback时才会释放,且所有锁在同一时刻被释放,根据隔离级别自动加锁。InnoDB也支持显示锁定,见1.2.1。

1.4 多版本并发控制(MVCC)

MySQL的大多数事务型存储引擎实现的都不是简单的行级锁,基于提升并发性能的考虑,一般同时实现了多版本并发控制(MVCC)。可以认为MVCC是行级锁的一个变种,实现了非阻塞的读操作,写操作也只锁定必要的行。

MVCC是通过保存数据在某个时间点的快照来实现的。根据事务开始的时间不同,每个事务对同一张表,同一时刻看到的数据可能是不一样的。

不同的存储引擎的MVCC实现是不同的,典型的有乐观并发控制和悲观并发控制。InnoDB的MVCC,是通过在每行记录后面保存两个隐藏的列来实现的,这两个列,一个保存了行的创建时间,一个保存行的过期时间(或删除时间),存储的是系统版本号,没开始一个新的事务,系统版本号会自动递增。

MVCC只在可重复读和提交读两个隔离级别下工作。因为未提交读总是读取最新的数据行,而不是符合当前事务版本的数据行,而串行读则会对所有读取的行都加锁。

下面看一下可重复读隔离级别下,MVCC是如何操作的?

select

InnoDB会根据一下两个条件检查每行记录:
a.InnoDB只查找版本早于当前事务版本的数据行(即行的系统版本号小于或等于事务的系统版本号),保证事务读取的行,要么在事务开始前已经存在,要么是事务自身插入或修改过的。 b.行的删除版本要么未定义,要么大于当前事务版本号,确保事务读取到的行,在事务开始之前未被删除。
只有符合上述两个条件的记录,才能返回作为查询结果。

insert

InnoDB为新插入的每一行保存当前系统版本号作为行版本号。

delete

InnoDB为删除的每一行保存当前系统版本号作为行删除标识。

update

InnoDB为插入一行新纪录,保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为行删除标识。
保存这两个额外系统版本号,使得大多数读操作都可以不加锁,提高性能。不足之处是每行记录都需要额外的存储空间,需要做更多的行检查工作以及一些额外的维护工作。