Mysql架构
1. 逻辑架构
Mysql逻辑架构
- 最上层(客户端)不是MySQL特有的,所有基于网络的C/S的网络应用程序都应该包括连接处理、认证、安全管理等
- 中间层是MySQL的核心,包括查询解析、分析、优化和缓存以及所有的内置函数(日期、时间、数学和加密函数)等.同时它还提供跨存储引擎的功能: 存储过程、触发器、视图等
- 最下层是存储引擎,它负责存取数据.服务器通过storage engine API可以和各种存储引擎进行交互
2.并发控制
MySQL提供两个级别的并发控制:服务器级(the server level)和存储引擎级(the storage engine level).
加锁是实现并发控制的基本方法,常见的有两种类型的锁
共享锁(shared lock), 或称读锁(read lock)排他锁(exclusive lock), 或称写锁(write lock), 写锁比读锁有更高的优先级
MySQL中锁的策略(粒度):
- 表级锁(
table lock):MySQL独立于存储引擎提供表锁,例如,对于ALTER TABLE语句,服务器提供表锁(table-level lock). - 行级锁(
row lock):InnoDB和Falcon存储引擎提供行级锁,此外,BDB支持页级锁.InnoDB的并发控制机制.
另外,值得一提的是,MySQL的一些存储引擎(如InnoDB、BDB)除了使用封锁机制外,还同时结合MVCC机制,即多版本两阶段封锁协议(Multiversion two-phrase locking protocal),来实现事务的并发控制,从而使得只读事务不用等待锁,提高了事务的并发性.
3.事务
事务的ACID特性
- 原子性(Atomicity):事务是一个原子操作单元,其对数据的修改,要么全都执行,要么全都不执行.
- 一致性(Consistent):在事务开始和完成时,数据都必须保持一致状态.这意味着所有相关的数据规则都必须应用于事务的修改,以保持数据的完整性;事务结束时,所有的内部数据结构(如B树索引或双向链表)也都必须是正确的.
- 隔离性(Isolation):数据库系统提供一定的隔离机制,保证事务在不受外部并发操作影响的“独立”环境执行.这意味着事务处理过程中的中间状态对外部是不可见的,反之亦然.
- 持久性(Durable):事务完成之后,它对于数据的修改是永久性的,即使出现系统故障也能够保持.
四种事务隔离级别
| 隔离级别 | 脏读(Dirty Read) | 不可重复读(NonRepeatable Read) | 幻读(Phantom Read) | 加锁读 |
|---|---|---|---|---|
| 未提交读(Read uncommitted) | 可能 | 可能 | 可能 | 不可能 |
| 已提交读(Read committed) | 不可能 | 可能 | 可能 | 不可能 |
| 可重复读(Repeatable read) | 不可能 | 不可能 | 可能 | 不可能 |
| 可串行化(Serializable) | 不可能 | 不可能 | 不可能 | 可能 |
- 未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据
- 提交读(Read Committed):只能读取到已经提交的数据.Oracle等多数数据库默认都是该级别 (不重复读)
- 可重复读(Repeated Read):可重复读.在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别.在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读. InnoDB和XtraDB存储引擎通过多版本并发控制(MVCC, Multiversion Concurrency Control)解决了欢读问题.可重复读是MySQL的默认事务隔离级别.
- 串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞
死锁
死锁是指两个或者多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环的现象. InnoDB目前处理死锁的方法是, 将持有最少行级排他锁的事务进行回滚(只是相对比较简单的死锁回滚算法).
4.多版本并发控制
MVCC 的实现,是通过保存数据在某个时间的点的快照来实现的.典型的有乐观(optimistic)并发控制和悲观(pessimistic)并发控制, 通过InnoDB的简化版来说明MVCC是如何工作的.
InnoDB的MVCC, 是通过在每行记录后面保存两个隐藏的列来实现.这两个列, 一个保存了行的创建时间, 一个保存行的过期时间(或删除时间),实际存储是是系统版本号.每开始一个新的事务,系统版本号都会自动递增.事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较.
SELECT InnoDB 会根据一下两个条件检查每行记录:
- InnoDB只查找版本早于当前事务版本的行数据(也就是,行的系统版本号小于或者等于事务的系统版本号),这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的
- 行的删除版本要么未定义,要么大于当前事务版本号.这可以确保事务读取到的行,在事务开始前未被删除
INSERT InnoDB为新插入的每一行保存当前系统版本号作为行版本号
DELETE InnoDB为删除的每一行保存当前系统版本号作为行删除标识
UPDATE InnoDB为插入一行新纪录,保存当前系统版本号作为行版本号,同时保存当前系统版本号到原来的行作为行删除标识
MVCC只在 Read committed 和 Repeatable read 两个隔离级别下工作,其它两个隔离级别都和MVCC不兼容.
5.间隙锁(Next-Key锁)
间隙锁只会阻塞insert操作,对于delete和update操作是不会阻塞的
当我们使用范围条件而不是相等条件来检索数据,并请求共享或排它锁时,InnoDB会给符合条件的已有数据记录的索引项加锁;对于键值在条件范围内但并不存在的记录,就叫做"间隙(GAP)",InnoDB也会对这个间隙加锁,这种锁机制就是所谓的间隙锁.(Next-Key锁)
比如id>100,但数据的id只有101,但也会对102,103等间隙加锁.
InnoDB使用间隙锁的目的,一方面是为了防止幻读,以满足相关隔离级别的要求.对于上面的例子,要是不使用间隙锁,如果其他事务插入了id大于100的新记录,就会产生幻读.另一方面,是为了满足其恢复和复制的需要.
很显然,范围条件检索并锁定记录,这时的并发插入会造成严重的锁等待.因此,实际开发中,并发插入较多的应用,要尽量优化逻辑,尽量使用相等条件也访问更新数据,避免使用范围条件.
注意: 如果相等条件请求是给一个不存在的记录加锁,InnoDB也会使用间隙锁!
6.存储引擎
InnoDB InnoDB是一个高性能的事务存储引擎 存储表和索引有两种方式:
- 共享表空间存储: 这种方式下,表的定义位于.frm文件中,数据和索引保存在innodb_data_home_dir和innodb_data_file_path指定的表空间中
- 多表空间存储: 表的定义仍位于.frm文件,但是,每个InnoDB表和它的索引在它自己的文件(.idb)中,每个表有它自己的表空间
InnoDB采用MVCC来支持高明发, 并且实现了四个标准的隔离级别. 默认级别是 REPEATABLE READ(可重复度),并且通过间隙锁(next-key locking)策略防止幻读的出现.
MyISAM 特性
- 锁与并发性: MyISAM只有表级锁,不支持行级锁.所以不适合于大量的写操作,但是它支持并发插入(concurrent inserts),这是一个非常重要且有用的特性
- 自动修复: MySQL支持自动检查和修复MyISAM表
- 手动修复: 你可以使用CHECK TABLE检查表的状态,并用REPAIR TABLE修复表
- 索引: 你可以为BLOB和TEXT的前500个字符创建索引.而且,MyISAM还支持全文索引,但仅限于CHAR、VARCHAR、和TEXT列
- 延迟键写(Delayed key writes): 如果创建MyISAM表时指定DELAY_KEY_WRITE,MySQL在查询结束时,不会将改变的索引数据写入磁盘,而将修改保存在key buffer中.只有要改变缓存或者关闭表时,才会把索引数据刷入磁盘
MyISAM最典型的性能问题还是表锁的问题, 如果发现所有查询都长期处于"Locked"状态,那毫无疑问表锁就是罪魁祸首.
其它存储引擎
- Archive: 只支持INSERT 和 SELECT 操作
- Blackhole: 没有实现任何存储机制, 丢弃所有插入数据, 只在一些特殊的复制架构和日志审核时发挥作用
- CSV: 处理CSV文件,把CSV文件当作表处理
- Federated、Memory、Merge、NDB等