最上层的服务,连接和线程处理。比如连接处理、授权认证、安全等
第二层架构,MySQL的核心服务功能都在这一层,包括查询解析、分析、优化、缓存以及所有的内置函数(例如,日期,时间
第三层包含了存储引擎,服务器通过API
与存储引擎进行通信,不同存储引擎之间也不会互相通信
在第二层当中,服务器会先检查缓存,如果存在就不会执行查询解析、优化和执行的整个过程
所以查询缓存是在查询解析、优化和执行之前进行的
在处理并发都或者写时,可以通过实现一个由两种类型的锁组成的锁系统来解决问题,这两种类型的锁通过被称为
共享锁
(shared lock)和排他锁
(exclusive lock),也叫读锁
(read lock)和写锁
(write lock)
读锁是共享的,或者说是互相不阻塞的,多个客户在同一时刻可以同时访问同一个资源,而互不干扰
写锁则是排他的,也就是说一个写锁会阻塞其他的写锁和读锁
一种提高共享资源并发性的方式是让锁定对象各有选择性,尽量之锁定需要修改的部分数据,而不是所有数据。更理想的方式是,只对会修改的数据片进行精确的锁定。
所谓的锁策略,就是在锁的开销和数据的安全性之间寻求平衡,这种平衡也会影响到性能
大多数商业数据库系统,都是在表上施加行级锁(row-level lock)
表锁(table lock)
表锁是MySQL中最基本的锁策略,并且是开销最小的策略。一个用户在对表进行写操作的时候,需要先获得写锁
,这会阻塞其他用户对该表的所有读写操作
,只有没有写锁的时候,其他读取的用户才能获得读锁,读锁之间是不互相阻塞
的
写锁具有比读锁更高的优先级,一个写锁请求可能会插入到读锁队列的前面
行级锁(row lock)
行级锁可以最大程度地支持并发处理,同时也带来了最大的锁开销
事务就是一组原子性的SQL查询,事务内的语句,要么全部执行成功,要么全部执行失败
事物的ACID
原子性(atomicity):事务必须被视为一个不可分割的最小工作单元,整个事务的所有操作要么全部提交成功,要么全部失败回滚
一致性 (consistency):数据库总是从一个一致性的状态转换到另外一个一致性的状态。事务没有最终提交,事务中所作的修改也不会被保存到数据库中
隔离性(isolation):一个事务所作的修改在被最终提交以前,对其他事务是不可见的
持久性(durability):一旦事务提交,则其所作的修改就会永久保存到数据库中
隔离性远比想象中的复杂,在SQL表中中定义了四种隔离级别
READ UNCOMMITTED (未提交读):事务可以读取未提交的数据,这也被称为脏读(Dirty Read)
READ COMMITTED (提交读):大多数数据库系统默认的隔离级别都是READ COMMITTED
(但是MySQL不是,MySQL是 REPEATABLE READ
)。一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的
REPEATABLE READ(可重复读):该级别保证了在同一个事务中多次读取同样记录的结果是一致的。理论上可重复读无法解决幻读(Phantom Read)问题。可重复读是MySQL的默认事务隔离级别
SERIALIZABLE(可串行化):SERIALIZABLE是最高的隔离级别。它通过强制事务串行执行,避免了幻读问题。简单来说,SERIALIZABLE会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用问题。实际中很少用到这个隔离级别
死锁是指两个或者多个事务在同一资源上互相占用,并请求锁定对方占用的资源,从而导致恶性循环的现象
数据库系统实现了各种死锁检测和死锁超时机制。
死锁检测,比如InnoDB存储引擎,能检测到死锁的循环依赖,并立即返回一个错误
死锁超时(不太好),当查询的时间达到锁等待超时的设定后放弃锁请求
目前InnoDB处理死锁的方法是,将持有最少行级排他锁的事务进行回滚
大多数情况下只需要重新执行因死锁回滚的事务即可
事务日志是一个与数据库文件分开的文件。它存储对数据库进行的所有更改,并全部记录插入、更新、删除、提交、回退和数据库模式的变化
事务要保证ACID的完整性必须依靠事务日志做跟踪,每一个操作在真正写入数据数据库之前,先写入到日志文件中。如要删除一行数据会先在日志文件中将此行标记为删除,但是数据库中的数据文件并没有发生变化。只有在(包含多个sql语句)整个事务提交后,再把整个事务中的sql语句批量同步到磁盘上的数据库文件。
修改数据需要写两次磁盘
在事务引擎上的每一次写操作都需要执行两遍:
先写入日志文件中。
写入日志文件中的仅仅是操作过程,而不是操作数据本身,所以速度比写数据库文件速度要快很多
然后再写入数据库文件中
写入数据库文件的操作是重做事务日志中已提交的实务操作的记录
事务日志的好处
事务日志采用的追加的方式,因此写日志的操作是磁盘一小块区域的顺序IO,而不像随机IO需要在磁盘上多个地方移动,所以采用事务日志的方式,相对来说快很多,事务日志持久后,内存中的修改在后台慢慢刷回磁盘,期间如果系统发生崩溃,存储引擎在重启的时候依靠事务日志恢复这部分被修改的数据。
MySQL提供了两种事务型的存储引擎:InnoDB和NDB Cluster。
自动提交(AUTOCOMMIT)
MySQL默认采用**自动提交(AUTOCOMMIT)**模式,也就是说,如果不是显示地开始一个事务,则每个查询都被当作一个事务执行提交操作
可以通过设置AUTOCOMMIT
变量来启用胡总和禁用自动提交模式
set AUTOCOMMIT = 1; # 1或者ON 表示启用,0或者OFF表示禁用
MySQL能够识别所有的4个ANSI隔离级别,InnoDB引擎也支持所有的隔离级别
可以通过命令来设置隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
隐式和显式锁定
InnoDB采用的是两阶段锁定协议(two-phase locking protocol)。
隐式锁定:在事务执行过程中,所示可以执行锁定,锁只有在执行COMMIT或者ROLLBACK的时候才会解开,并且所有的锁实在同一时刻被释放
显示锁定:InnoDB支持通过特定的语句进行显示锁定
SELECT ... LOCK IN SHARE MODE SELECT ... FOR UPDATE
MySQL也支持LOCK TABLE 和 UNLOCK TABLES语句,这是在服务器层面实现的,和存储引擎无关
多版本并发控制MVCC(Multiversion Concurrency Control),解决了REPEATABLE READ可重复读中存在的幻读问题,它在很多情况下避免了加锁的操作,因此开销更低。虽然实现的机制各有不同,但大都实现了 非阻塞的读操作,写操作也之锁定必要的行
InnoDB的MVVC,是通过再每行记录后面保存两个隐藏的列来实现。这两个列,一个保存了行的创建时间,一个保存行的过期时间(或删除时间)。存储的并不是时间,而是系统版本号(system version number)。每开始一个新的事务,系统版本号就会递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询到每行记录的版本号进行比较。
REPEATABLE READ隔离级别下,MVCC具体操作
优点:
保存两个额外的版本号,使大多数操作都可以不用加锁,使得读数据操作更加简单,性能更好,并且能够保证只会读到符合标注的行
缺点:
需要额外的空间
需要更多的行检查工作
一些额外的维护工作
MVVC只在
REPEATABLE READ
和READ COMMITED
两个隔离模式下工作。READ UNCOMMITED
总是读取最新的数据行,SERIALIZABLE
会对读取的行都加锁