目录


MySQL的架构与历史

  1. MySQL的逻辑架构
  2. 并发控制
  3. 事务
  4. 多版本并发控制
  5. MySQL的存储引擎
  6. 总结

一、MySQL的逻辑结构

逻辑结构主要有三层

  1. 第一层:不是mysql独有的,大多数基于网络的客户端、服务端的工具或者服务都有类似的架构,如连接处理、授权认证、安全等等
  2. 第二层:大多数mysql的核心功在这一层,包括查询解析、分析、优化、缓存以及所以的内置函数(如日期、数字、时间、加密函数等),所有存储引擎层的功能都在这一层实现(存储过程、触发器、视图等)
  3. 第三层:也就是存储引擎层,负责将MySQL中的数据存储和提取。和GNU、Linux下的文件系统一样每种存储引擎都有优缺点,上两层通过API和存储引擎层交互,这些接口屏蔽了不同存储引擎的差别,如API开始事务、根据主键提取一行记录等,但是存储引擎不会解析SQL,不同的存储引擎之间也不能相互通信

《高性能MySQL第3版》MySQL架构与历史_数据库

连接管理与安全性

  • 每个客户端连接都会在服务器进程拥有一个线程,这个连接的查询只会在这个单线程中,该线程只能轮流的在某个CPU核心或者CPU中运行
  • 当客户端连接到mysql服务器的时候,服务器需要对其认证。认证基于用户名、原始主机信息和密码。如果使用了安全套接诶子SSL的方式连接,还可以使用X.509整数认证

优化与执行

  • mysql会解析查询,并创建内部的数据结构(解析树),然后对其进行各种优化,包括重写查询、决定表的读取顺序,以及选择合适的索引等,用户可以使用特殊的关键字 hint 提示优化器,影响他的决策过程;也可以请求优化器解释 explain 优化过程的各个因素
  • 对于select查询,在解析查询前,服务器会先检查查询缓存,如果能找到,服务器直接就返回了不在进行查询解析、优化、执行过程

二、并发控制

无论何时,只要有多个查询在一个时刻修改数据,都会产生并发控制的问题,mysql两个层面的并发控制

  • 服务器层
  • 存储引擎层

读写锁

  • 在处理并发读或者写时候,可以通过实现一个由两种类型的锁组成的锁系统来解决问题。
  • 这两种类型的锁被称为共享锁、排它锁,也叫读锁、写锁

锁粒度

  • 一种提高共享资源并发型的方式就是让锁定对象更有选择性,尽量只多订需要修改的部分,而不是所有的资源,更理想的方式是只锁定需要修改的数据
  • 加锁也需要消耗资源,锁的各种操作都会增加系统的开销,花费大量资源管理锁而不是处理数据也是不可行的
  • 所谓锁策略就是在锁的开销和数据安全性之间寻求平衡,这种平衡会影响到性能,大多数商业数据库没有提供更多的选择,一般就是在表上加行级锁,并以复杂的方式实现
  • mysql提供了多种选择,每种mysql存储引擎都可以实现自己的锁策略和锁粒度

两种重要的锁策略

  • 表锁:基本的锁策略,也是开销最小的,写操作(插入、更新、删除)会锁定整张表,写锁是排它锁,读锁是共享锁,另外写锁的优先级高于读锁,尽管存储引擎可以管理自己的锁,mysql本身还是会用各种有效的表锁实现不同的目的。如服务器会为ALTER table之类的语句使用表锁
  • 行级锁:可以最大程度的支持并发处理,(同时也带来了最大的锁开销),在InnoDB和XtraDB以及一些存储引擎中实现了行级锁。在服务层没有实现,服务层完全不了解存储引擎层的锁实现。所有的存储引擎都以自己的方式实现了锁机制。

三、事务

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

  • 原子性:atomicity,一个事务必须被视为不可分割的最小工作单元,整个事务的所有操作要么全部都提交成功,要么全部失败回滚。
  • 一致性:consistency,数据库总是从一个一致性的状态转换到另外一个一致性的状态。
  • 隔离性:isolation,通常来说一个事务提交前,对其他事务是不可见的。
  • 持久性:durability,一旦事务提交,则其所做的修改就会永久的保存到数据库中,此时系统如果崩溃,数据也不会丢失。

隔离级别

  1. 未提交读 READ UNCOMMITTED:事务中的修改,即使未提交,对于其他事务也是可见的,可以读取到的不是最终结果的数据,也就是脏读,这个级别会导致很多问题,但也是性能最高的,实际使用很少。
  2. 提交读 READ COMMITTED:只有事务中提交之后的修改,才对其他事务是可见的,大多数数据库默认隔离级别就是提交读,但是MySQL不是,当前事务在多次读取数据的时候,可能中间穿插其他事务将数据修改了,即两次同样的查询,得出了不同的结果,称之为不可重复读,解决了脏读。
  3. 可重复读 REPEATABLE READ:MySQL的默认隔离级别,该级别通过读写锁保证了一个事务开启的同时,其他事务不能做修改操作(可以进行读操作、插入操作),因此解决了不可重复读,但是出现了幻读问题(当某个事务在读取某个范围的记录时,其他事务进行插入记录的操作),InnoDB和XtraDB存储引擎通过多版本并发控制MVCC解决了幻读问题
  4. 可串行化 SERIALIZABLE:最高的隔离级别,通过强制事务的串行执行(一个事务结束之后另一个事务才能开始),避免了前面出现的问题,简单来说就是SERIALIZABLE会在读取的每一行数据都加锁,所以可能导致大量的超时和锁征用,实际很少用。

