目录
MySQL的架构与历史
- MySQL的逻辑架构
- 并发控制
- 事务
- 多版本并发控制
- MySQL的存储引擎
- 总结
一、MySQL的逻辑结构
逻辑结构主要有三层
- 第一层:不是mysql独有的,大多数基于网络的客户端、服务端的工具或者服务都有类似的架构,如连接处理、授权认证、安全等等
- 第二层:大多数mysql的核心功在这一层,包括查询解析、分析、优化、缓存以及所以的内置函数(如日期、数字、时间、加密函数等),所有存储引擎层的功能都在这一层实现(存储过程、触发器、视图等)
- 第三层:也就是存储引擎层,负责将MySQL中的数据存储和提取。和GNU、Linux下的文件系统一样每种存储引擎都有优缺点,上两层通过API和存储引擎层交互,这些接口屏蔽了不同存储引擎的差别,如API开始事务、根据主键提取一行记录等,但是存储引擎不会解析SQL,不同的存储引擎之间也不能相互通信
连接管理与安全性
- 每个客户端连接都会在服务器进程拥有一个线程,这个连接的查询只会在这个单线程中,该线程只能轮流的在某个CPU核心或者CPU中运行
- 当客户端连接到mysql服务器的时候,服务器需要对其认证。认证基于用户名、原始主机信息和密码。如果使用了安全套接诶子SSL的方式连接,还可以使用X.509整数认证
优化与执行
- mysql会解析查询,并创建内部的数据结构(解析树),然后对其进行各种优化,包括重写查询、决定表的读取顺序,以及选择合适的索引等,用户可以使用特殊的关键字
hint
提示优化器,影响他的决策过程;也可以请求优化器解释explain
优化过程的各个因素 - 对于select查询,在解析查询前,服务器会先检查查询缓存,如果能找到,服务器直接就返回了不在进行查询解析、优化、执行过程
二、并发控制
无论何时,只要有多个查询在一个时刻修改数据,都会产生并发控制的问题,mysql两个层面的并发控制
- 服务器层
- 存储引擎层
读写锁
- 在处理并发读或者写时候,可以通过实现一个由两种类型的锁组成的锁系统来解决问题。
- 这两种类型的锁被称为共享锁、排它锁,也叫读锁、写锁
锁粒度
- 一种提高共享资源并发型的方式就是让锁定对象更有选择性,尽量只多订需要修改的部分,而不是所有的资源,更理想的方式是只锁定需要修改的数据
- 加锁也需要消耗资源,锁的各种操作都会增加系统的开销,花费大量资源管理锁而不是处理数据也是不可行的
- 所谓锁策略就是在锁的开销和数据安全性之间寻求平衡,这种平衡会影响到性能,大多数商业数据库没有提供更多的选择,一般就是在表上加行级锁,并以复杂的方式实现
- mysql提供了多种选择,每种mysql存储引擎都可以实现自己的锁策略和锁粒度
两种重要的锁策略
- 表锁:基本的锁策略,也是开销最小的,写操作(插入、更新、删除)会锁定整张表,写锁是排它锁,读锁是共享锁,另外写锁的优先级高于读锁,尽管存储引擎可以管理自己的锁,mysql本身还是会用各种有效的表锁实现不同的目的。如服务器会为
ALTER table
之类的语句使用表锁 - 行级锁:可以最大程度的支持并发处理,(同时也带来了最大的锁开销),在InnoDB和XtraDB以及一些存储引擎中实现了行级锁。在服务层没有实现,服务层完全不了解存储引擎层的锁实现。所有的存储引擎都以自己的方式实现了锁机制。
三、事务
事务就是一组原子性的SQL查询,或者说是一个独立的工作单元,也就是说事务内的语句要么全部执行成功,要么全部执行失败。事务的ACID特性
- 原子性:atomicity,一个事务必须被视为不可分割的最小工作单元,整个事务的所有操作要么全部都提交成功,要么全部失败回滚。
- 一致性:consistency,数据库总是从一个一致性的状态转换到另外一个一致性的状态。
- 隔离性:isolation,通常来说一个事务提交前,对其他事务是不可见的。
- 持久性:durability,一旦事务提交,则其所做的修改就会永久的保存到数据库中,此时系统如果崩溃,数据也不会丢失。
隔离级别
- 未提交读 READ UNCOMMITTED:事务中的修改,即使未提交,对于其他事务也是可见的,可以读取到的不是最终结果的数据,也就是脏读,这个级别会导致很多问题,但也是性能最高的,实际使用很少。
- 提交读 READ COMMITTED:只有事务中提交之后的修改,才对其他事务是可见的,大多数数据库默认隔离级别就是提交读,但是MySQL不是,当前事务在多次读取数据的时候,可能中间穿插其他事务将数据修改了,即两次同样的查询,得出了不同的结果,称之为不可重复读,解决了脏读。
- 可重复读 REPEATABLE READ:MySQL的默认隔离级别,该级别通过读写锁保证了一个事务开启的同时,其他事务不能做修改操作(可以进行读操作、插入操作),因此解决了不可重复读,但是出现了幻读问题(当某个事务在读取某个范围的记录时,其他事务进行插入记录的操作),InnoDB和XtraDB存储引擎通过多版本并发控制MVCC解决了幻读问题
- 可串行化 SERIALIZABLE:最高的隔离级别,通过强制事务的串行执行(一个事务结束之后另一个事务才能开始),避免了前面出现的问题,简单来说就是SERIALIZABLE会在读取的每一行数据都加锁,所以可能导致大量的超时和锁征用,实际很少用。
在MySQL中可以设置隔离级别
-
set transcation isolation level xxx
来设置隔离级别,新的隔离级别在下一个事务开始时生效。 set session transcation isolation level READ COMMITED
,临时设置当前会话的隔离级别。
死锁
死锁指的是两个或者多个事务在同一资源上相互占用,并且请求锁定对方占用的资源,从而导致恶性循环的现象
- 多个事务试图以不同的顺序锁定资源时候,可能会产生死锁
- 多个事务同时锁定一个资源的时候,也会发生死锁
举栗
为了解决这个问题,数据库系统实现了各种死锁检测 和 死锁超时 机制,还有一种方式就是设置获取锁的超时等待,不过这种方式不太友好,InnoDB目前处理死锁的方式是:将持有最少行级锁的事务进行回滚(这是相对比较简单的死锁回滚算法)
MySQL中的事务
- MySQL提供了两种事务性的存储引擎:InnoDB 和 NDB Cluster,另外还有一些第三方的存储引擎也支持事务,常见的有XtraDB、PBTX
- 自动提交 AUTOCOMMIT:默认的模式,也就是如果不显示的开始一个事务,每次查询SELECT都被当做一个事务执行提交操作,可以通过
show variables like 'AUTOCOMMITED';
查看,value为ON表示开启。另外还有一些DDL命令、ALTER TABLE 执行之前,会强制执行COMMIT提交当前活动事务。 - 在事务中混合使用存储引擎: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是基于聚簇索引建立的(二级索引包含主键列)
更多待更新…