MySql 架构与历史
Last updated
Last updated
[TOC]
Mysql 是C/S架构,总共可以分为三层:
最上层客户端,mysql的图形化连接软件和各种编程语言的mysql连接模块,例如Python的MySQLdb模块,还有API接口等等
第二层就是mysql的核心服务,也称为service层。包括:查询解析、分析、优化、缓存以及所有的内置函数(例如:日期、时间、数学和加密函数)。同时,所有的跨存储引擎的功能都在这一层实现:存储过程、触发器、视图等
第三层为存储层,包含了存储引擎,存储引擎负责数据的存储与提取,服务器通过API与存储引擎进行通信,这些接口屏蔽了不同存储引擎之间的差异,使得这些差异对于上层的查询过程透明。
每一个客户端都会在服务器中拥有一个线程,这个连接的查询只会在该线程中进行,这个线程只会轮流在某一个cpu核心或者cpu上运行,服务器负责缓存线程。只要有多个查询需要同时操作数据都会有并发控制问题。mysql在服务器层、存储引擎层进行并发控制。
读写锁(共享锁、排他锁)
锁粒度: 行级锁、表级锁 不同类型的存储引擎会实现各自的锁策略以及锁粒度。ALTER TABLE 操作服务器层会使用表锁忽略存储引擎的锁机制;而存储引擎中的行锁实现对于服务器层也是不可见的
事务: 一组原子性的SQL查询,或者一个独立的工作单元
一个运行良好的事务管理系统,需要具有ACID属性:
原子性(Atomicity): 一个事务是一个最小的可操作单元,要么成功,要么回滚,不可能只执行一部分
一致性(Consistency):数据库总是从一个一致性状态转移到另一个一致性状态
隔离性(Isolation):通常来说,一个事务所做的修改在提交之前对于其他事务是不可见的;在不同的隔离级别下,事务具有不同的可见性
持久性(Durability):事务一旦提交,所做的修改就会永久保存在数据库中
每种存储引擎实现的隔离级别不尽相同,主要有以下隔离级别:
未提交读(Read Uncommited):
事务的修改即使没有提交,对于其他事务也是可见的,也称为脏读。这个隔离级别会导致很多问题,从性能上讲,并不会比其他隔离级别好太多,实际很少使用。
提交读(Read Commited):
一个事务开始时只能看到其他事务已提交的结果,换句话说,一个事物未提交时所做的操作修改对于其他事务都是不可见的。又称为不可重复读,两次执行查询的结果可能不一致。
可重复读(Repeatable Read):
解决了脏读的问题,该级别保证同一个事务多次读取同样记录的结果是一致的。但是理论上可重复读不能解决幻读(Phantom Read)*的问题, 幻读是指 当事务读取某一范围的数据时,另一个事务在该范围插入了新的记录,导致之前事务再次读取该范围的数据时会产生幻行
。 InnoDB通过多版本并发控制(MVCC, Multiversion Concurrency Control) 解决幻读问题 可重复读也是Mysql默认的隔离级别
可串行化(Serializable):
最高隔离级别,强制事务串行执行,避免幻读问题。可串行化通过对于每一行数据加锁,会导致大量的超时以及锁竞争问题。只有在非常需要确保数据一致,而且接受没有并发的情况下使用。
多个事务在同一资源上相互占用,并且请求锁定对方占用的资源,从而导致恶性循环的现象。多个事务试图以不同顺序锁定多个资源时就会产生死锁。数据库有多种死锁检测以及死锁超时机制。死锁发生后只有部分或者完全回滚其中一个事务才可以打破死锁。
事务日志可以帮助提高事务的效率,进行数据修改时,只修改其内存拷贝,修改记录持久化到事务日志中,不需要立即将修改数据持久化到硬盘,之后依据日志异步进行数据持久化。事务日志采用追加方式,写日志操作只涉及一小块区域内的顺序io,效率更高。称为预写式日志(Write-Ahead Logging)
大多数存储引擎都实现了MVCC,可以认为MVCC是行级锁的一个变种,但在很多情况下避免了加锁操作,因此开销更低。MVCC通过保存数据在某一个时间点的快照实现的,从而保证同一个事务看到的数据一致。典型的实现有乐观的并发控制以及悲观的并发控制。
InnoDB的mvcc通过在每一行记录的后面保存两个隐藏的列实现的。分别保存行的创建时间、删除时间。保存的并不是实际的时间戳,而是系统版本号。每开始一个事务,系统版本号都会加一,事务开始时刻的系统版本号作为事务版本号:
对于事务A,版本号 Va
SELECT
a. 创建版本号小于等于事务版本号Va的行
b. 行的删除版本号未定义或者大于Va,确保事务读取的行在事务开始前未被删除
INSERT
插入的每一行使用系统版本号作为创建版本号
DELETE
为删除的行保存当前系统版本号作为行删除标志
UPDATE
相当于删除原有记录,创建一条新的记录。新插入一行使用系统版本号作为创建版本号,并将原有行的删除版本号置为系统版本号