高性能MySql学习笔记-第一章:MySQL 架构与历史
1. MySql逻辑架构
- MySQL架构图一般有如下的分层:
- 第一层的服务主要处理连接处理,授权认证,安全等等。
- 第二层服务是MySQL的核心服务,包括解析、分析、优化、缓存及所有的内置函数,所有跨存储引擎的功能都在这一层实现:存储过程,触发器,视图等。
- 第三层是存储引擎(如InnoDB)。存储引擎负债MySQL中数据的存储和提取。服务器通过API和存储引擎进行通信,而无需关注存储引擎的具体实现。
- MySQL会解析查询,然后进行各种优化以加快查询速度。可以通过关键字
hint
提示优化器,影响其决策过程。也可以通过关键字explain
,请求优化器解释优化过程的各个因素。
2. 并发控制
- 表锁: 表锁是MySQL中最基本的锁,也是开销最小的锁策略。一个用户进行写操作时,会对整张表加一个写锁,阻塞其他用户对这张表的读写操作。
- 行级锁:行级锁可以最大程度地支持并发处理,但也带来了最大的锁开销。大多数数据库都是在表上施加行级锁。行级锁只在存储引擎层实现。
3. 事务
-
事务是一组原子性的SQL查询,要么全部成功执行,要不全部不执行。
-
一个合格良好的事务处理系统,必须严格通过ACID测试。ACID是指:
- 原子性(Atomicity)
原子性是指事务是一个不可分割的最小工作单位。一个事务要么全部提交成功,要么全部失败回滚,不能只执行其中的一部分操作,这就是事务的原子性 - 一致性(Consistency)
事务的执行不能破坏数据库数据的完整性和一致性,一个事务在执行之前和执行之后,数据库都必须处于一致性状态。
如果数据库系统在运行过程中发生故障,有些事务尚未完成就被迫中断,这些未完成的事务对数据库所作的修改有一部分已写入物理数据库,这是数据库就处于一种不正确的状态,也就是不一致的状态 - 隔离性(Isolation)
事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,在最终提交之前,对其他事务是不可见的,多个并发事务之间要相互隔离。 - 持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响
- 原子性(Atomicity)
-
针对上述的隔离性,SQL标准中定义了四种隔离级别:
- 读未提交(READ UNCOMMITED):
该级别是指,即便事务中的修改还没有提交,其他的事务也可以读取到未提交的数据。
这个隔离级别会有脏读(dirty read)的问题,即:A事务读取B事务尚未提交的数据,此时如果B事务发生错误并执行回滚操作,那么A事务读取到的数据就是脏数据。 - 读提交(READ COMMITED):
该级别是指,一个事务开始之后,只能看见已经提交的事务所作的修改。
这个隔离级别解决了脏读的问题,但仍然有不可重复读的(no repeatable read)问题,即:事务A读取数据x后,事务B对数据x进行更新,事务A再次读取时,读取的结果与前一次的不同。 - 可重复读(REPEATABLE READ):
该级别是指,保证在同一个事务中多次读取同样一条记录的结果是一致的。可重复读是 MySQL 默认的隔离级别。
这个隔离级别解决了不可重复读的问题,但仍有幻象读(phantom read)的问题,即:事务A在进行两次范围查询的过程中,事务B对数据进行了插入或删除,导致事务A两次查询的记录数不一致。InnoDB 和 XtraDB 通过多版本并发控制(MVCC)解决幻象读的问题。 - 可串行化(SERIALIZABLE):
该级别是最高的隔离级别,强制事务串行执行,不会有事务的并发问题。但是可能导致大量的超时和抢锁问题。
- 读未提交(READ UNCOMMITED):
-
表:ANSI SQL 隔离级别 :
隔离级别 脏读 不可重复读 幻象读 加锁读 读未提交 YES YES YES NO 读提交 NO YES YES NO 可重复读 NO NO YES NO 可串行化 NO NO NO YES -
死锁
死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方占用的资源。
InnoDB引擎目前处理死锁的方案是:将持有最少行级排他锁的事务进行回滚。 -
事务日志
事务日志可以提高事务的效率。存储引擎修改表的数据时,只需要修改内存中的数据,并把该条修改记录持久化到硬盘的事务日志中。
由于事务日志采用的是追加的方式,所以写日志的操作是对磁盘而言是顺序 IO,速度相对快很多。事务日志持久化以后,内存中被修改的数据可以慢慢地刷回到磁盘。
一旦数据库崩溃,内存中的数据丢失,可以通过已经持久化的事务日志进行恢复,具体恢复方式视存储引擎而定。 -
MySQL 中的事务
MySQL 提供了两种事务引擎:InnoDB 和 NDB Cluster-
自动提交(AUTOCOMMIT)
MySQL 默认采用自动提交模式,就是说,除非显式地开启一个事务,则每个查询都会被当做一个事务执行提交操作。
在当前链接中,可以通过设置AUTOCOMMIT变量来启用或禁用自动提交模式。当自动提交模式关闭时,所有的查询都在一个事务中,直到显式地执行 COMMIT 提交或 ROLLBACK 回滚。
-
隐式锁定和显式锁定
InnoDB 采用的是两阶段锁定协议,在事务执行过程中,随时都可以执行锁定,但是只有在执行 COMMIT 或者 ROLLBACK 时才会释放锁,并且所有的锁都是同时释放。这些锁定都是隐式锁定,InnoDB 会根据隔离级别在需要的时候自动加锁。
InnoDB 也支持通过特定的语句进行显式锁定,但是这些语句不属于 SQL 规范。实际操作中应尽量避免使用此类语句。
-
4. 多版本并发控制(MVCC, Multiversion Concurrency Control)
- 绝大多数事务引擎都不是简单的行级锁,基于并发性能的考虑,往往实现了多版本并发控制(MVCC)。多版本并发控制的实现,是通过保存数据在某个时间点的快照实现的。即每个事务开始执行后,执行期间看到的数据都是一致的。开始时间不同的事务,同一时刻对同一张表看到的数据可能不同。
- 不同事务引擎的MVCC实现有所不同,典型的有乐观并发控制和悲观并发控制。
- InnoDB的MVCC是通过在每一行记录后保存两个隐藏的列实现的。这两列,一个保存行的创建版本号,一个保存行的过期版本号(删除版本号)。版本号和事务相关,没开始一个事务,版本号自动加一。在可重复读的隔离级别下,InooDB的MVCC具体操作如下:
- SELECT
a. 查找行的版本号小于等于当前事务的版本号的行数据。这样确保事务读取的行,要么是事务开始前已经存在的,要么是事务本身修改的。
b. 查找行的删除版本号大于当前事务的版本号,或者无删除标记的行数据。这样确保事务读取到的行,在事务开始之前未被删除。
以上两部分数据取交集,即为当前事务的查询结果。 - INSERT
每插入一行新的数据则保存当前事务的系统版本号作为该行创建版本号 - DELETE
每删除一行数据则保存当前事务的系统版本号作为该行的删除版本号 - UPDATE
插入一行新的数据,保存当前事务的系统版本号作为该行创建版本号,同时保存当前事务的系统版本号作为原来行的删除版本号。
- SELECT
- MVCC只在读提交和可重复读两个隔离级别下工作
5. MySQL的存储引擎
-
MySql中表的定义是在服务层统一处理的,可以使用
SHOW TABLE STATUS
命令查看表相关信息。对于MyISAM存储引擎而言,表中的行数是精确值,而对于InnoDB引擎而言,该值是估计值。 -
InnoDB存储引擎
- InnoDB的数据存储在表空间中,表空间是由一系列文件组成。
- InnoDB采用MVCC支持高并发,并且实现了四个标准的隔离级别。InnoDB的默认级别是可重复读,并且通过间隙锁策略防止幻读的出现。
- InnoDB表是基于聚簇索引建立的,聚簇索引对主键查询有很高的性能,不过它的二级索引(非主键索引)中必须包含主键列,所以如果主键列很大的话,其他索引都会很大。InnoDB的存储格式是平台无关的,即可以在不同平台间迁移。
-
MyISAM存储引擎
- MyISAM是MySQL5.1版本及之前的默认存储引擎。MyISAM有如下一些特性:全文索引、压缩、空间函数等。但是MyISAM不支持事务和行级锁,崩溃后无法安全恢复。
- MyISAM对整张表加锁,而不是针对行。读取时会对读到的所有表加共享锁,写入时则对表加排他锁。
- MyISAM压缩表适合在创建并导入数据后,不会再进行修改操作的表。因为压缩表被压缩,极大的减小了磁盘空间占用和磁盘IO,从而提升查询性能。
-
MySQL中内建的其他引擎
- Archive引擎只支持INSERT和SELECT操作,每次SELECT查询都需要执行全表扫描,所以比较适合日志类和数据采集类应用。
- CSV引擎可以将普通的CSV文件作为MySQL的表来处理,这种表不支持索引,其他软件如Excel可以打开其文件,反之亦可。
- Memory引擎所有的数据都保存在内存中,查找操作非常快,但因为是表级锁,所以并发写入性能较低。Memory表结构在重启后仍会保留,但数据会丢失。
-
第三方存储引擎
- 事务类引擎有XtraDB,PBXT,TokuDB等。面向列的存储引擎有InfoBright,在大数据量(数十TB)时工作良好。
-
如何选择合适的存储引擎
- 除非需要用到InnoDB不具备的特性且没有其他办法可以替代,否则都应该优先选择InnoDB。
- 选择不同的存储引擎时需要考虑如下的几个因素:对事务的支持、备份、崩溃后的恢复、特有的特性。
- 日志型应用可以选择MyISAM或Archive存储引擎,因为它们开销低且插入速度非常快。读多写少的业务,在不介意MyISAM的崩溃恢复问题的情况下,可以选择MyISAM。
- 订单处理类应用需要对事务处理的支持,因此应该优先选择InnoDB。
- 如果是10TB以上的大数据量,可能需要建立数据仓库,可以选择InfoBright或者TukoDB。
-
转换表的引擎的三种方式
- 使用ALTER TABLE语句修改表的信息,但是该语句需要执行很长时间,且会复制原表到新表中。
- 可以用mysqldump工具将原表数据导出到文件,修改文件中的CREATE TABLE语句的存储引擎选项,修改表名后,导入到数据库中。
- 创建一个新的存储引擎的表,然后利用INSERT···SELECT的语法来导数据。