第一层:连接处理、授权认证、安全等;
第二层:MySQL的核心层,包括查询解析、分析、优化、缓存以及所有的内置函数(例如,日期、日期、数学和加密函数),所有跨存储引擎的功能都在这一层实现:存储过程、触发器、视图等。
第三层:存储引擎,负责MySQL中数据的存储和提取。存储引擎API包含十几个底层函数,用于执行诸如"开始一个事务"或者"根据主键提取一行记录"等操作。但存储引擎不会去解析SQL,不同存储引擎之间也不会相互通信,而只是简单地响应上层服务器的请求。
连接管理:每个连接有一个单独的线程,这个连接的查询只会在这个单独线程中执行。MYSQL5.5提供了一个线程池API。
安全性:客户端连接服务器需要认证,基于用户名、原始主机信息和密码。如果使用了安全套接字SSL,可以使用证书认证。
MySQL会解析查询,并创建内部数据结构(解析器),然后对其进行各种优化,包括重写查询、决定表的读取顺序,以及选择合适的索引。对于SELECT语句,会先查缓存是否有该查询,有则直接返回结果。
分两个层面讨论服务器层与存储引擎层。
读锁是共享的,写锁是排他的。
锁的粒度控制太小,会也加大资源消耗(加锁,检查锁,释放)。所谓锁策略就是在锁的开销和数据安全性之间提供一种平衡,大多数数据库只是在表上施加行级锁,并以各种复杂方式实现,以便在锁比较多的情况下提供更好的性能。MySQL有更多选择,每种MySQL存储引擎都可以实现自己的锁策略和锁粒度。
表锁:开销最小的策略,尽管存储引擎可以管理自己的锁,MySQL本身还是会使用各种有效的表锁来实现不同的目的。例如,服务器会为诸如ALTER TABLE之类的语句使用表锁,而忽略存储引擎的锁机制。
行级锁:行级锁只在存储引擎实现,而MySQL服务器层没有实现。服务器层完全不了解存储引擎的锁实现。
ACID:atomicity、consistency、isolation、durability
原子性:不可分割的最小单元。
一致性:数据库总是从一个一致性状态转换到另一个一致性状态。
隔离性:通常来说,一个事务修改在最终提交之前,对其他事务时不可见的。
持久性:一旦事务提交,则其所做的修改就会永久保存到数据中。
未提交读:事务中的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,这也被称为脏读。
提交读(不可重复读):一个事务开始时,只能"看见"已经提交的事务所做的修改。对某一行数据,可能出现一个事务开始和末尾读到的数据不一致问题。
可重复读:可能出现幻读,即新增了某一行数据,但是之前没读到这行数据,出现了幻觉…
可串行化:强制事务串行化执行,最高隔离级别。
死锁是指两个或多个事务在同一资源上相互占用。
比如:
事务A,操作行1及行2
事务B,操作行2及行1
如果凑巧,都执行第一步,同时锁定了改行,则两个事务陷入死锁中。
数据库实现了各种死锁检测和死锁超时机制。越复杂的系统,比如InnoDB存储引擎,越能检测到死锁的循环依赖,并立即返回一个错误。InnoDB目前是等待锁超时后将持有最少行级排他锁的事务进行回滚。
锁的行为和顺序是和存储引擎相关的,有些死锁是因为真正的数据冲突,但有些是由于存储引擎实现方式导致的。
事务日志即存储引擎在修改表的数据时只需要修改其内存拷贝,再把该修改行为记录到持久在硬盘上的事务日志中,而不用每次都将修改的数据本身持久到磁盘。事务日志采用的是追加的方式,所以写事务日志的操作是磁盘上一小块区域内的顺序IO,而不像随机IO需要在磁盘多个地方移动磁头,所以采用事务日志的方式快得多。事务日志持久后,内存中被修改的数据在后台可以慢慢地刷回到磁盘。目前大部分数据库引擎是这样设计,成为预写式日志,修改数据需要写两次磁盘。
MySQL提供了两种事务型的存储引擎:InnoDB和NDB Cluster。
MySQL默认采用自动提交,如果不显式地开始一个事务,则每个查询都被当作一个事务执行提交操作。
对于非事务型的表,修改AUTOCOMMIT字段不会有任何影响。
如果在事务中操作了事务表和非事务表,正常提交不会有问题,但如果该事务需要回滚,非事务表的变更无法撤销。
InnoDB采用的是两阶段锁定协议。
在事务执行过程中,随时可以执行锁定,一个事务内的所有锁只有在COMMIT或者ROLLBACK的时候才会释放,并且所有锁匙同一时刻被释放。这属于隐式锁定。
也可以进行显式锁定。
同时MySQL也支持LOCK TABLES和UNLOCK TABLES语句,这是在服务层实现的,和存储引擎无关。
对于事务性表,不要使用LOCK TABLES, 这不但没有必要,还会严重影响性能。
MySQL的大多数事务型存储引擎实现都不是简单的行级锁。基于提升并发性能的考虑,它们一般都同时实现了多版本并发控制(MVCC)。它在很多情况下避免了加锁操作,开销更低。
InnoDB每行记录后面保存两个隐藏的列来实现,这两个列,一个保存了行的创建时间,一个保存了行的过期时间。时间只是一个代词,实际保存的是系统版本号。每开始一个事务,系统版本号会自动递增,事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行对比。
在REPEATABLE READ隔离级别下,MVCC具体是如何操作:
SELECT:
1.只查找行的创建版本号早于当前事务版本的数据行,这可以确保读取的行要么是在事务开始前已经存在,要么在事务自身中插入或者修改的。行的删除版本要么未定义,要么大于当前事务版本号。
INSERT
InnoDB为新插入的每一行保存当前系统版本号作为行版本号。
DELETE
InnoDB为删除的每一行保存当前系统版本号作为行删除标识。
UPDATE
InnoDB为插入一行新纪录,保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为行删除标识。
MVCC只在REPEATABLE READ和READ COMMITTED两个隔离级别下工作。
MySQL将每个数据库保存为数据目录下的一个子目录,创建表时,MySQL会在数据库子目录下创建一个和表同名的.frm文件保存表定义。
InnoDB采用MVCC来支持高并发,并且实现了四个标准的隔离级别。其默认级别是REPEATABLE READ(可重复读),并且通过间隙锁策略防止幻读的出现。间隙锁使得InnoDB不仅仅锁定查询涉及的行,还会对索引中的间隙进行锁定,以防止幻影行的插入。
InnoDB表示基于聚簇索引建立的。聚簇索引对主键查询有很高的性能,不过它的二级索引中必须包含主键列,所以如果主键列很大的话,其他的所有索引都会很大。因此,若表上的索引较多的话,主键应尽可能小。
简单归纳为一句:“除非需要用到某些InnoDB不具备的特性,并且要用到全文索引,建议优先考虑InnoDB加上Sphinx的组合,而不是使用支持全文索引的MyISAM。”
从下面几个方面考虑数据库引擎:
事务
如果需要事务,请优先考虑InnoDB。如果不需要事务吗,并且主要是SELECT和INSERT操作,那么MyISAM是不错的选择。一般日志型应用比较符合
备份
如果需要在线热备份,那么选择InnoDB就是基本要求。
崩溃恢复
数据量比较大的时候,系统崩溃后如何快速地回复是一个需要考虑的问题。相对而言,MyISAM崩溃后发生损坏的概率比InnoDB大得多,而且恢复速度也要慢。因此即使不需要事务支持,很多人也选择InnoDB引擎。
日志型应用
MyISAM或者Archive存储引擎对日志型应用比较合适,因为它们开销低,而且插入速度非常快。