目录
1.1 MySQL逻辑架构
最上层的服务非mysql独有,大部分基于网络客户端/服务端都类似的架构,如连接处理,认证处理等等
第二层是mysql最有意思的部分,所有核心功能都在这一层
第三层包含了存储引擎,负责数据提取和存储。
1.1.1 连接管理与安全性
每个客户端连接都会拥有一个线程,这个连接的查询都会在这个线程进行,所有线程需要轮流在cpu在运行。服务器会缓存线程,就是在约定时间内,一个客户端访问结束后一段时间内,另一个客户端连上来时服务器会把之前的线程分配给当前客户端使用。
1.1.2 优化和执行
mysql会解析查询,并创建内部数据结构(解析树),然后对其进行各种优化,包括重写查询,决定表的读取顺序,以及选择合适的索引等等。对于select语句,服务器会先检查查询缓存,若存在数据,服务器不必执行查询解析优化和执行的整个过程,直接返回查询缓存集。
1.2 并发控制
举例,如果一个邮箱同时有多份邮件发送进来,没有任何加锁措施情况下,显然邮箱数据会收到破坏,邮件内容交叉。为此,如何维护好数据系统是非常必要的。
1.2.1 读写锁
同一时刻多个用户读取同份文件不会出问题,但是万一一人正在修改文件,其他人在读,结果会怎么样?结论是不确定的。
解决这类经典并发问题就是并发控制。在处理过程中实施2种锁,共享锁(读锁)lock table XXX read(此时不能写)和独占锁(写锁)lock table XXX write,共享锁就是允许多个用户同时读取同一份资源,互不干扰,而独占锁是只要有一个人在写,其他人的读写都遭到限制(堵塞直到解锁)。
1.2.2 锁粒度
上面说到锁,锁虽能维护好数据安全,而安全往往需要性能的损耗,锁会降低共享资源的并发性,一种提高共享资源并发性的方式是让锁对象更有选择性。接下来介绍mysql提供了2种最重要的锁策略。
表锁
表锁是mysql最基本的锁策略,并且是开销最小的策略。只要有用户在修改表,它就会把整张表给锁住,堵住其它用户对该表的所有操作。锁住整张表这给人第一感觉就是他性能肯定很慢,其实不然,在一些特定场合,表锁也可以具有良好的性能。另外写锁的优先级比读锁高,所以写请求可能插到读请求队列的前面。
行锁
行锁可以最大程度地支持并发处理,但是带来了最大的锁开销。只有有人对某行数据进行修改,其它用户操作就堵塞直至修改操作结束。
需要说明的是,读写优先级可以自己修改,后面会给大家讲如何配置。
1.3 事务
事务就是一组原子操作,事务内的语句要么全部成功要么全部失败。
经典例子:
银行转账100元操作:检查帐户余额是否大于100元-->从帐户减去100元-->对方帐户增加100元.
上面三个步骤必须在事务内执行,任何一个步骤失败,都会回滚到原来状态。
语法:
start transaction #开启事务
需要执行的操作
。。。。。。。
commit #提交事务
commit and chain #提交事务后开启新事务
rollback #事务回滚
rollback and release #事务回滚后断开和客户端连接
事务的标准特征ACID:
原子性auomicity:一个事务必须被视为一个不可分割的最小单位,整个事务要么成功,要么失败,不可能只执行一部分。
一致性consistency:数据库总是从一个一致性的状态的转为另一个一致性的状态,若事务未提交,数据库将不会发生任何修改
隔离性isolation:一个事务在提交之前,对其它事务是不可见的。
持久性durability:一旦事务提交,所做修改将永久保存在数据库中,即使系统崩溃,数据依旧存在。
1.3.1 隔离级别
查询隔离级别语句:show variables like 'transaction_isolation'
未提交读READ UNCOMMITTED:事务即使没有提交,对其它事务来讲都是可见的,为此事务可以读取未提交的数据称为脏读。
提交读READ COMMITTED:一个事务只能看到已经提交事务所做的修改,在一个事务提交之前,其所做的操作对其它事务是不可见的,这也称为不可重复读,因为两次执行会得到不同结果。
可重复读REPEATABLE READ:保证了在同一个事务中多次读取同样记录的结果是一致的,这解决了脏读问题,这也是mysql默认的隔离级别,但是会造成幻读----当某个事务读取的范围正好被另一个事务插入新数据,导致之前的事务再次读取该范围时,会产生幻行。
可串行化SERIALIZABLE:最高的隔离级别,读取时会在每一行数据上加锁,杜绝了脏读和不可重读和幻读可能性,但是可能会导致大量的超时和锁争用问题。
举例:
READ UNCOMMITTED
原始数据:
mysql> select * from Course;
+------+--------+------+
| c_id | c_name | t_id |
+------+--------+------+
| 02 | 数学 | 01 |
| 03 | 英语 | 03 |
| 04 | 语文 | 04 |
| 05 | 物理 | 05 |
| 06 | 化学 | 06 |
| 07 | 高数 | 07 |
| 08 | 计组 | 08 |
+------+--------+------+
7 rows in set (0.00 sec)
设置级别,插入数据,未提交事务
mysql> set global transaction isolation level READ UNCOMMITTED; #设置级别
Query OK, 0 rows affected (0.00 sec)
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into Course values('09' , '算法' ,'09');
Query OK, 1 row affected (0.00 sec)
另一个事务情况:
mysql> select * from Course;
+------+--------+------+
| c_id | c_name | t_id |
+------+--------+------+
| 02 | 数学 | 01 |
| 03 | 英语 | 03 |
| 04 | 语文 | 04 |
| 05 | 物理 | 05 |
| 06 | 化学 | 06 |
| 07 | 高数 | 07 |
| 08 | 计组 | 08 |
| 09 | 算法 | 09 |
+------+--------+------+
8 rows in set (0.00 sec)
发现在未commit情况下另一个事务可以看见。
其它三个级别大家自行实验
1.3.2 死锁
死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环现象。当多个事务以不同顺序请求是可能出现死锁现象。为了解决这一问题,数据库系统实现了各种死锁检测和死锁超时机制。InnoDB目前处理死锁的方法是,将持有最少行级排他锁的事务进行回滚。
1.3.3 事务日志
事务日志可以提高事务效率,使用事务日志,存储引擎在修改表时只需要修改其内存拷贝,再把他修改行为记录到持久化的硬盘事务日志中,而不用每次都将修改的数据持久化到磁盘。事务日志采用的是追加方式,因此写日志操作只需要磁盘一小块 区域顺序IO,所以采用事务日志的方式相对来说要快得多。在事务日志持久化后,内存中被修改的数据在后台可以慢慢刷回磁盘。我们通常把这称为预写式日志,修改数据需要写两次磁盘.
1.4 MySQL中的事务
在事务中混合不同引擎时,如混合使用事务型和非事务型的表,在非正常提交情况下,会导致非事务型表的变更无法撤销,这就导致了数据库不一致,这种情况很难修复,事务的最终结果无法确定。所以,为每张表选择适合的存储引擎是非常重要的。
隐式和显式锁定
InnoDB采用量阶段锁定协议,在事务执行过程中,随时可以执行锁定,锁只有执行了commit或者rollback时才会释放,并且所有锁是在同一时刻释放的。前面描述的都是隐式锁定,InnoDB会根据隔离级别在需要的时候自动加锁。
InnoDB也支持特定语句进行显式锁定
- select .. lock in share mode
- select .. for update
mysql也支持lock table 和unlock table 语句,但这不能替代事务处理,如果需要用到事务,还是使用事务引擎。
1.5 MySQL的存储引擎
Innodb引擎:
InnoDB是基于聚簇索引建立的,采用MVCC 来支持高并发,并且实现了四个标准隔离级别,其默认级别是可重复读,并且通过间隙锁策略来防止幻读出现。聚簇索引对主键查询有很高性能,不过二级索引中必须包含主键列。
MyISAM引擎:
MyISAM会将表存储在两个文件中:数据文件和索引文件,分别以.MYD 和.MYI为扩展名。它支持加锁并发,修复,索引特性,延迟更新索引键等特性。另外,如果表创建后并导入数据后不会再进行修改操作,那么这样对表或许适合采用MyISAM压缩表 ,压缩表能减少磁盘占用,也可以减少磁盘IO,从而提高查询性能,它也支持索引,但索引也是只读。
后面会用一章专门来讲解上面俩引擎的特点。
MySQL还有Archive引擎,Blackhole引擎,CSV引擎,Federated引擎,Memory引擎,Merge引擎,NDB集群引擎以及第三方引擎,这里就展开说了。