在MySQL中可以设置隔离级别

  • set transcation isolation level xxx 来设置隔离级别,新的隔离级别在下一个事务开始时生效。
  • set session transcation isolation level READ COMMITED,临时设置当前会话的隔离级别。

死锁

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

  • 多个事务试图以不同的顺序锁定资源时候,可能会产生死锁
  • 多个事务同时锁定一个资源的时候,也会发生死锁

举栗

《高性能MySQL第3版》MySQL架构与历史_数据库_02

为了解决这个问题,数据库系统实现了各种死锁检测 和 死锁超时 机制,还有一种方式就是设置获取锁的超时等待,不过这种方式不太友好,InnoDB目前处理死锁的方式是:将持有最少行级锁的事务进行回滚(这是相对比较简单的死锁回滚算法)

MySQL中的事务

  1. MySQL提供了两种事务性的存储引擎:InnoDB 和 NDB Cluster,另外还有一些第三方的存储引擎也支持事务,常见的有XtraDB、PBTX
  2. 自动提交 AUTOCOMMIT:默认的模式,也就是如果不显示的开始一个事务,每次查询SELECT都被当做一个事务执行提交操作,可以通过show variables like 'AUTOCOMMITED';查看,value为ON表示开启。另外还有一些DDL命令、ALTER TABLE 执行之前,会强制执行COMMIT提交当前活动事务。
  3. 在事务中混合使用存储引擎:MySQL服务层不管理事务,事务都是在存储引擎层实现的,因此此种做法并不可靠。

隐式和显示锁定

  • InnoDB采用的是两阶段锁协议,在事务执行的时候,随时可以锁定,只有在执行提交、回滚的时候才会释放锁,并且所有的锁都是同一时刻释放的。
  • 隐式锁定:前面介绍的,会在隔离级别需要的时候自动加锁
  • 显示锁定:不属于SQL规范
  • select xxx lock in share mode
  • select xxx for update
  • 另外可以使用LOCK TABLES 和 UNLOCK TABLES语句,这是在服务层实现的,有自己的用途,并不能替代事务。不建议使用

四、多版本并发控制MVCC

MVCC概述

  • MySQL的大多数事务型存储引擎实现的都不是简单的行级锁,基于提高并发性能,他们都一般同时实现了多版本并发控制MVCC,像Oracle、PostgreSQL等其他数据库
  • 可以认为MVCC是行级锁的一个变种,但是他在很多情况下避免了加锁的操作,因此开销更低,虽然底层实现的机制有所不同,但大都实现了非阻塞的读操作,写操作也只锁定指定的行
  • MVCC的实现是通过保存数据在某个时间点的快照实现的,也就是说,不管需要执行多长时间,每个事务看到的数据都是一致的。根据事务开始的时间不同,每个事务对同一张表,同一时刻看到的数据可能是不一样的

不同存储引擎实现MVCC的方式是不同的,典型的有 乐观锁、悲观锁 并发控制

InnoDB的MVCC,是通过在每行记录后面保存两个隐藏的列实现的,这两个列

  • 一个保存行的创建时间
  • 一个保存行的过期时间(或删除时间)

当然存储的并不是实际的时间值,而是系统版本号(system version number)

  • 每开始一个新的事务,系统版本号就会递增
  • 事务开始的版本号会作为当前事务的版本号,用来和查询到的每行记录的版本号进行比较

具体操作

  • select:InnoDB会根据下面两个条件检查每行记录。1、查找版本早于当前事务版本的记录,保证查找的行在事务开始就是存在的,要么就是当前事务自身插入、修改过的。2、记录的删除版本要么未定义,要么大于当前事务版本号,可以保证读取到的行在事务开始前没有被删除。
  • insert:设置记录的版本号为当前事务版本号
  • delete:设置记录的删除版本号为当前事务版本号
  • update:插入一条新的记录,之前的行删除版本号设置为当前事务版本号最为删除标志,新纪录的版本号为当前事务版本号

保存着两个额外的系统版本号,使大多数读操作可以都不用加锁,这样设计可以使得读操作很简单,性能很好,并且能保证只会读取到符合标准的行,不足就是每行记录都需要额外的存储空间,需要更多的行检查操作以及一些额外的维护工作

MVCC只在 重复读 和 提交读 两种隔离级别下工作。

五、存储引擎

在文件系统中,MySQL将每个数据库(schema)保存为数据目录下的一个子目录。创建表的时候,会在子目录下创建一个 表名.frm 文件保存该表的定义,也就是使用文件系统的子目录和文件保存数据库和表的定义,大小写敏感,表的定义是在服务层统一处理的。

1、InnoDB引擎

概述

  • 采用MVCC支持高并发,并且实现了四个标准的隔离级别,默认级别是 可重复读,并且通过间隙锁(next-key locking)策略防止幻读的出现,间隙锁使得InnoDB不仅锁定查询涉及的行,还会对索引中的间隙进行锁定,防止幻行的插入。
  • InnoDB是基于聚簇索引建立的(二级索引包含主键列)

更多待更新…