和其他数据库相比,MySQL也许幷不完美,但是他的灵活能让他在不同的场景中应用幷发挥良好,而这要归结其架构,其独特的存储引擎架构更是其丰富功能及灵活性的重要来源。本章主要描述了MySQL的服务器架构、各种存储引擎之间的主要区别、以及这些区别的重要性。
1.1MySQL逻辑架构
上图为MySQL服务器的主要逻辑架构图,如图所展示,MySQL架构主要分为三层
- 第一层是和大多数基于网络客户端/服务端的工具或者服务类似的架构,用户处理网络连、授权认证和安全等
- 第二层是MySQL所有跨搜索引擎功能的实现出,包括查询解析、分析、优化、缓存及内置函数,我们常常进行sql的优化及分析就是基于第二层提供的功能
- 第三次存储引擎层,其灵活性主要就来源于存储引擎层灵活的个性化选择,MySQL服务器和存储引擎通过API进行通信,这极大的降低了存储引擎和服务器的耦合关系,使得用户可以方便的根本业务场景和各个存储引擎的特点选择最适合自己的存储引擎,达到个性化的目的。
1.2 并发控制
1.2.1 锁
数据库同一时刻会存在多个连接同时对数据进行操作,如果多个线程对同一个数据进行修改和查询,这就涉及到了并发的问题,MySQL主要从两个层面进行并发控制:服务处和存储引擎层。
MySQL的并发控制主要通过共享锁(读锁)和排它锁(写锁)组成的锁系统来进行,主要的实现思路是保证只存在读时,各个线程互不干扰,但在写的情况下,保证只有一个用户能进行写入,幷防止其他线程进行读写。
1.2.2 锁粒度
锁可以有效的处理并发问题,但是锁也带来了性能的损耗,而且锁的粒度越大,锁消耗性能越小,但是粒度越小,并发处理支持程度越高,所以这就需要在锁的开销和数据安全之间进行平衡,相比于大多数只提供行级锁的数据库,MySQL提供了多种选择,这些锁粒度和锁策略则是由MySQL服务器和存储引擎共同来实现的,也就是说你可以根据不同的需要通过使用合适的存储引擎来决定MySQL使用的锁策略和锁粒度
最重要的锁策略为表锁和行级锁
- 表锁:MySQL最基本的锁策略,也是开销最小的策略,一个用户获得写锁后进行写操作会阻塞对这张表进行读写的所有用户,MySQL服务器自身实现的就是表锁,而且,根本其本身的优化策略,有时候会忽略存储引擎的锁机制
- 行级锁:行级锁可以最大程度的支持并发操作,但同时也带来了最大的所开销,行级锁只在搜索引擎层实现,MySQL服务器层没有进行实现,并且所有的存储引擎都以自己的方式进行锁机制实现,比如InnoDB实现了行级锁。
1.3事务
事务就是一组原子性的SQL操作,一个事务内,所有语句要么全部成果、要么全部失败,事务的作用就是将多个单独的原子操作组合成一个原子操作。一个运行良好的事务处理系统必须具备ACID四个特性
- A-原子性(atomicity)一个事务被视为一个原子操作,事务内的所有操作,要么同时成果、要么同时失败,不会出现第三种情况
- C-一致性(consistency)涉及操作的数据总是从一个一致的转态转移到另一个一致性的状态,比如转账,总是一个账户扣钱,一个账户加钱,但两个账户的总额总是不变的。4
- I-隔离性(isolation)隔离性指的是多个事务之间,事务对非本事务操作所能感知的程度,比如两次转账,隔离性可以控制两次转账之间,对另一个转账的金额感知的时机,不如转账完成前,转账完成后。
- D-持久性(duration)一次事务的提交,所做的修改需要永久的保持到数据库中,比如落地到磁盘。但持久性也分多个级别,需要不同的策略来保证数据的安全落地。
1.3.1隔离级别
在事务中提到了隔离性,这里的隔离级别是对隔离性实现更细致的划分,在SQL标准中共定义了四种隔离级别,每种存储引擎实现隔离级别也是不一样的,隔离级别划分如下
隔离级别 | 说明 | 脏读 | 不可重复读 | 幻读 | 加锁读 |
read uncommitted | 在事务的修改中,即使没有提交,对其它事务也都是可见的,比如一次转账,还只进行了扣款,还未往另一个账户加钱,但如果此时又进行一次转账,虽然上一次转账还未成果,这次转账也可能因为余额不足尔无法发起 | YES | YSE | YES | NO |
read committed | 在事务的修改中,只要没有提交,其它事务就不能看见因为你的操作所造成的改变,也就是上个例子中,你可以成功的开始第二次转账。 | NO | YES | YES | NO |
repeatable read | 保证同一个事务中多次读取同样记录的结果是一致的,这也是MYSQL默认的隔离级别,且InnoDB通过MVCC解决了幻读的问题 | NO | NO | YES | NO |
serializable | 最高的隔离级别,通过串行的方式执行事务,相当于就是把多线程变为的单线程,纯粹是牺牲性能换取数据强一致性 | NO | NO | NO | NO |
脏读:读到未提交的数据,并且,最终数据也没有提交,导致读到不存在的数据
不可重复读:同一条数据,两次读取的数据不同
幻读:两次读取到的数据不一样,第一次不存在第二次读存,或者第一次存在,第二次不存在
1.3.2死锁
死锁的出现,会导致相关资源无法释放,导致事务永远阻塞,为了解决这种问题,数据库系统实现了各种死锁检测和死锁超时机智,比如InnoDB存储引擎,会自动检测到死锁的循环依赖,幷立即返回错误。InnoDB目前解决死锁的方法是:将持有最少行级排它锁的事务进行回滚。需要注意的是,锁的行为和顺序是和存储引擎相关的,所以在不同的存储引擎下,可能有的会出现死锁,有的则不会。
1.3.3事务日志
事务日志可以帮助提高事务的效率,在使用事务日志,在修改表数据时,只需要修改内存的拷贝幷持久化到事务日志中,而不用每次修改数据本身持久到磁盘,因为事务日志采用追加的方式,因此写日志使用的是顺序IO,相比于直接修改数据的随机IO,可以加快事务的执行效率,然后可以在后台慢慢的根据事务日志更新数据本身。
1.3.4 MySQL中的事务
MySQL的事务支持是由搜索引擎来实现的,MySQL主要提供了InnoDB和NDB Cluster来支持事务,平时我们使用最多的就是InnoDB了。MySQL默认采用的是自动提交模式,也就是说每执行一次sql,MySQL会自动进行提交,我们可以通过SET AUTOCOMMIT = 1或者OFF来禁用,然后通过执行commit来手动提交事务。由于事务主要由存储引擎实现,而我们可以为每张表选择不同的存储引擎,这就导致有可能出现一个事务中,存在使用不同存储引擎的表,需要回滚是,由于非事务性表存在,会出现部分回滚失败的情况,这是需要注意的
InnoDB通过锁来满足事务的隔离级别需求,在事务开始执行过程中执行锁定,在提交或者回滚后释放释放锁,这完全由InnoDB来实现,对用户透明,但有时候,我们希望自己控制资源的锁定,这时候我们可以运用InnoDB的特性来进行显式锁定
- SELECT ... LOCK IN SHARE MOOD
- SELECT ... FOR UPDATE
在MySQL服务器层面也支持使用LOCK TABLES 和UNLOCK TABLES进行锁定,但尽量还是使用存储引擎层面提供的锁特性,防止由于LOCK TABLES和事务之间(存储引擎级别的锁)的相互影响导致复杂的情况出现
1.4 多版本并发控制(MVCC)
前面所MySQL默认使用的是repeatable read的隔离级别,但在该隔离级别下,会出现幻读的情况,InnoDB就是通过MVCC来解决repeatable read级别下的幻读问题的,那MVCC究竟是什么呢?
MVCC幷不是InnoDB特有的事务属性,可以认为它是行级锁的一个变种,处理MySQL,其他的数据库系统也都是些了MVCC,比如oracle,MVCC主要通过保持数据在某个时间点的快照来保证一个事务看到的数据总是一样的下面通过innoDB来理解MVCC的工作原理
innoDB的MVCC是通过在每行记录后面保持两个隐藏的列来实现的,这两个列,一个保存了行创建时的版本号,一个保存行删除时的版本号,每开始一个新事物,系统版本号就会自动递增,这就保证每个事物都有自己特有的版本号,然后结合隐藏的列字段,就可以进行查询记录的控制了,在repeatable read隔离级别下 MVCC的操作过程如下
SELECT | 1.只查找版本早于当前事物版本的数据行 2.只读在事务开始之前未被删除的数据 |
INSERT | 为新插入的每一行保存当前系统版本号作为行版本号 |
DELETE | 为删除的每一行保存当前版本号作为删除标识 |
UPDATE | 1.插入一行新的记录,保存当前系统版本号作为行版本号 2.保存当前系统版本号作为旧数据的删除版本号 |
通过以上规则,就能保证事务读取到的,永远是在其开始之前的事务。使用这种方式,可以减少很多锁操作,进而提升性能,但也因为需要隐式的存储版本号而需要消耗多余的空间。
1.4存储引擎
前面说过,MySQL的架构使其具有优越的灵活性,而存储引擎是其灵活性的重要来源,因为MySQL服务器和存储引擎通过API交互,这种低耦合的架构,可以让我们轻而易举的选择各种存储引擎,而MySQL也确实有很多可供我们选择的引擎,其中InnoDB是MySQL的默认事务型引擎,也是最重要、使用最广泛的存储引擎,innoDB有如下一些特性
- 支持处理大量的短期事务
- 高性能以及自动崩溃恢复特性
- 利用顺序创建索引、创建和删除索引时不需要复制全表数据
- 支持压缩的存储格式
- 采用MVCC支持高并发,幷实现了四个隔离级别
除了innoDB,MySQL还有很多其他在不同的业务场景可以工作的更好的存储引擎,以下为其他部分引擎和其比较合适的适用场景总结
存储引擎 | 特性 | 场景 |
MyISAM | 全文索引 压缩存储 空间函数 延迟更新索引 | 日志型应用,对插入速度要求高,且可接受数据丢失的场景 |
Archive | 缓存所有的写 zlib压缩 | 日志和数据采集类应用 |
CVS | 支持将普通文件作为表来处理,可以将Excel等电子表格软件中的数据存储为CVS文件,然后复制到MySQL数据目录下,就能直接作为表使用 | 数据交换机制 |
Memory | 所有数据保存在内存中 | 1.用于查找或者映射表 2.用于周期性聚合数据的结果 3.用于保持数据分析中产生的中间数据 |
MySQL除了官方认证的存储引擎,还有许多社区提供的第三方引擎,有些引擎在它涉及的藏家中确实能表现的很好,虽然有这么多的引擎可以选择,但是在选择引擎时,请记住一句话:除非需要用到某些innoDB不具备的特性,并且没有其他办法可以替代,否则都应该优先选择innoDB引擎。