MySQL逻辑架构
MySQL
存储引擎架构的设计将查询处理及其他系统任务和数据的存储/提取相分离。将处理和存储分离的设计可以在使用时根据性能、特性,以及其他需求选择数据存储的方式。
连接管理与安全性
客户端连接在服务器中拥有线程,连接的查询只会在单独的线程中执行,线程只能轮流在某个CPU核心或者CPU中运行。服务器会负责缓存线程,不需要为每个新建的连接创建或者销毁线程。客户端连接到MySQL服务器时,服务器需要基于用户名、原始主机信息和密码进行认证。连接成功后,还需验证客户端是否具有执行某个特定的查询权限。
优化和执行
MySQL会解析查询,井创建内部数据结构(解析树) ,然后对其进行各种优化,包括重写查询、决定表的读取顺序,以及选择合适的索引等。用户可以通过以下方式提高运行效率:
- 特殊关键字提示(hint)优化器,影响决策过程。
- 请求优化器解释(explain)优化过程的各个因素,使用户知道服务器优化决策的步骤,提供参考基准,便于用户重构查询和schema、修改相关配置。
并发控制
在数据库中通过并发控制,在处理并发读写,通过共享锁和排它锁解决数据不一致性问题。
- 共享锁(读锁):多个客户在同一时刻同时读取相同资源,而互不干扰。
- 排它锁(写锁):排它锁会阻塞其他的写和读。
让锁定对象更有选择性,提高共享资源并发性,尽量锁定需要修改的部分数据而不是所有资源。在任何时候,在给定的资源上,锁定的数据量越少,则系统的并发程度越高,只要相互间不发生冲突即可。
锁的各种操作,包括获得锁、检查锁是否已经解除、释放锁等,都会增加系统的开销。如果系统花费大量的时间来管理锁,而不是存取数据,系统性能可能会受影响。
锁的策略在锁的开销和数据的安全性之间寻求平衡,一般在表上施加行级锁,并以各种复杂的方式来实现。
- 表锁:表锁是MySQL中最基本的锁策略,并且是开销最小的策略。写锁阻塞读写操作,读锁互不阻塞。写锁比读锁有更高优先级。
- 行级锁:最大程度支持并发处理(同时带来最大锁开销)。
事务
事务是一个独立的工作单元,事务内的语句,要么全部执行,要么全部不执行。
- 原子性:事务是不可分割的最小工作单元,要么全部提交成功,要么全部失败回滚。
- 一致性:从一个一致性的状态转换到另外一个一致性状态。
- 隔离性:一个事务所做的修改在最终提交之前,对其他事务是不可见的。
- 持久性:一旦事务提交,则其所做的修改会永久保存到数据库中。
锁的粒度增加系统开销,实现ACID特性的数据库通常会需要更强的CPU处理能力、更大的内存和更多的磁盘空间。
隔离级别
Read Uncommitted(未提交读)
事务中的修改,即使没有提交,对其他事务也是可见的。读取未提交的数据成为脏读
。
Read Committed(提交读)
大多数数据库系统的默认级别都是Read Committed(MySQL不是)。一个事务从开始到提交前,所做的任何修改对其他事务都是不可见的。执行两次相同查询,可能得到不同结果。这个级别也叫不可重复读
。
Repeatable Read(可重复读)
解决了脏读,保证多次读取同样记录结果一致,但是无法解决幻读
,即当某个事务在读取某个范围内的记录时,另一个事务又在该范围内插入新的记录,当之前的事务再次读取该范围的记录时,会产生幻行
。
Serializable(可串行化)
最高的隔离级别,通过强制事务串行执行,避免幻读问题。Serializable会在读取的每行数据上都加上锁,所以可能导致大量的超时和锁争用问题。
脏读:一个事务读取到了另外一个事务没有提交的数据; 如:事务T1更新了一行记录的内容,但是并没有提交所做的修改。事务T2读取到了T1更新后的行,然后T1执行回滚操作,取消了刚才所做的修改。现在T2所读取的行就无效了;
不可重复读:在同一事务中,两次读取同一数据,得到内容不同; 如:事务T1读取一行记录,紧接着事务T2修改了T1刚才读取的那一行记录。然后T1又再次读取这行记录,发现与刚才读取的结果不同。这就称为“不可重复”读,因为T1原来读取的那行记录已经发生了变化;
幻读:同一事务中,用同样的操作读取两次,得到的记录数不相同; 如:事务T1读取一条指定的WHERE子句所返回的结果集。然后事务T2新插入 一行记录,这行记录恰好可以满足T1所使用的查询条件中的WHERE子句的条件。然后T1又使用相同的查询再次对表进行检索,但是此时却看到了事务T2刚才插入的新行。这个新行就称为“幻像”,因为对T1来说这一行就像突然出现的一样。 隔离级别越低,事务请求的锁越少或保持锁的时间就越短。InnoDB存储引擎默认的支持隔离级别是REPEATABLE READ。
死锁
死锁是指两个或者多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环的现象。
- 死锁检测 检测到死锁的循环依赖,并立即返回一个错误。
- 死锁超时机制查询的时间达到锁等待超时的设定后放弃锁请求。
死锁发生后,只有部分或者完全回滚其中一个事务才能打破死锁。
事务日志
使用事务日志,存储引擎在修改表的数据时,只需要修改其内存拷贝,再把修改行为记录到持久化的硬盘事务日志中,无需每次修改数据本身持久到磁盘。事务日志持久化后,内存中被修改的数据在后台慢慢地刷回到磁盘。通常称为预写式日志,修改数据需要写两次磁盘。
事务日志,采用追加的方式,写日志的操作是磁盘上一小块区域内的顺序I/O,不像随机I/O需要在磁盘的多个地方移动磁头,所以采用事务日志的方式相对来说要快的多。
多版本并发控制 MVCC
大多数事务型存储引擎实现的都不是简单的行级锁,在很多情况下避免了加锁操作,因此MVCC开销更低。基于提升并发性的考虑,一般同时实现多版本并发控制(MVCC)。
InnoDB的MVCC,通过在每行记录后面保存两个隐藏的列(创建时间、删除时间列)来实现。不过存储的并不是实际的时间值,而是系统版本号。每开始新的事务,系统版本会自动递增。事务开始的版本号作为事务的版本号,用来和查询到的每行记录的版本号进行比较。
参考资料
- 《高性能MySQL》