《高性能MySQL》读书笔记(一)——MySQL架构及重要属性概述
(原创内容,转载请注明来源,谢谢)
一、MySQL逻辑架构
1、三层架构
mysql逻辑分为三层设计:
第一层是基于网络的处理,如连接处理、授权认证、安全等,这个在基于网络的服务器、客户端中的各种软件都会有相应的实现。
第二层是mysql的核心功能部分,包括查询解析、分析、优化、缓存、以及所有的内置函数,所有跨存储引擎的功能也都在这一层实现,包括触发器、存储过程、视图等。
第三层是数据库的存储引擎,即通常提及mysql都会提到的mysiam、innodb等。存储引擎负责数据的存储和提取,不同的存储引擎适用于不同的场景,各有优缺点。服务器通过API与存储引擎进行通信,且不同的存储引擎,底层的函数名称与参数都是一样的,这也使得大部分情况下存储引擎对于数据库使用者是透明的(除了事务、锁等特殊操作,不同的存储引擎会有所不同)。
mysql三层架构如下图所示:
2、连接管理
每个客户端都用一个进程和mysql服务器进行连接,这个连接只在单独进程中进行,该进程只能轮流cpu的核心。另外,服务器会缓存线程,不用为每一个连接新建线程。
客户端连接时,mysql服务器会进行登陆验证,验证完毕后还有继续验证用户具备哪些权限(如对每个表的读、写权限等)。
3、优化与执行
mysql会解析查询语句,并且创建内部数据结构,对其进行优化,包括重写查询、决定表的读取顺序、选择合适的索引(每次查询只能用一个或一组索引)。对于select语句,查询之前,会先检查查询缓存,如果有缓存,则直接返回,不用解析、优化、执行的过程。
二、并发控制与锁
1、读写锁与锁粒度
锁分为读锁(又称为共享锁)和写锁(又称为排它锁)。读锁是共享的,即多个客户端可以在同一个时刻读同一个资源,并不会互相影响;写锁是排他的,对于同一个资源,一个进程在写的时候,另一个进程无法写,也无法读。
锁的粒度越细,越能精确控制锁的范围。如对整个表的锁,与对当行的锁,效果就不一样。锁表则如果仅操作某一行,其他行也会被锁住,这样耗费资源,但是只有一个锁,所以也只要维护一个锁;锁行,则只会锁住操作的那一行,其他行不影响,但是如果有多行操作,则需要维护多个锁,加大维护的开销。
mysql不同的存储引擎,对锁的策略不同,mysiam是表锁,innodb是行锁。但是,对于诸如alter table等ddl语句,mysql会直接使用表锁,而不管存储引擎采用的锁策略。
当两个事务之间采用不同的顺序操作某一条数据,则可能发生死锁,一个两个事务分别在等待对方释放对数据的锁,才能进行后续的操作。mysql的很多引擎会检测,如果语法分析发现会产生死锁,则不让执行;另外,如果真的进入死锁,则需要对其中一个事务进行回滚,以释放资源。
2、多版本并发控制(MVCC)
mysql大多数的支持事务的存储引擎,都不是简单的行锁,而会采用mvcc,以提升并发性能。
mvcc通过保存数据某个时间节点的快照实现的,不管事务需要执行多久,事务内部看到的内容总是一致的。根据事务开始时间不同,每个事务看到的表的数据可能不一样。根据不同的存储引擎,有乐观并发控制和悲观并发控制。
对于innodb,是通过每行后面保存两个隐藏的列来实现的,一个保存的是行的创建时间,一个保存的是过期时间(删除时间)。并且,不是保存具体的时间,而是用版本号的方式来进行保存。
每次开始一个新事务,事务的版本号都是增1,并且提交事务的时候,会先在数据库里进行查询,确认提交的时候数据库里的版本号和开始事务时的版本号一致,以确保事务执行期间,没有其他的进程或事务改动过该数据。
确认一致后,会在提交数据的同时,如果是update命令,会将数据库的版本号加1。这样,如果同期有其他的事务在进行,会提交失败,以确保数据一致性;如果是delete命令,则会将数据的过期时间加上标记;如果是新增,则版本号和当前事务版本号一致。
通过mvcc,可以避免大部分情况下的锁,特别是对于读,不需要加锁,性能较好。
三、事务
1、事务原则
具备事务的系统,都必须经过acid测试。
a(atomicity)是原子性,即要求事务是最小单元,要么其中内容全部成功,要么全部失败;c(consistency)是一致性,事务执行期间,从一个状态切到另一个状态,并不会造成数据库数据错乱;i(isolation)隔离性,事务提交之前,对其他事务、其他系统是不可见的;d(持久性),事务提交会保存到磁盘,即使系统奔溃,修改的数据也不会丢失。
2、隔离级别
针对事务,有四种级别的隔离方式,定义了事务的修改,对于事务内部和外部,哪些是可见的。
1)readuncommitted(未提交读),即事务未提交都可以读,这样级别几乎没有系统使用,对于未提交的事务都可以读,会严重影响事务的acid原则。
2)readcommitted(提交读),即事务提交之后,其他事务才可以读取,这个对于其他事务是没有影响的,基本符合acid,有一些系统采用此方式。但是mysql不是采用此方式。该方式下,一个事务执行期间,如果有需要两次读取同一行的数据,有可能是不一致的。
3)repeatableread(可重复读),这个是mysql的默认方式。即上述提到的mysql对于事务使用的快照的方式,在事务开始时记录当前状态,并且在整个事务期间,对于同一条数据,除非是该事务自身进行的修改,否则每次读取到的内容都是一致的。这样,可以避免事务执行期间其他事务修改了该数据,导致事务两次读取同一个数据不一致的情况。
4)serializable(可串行化),强制事务串行执行,严格性最高,为读取的每一行数据都会加上锁。但是这样也会带来比较大的开销。
如下图所示:
3、事务日志
事务日志提高事务的效率。在事务执行期间,mysql会将事务设计到的数据库操作,将结果存入内存中,而不是立即持久化到硬盘。对于事务里面的每条sql语句,采用追加的方式写入文件。
当事务结束后,再将内存中的数据,逐步写入磁盘中。这也称为预写式日志。
4、mysql的事务
mysql提供两个支持事务的存储引擎——innodb和ndb cluster。
对于普通的sql语句,mysql默认是自动提交,即不需要commit,输入语句就执行。但是,在一个事务中,如果要操作不同的表,而每个表的存储引擎不一致,则比较危险。如mysiam是不支持事务的,如果一个事务既操作innodb引擎的表,又操作mysiam的,则如果失败回滚,那对mysiam表的操作,会无法回滚。
innodb采用两阶段锁定协议进行数据锁定,在事务执行过程中,每个语句都可以显示的加锁,在事务执行完毕后会一次性释放所有事务期间的锁。
——written by linhxx 2017.09.11