MySQL架构
对MySQL服务端架构的概述,包括逻辑架构、并发控制、事务和MVCC(多版本并发控制)等内容。
逻辑架构
连接/线程管理
最上层负责与客户端交互,包括连接处理、身份验证、确保安全性等。
解析器、优化器
MySQL核心能力都在这一层。主要负责解析SQL语句、优化SQL语句和执行内置的函数。
优化器并不关心表使用的是什么存储引擎,但存储引擎对于查询优化是有影响的。优化器会向存储引擎询问它的一些功能、某个具体操作的成本,以及表数据的统计信息。
存储引擎
存储引擎负责MySQL中数据的读写,只是简单的执行上层传下来的SQL语句,可以理解为一个黑盒。MySQL支持多种存储引擎,但推荐使用的还是innodb引擎,功能比较强大
并发控制
在互联网业务里面,几乎都会遇到并发查询问题,读写同时发生。这时候就要采取一定的并发控制策略防止产生预期外的情况。
读写锁
读锁是共享的,写锁是排他的,写锁既会堵塞读锁也会堵塞写锁,保证确保在特定的时间点只有一个客户端能执行写入。
锁的粒度
理论上,锁的粒度越小,发生冲突的可能性就越小,性能就越强,但是管理锁也需要资源,粒度越小,锁需要的资源就越多,也会影响性能。锁的策略也就是锁开销和数据安全性之间的权衡。有两种最重要的锁策略:
- 表锁:锁住整张表,客户端进行写操作需要先获取表锁
- 行级锁:锁住表中的某一行,性能强但是开销大
行级锁是在存储引擎而不是服务器中实现的
事务
事务就是一组SQL语句,作为一个工作单元以原子方式进行处理。
ACID
老生常谈的ACID。
原子性(atomicity)
一个事务必须被视为一个不可分割的工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚。对于一个事务来说,不可能只执行其中的一部分操作,这就是事务的原子性。
一致性(consistency)
数据库总是从一个一致性状态转换到下一个一致性状态。我把它理解为能量守恒定律。
隔离性(isolation)
一个事务所做的修改在最终提交以前,对其他事务是不可见的。
持久性(durability)
一旦提交,事务所做的修改就会被永久保存到数据库中。此时即使系统崩溃,数据也不会丢失,注意没有完美的持久性,毕竟硬盘也有可能损坏。
隔离级别
又是一个常见的八股知识。
读未提交
可以查看其他事务中还没有提交的修改。万一其他事务回滚了,而当前事务根据这个数据做了操作,会导致很多问题。读取未提交的数据,也称为脏读。
读已提交
大多数数据库系统的默认隔离级别是读已提交(但MySQL是可重复读)。
一个事务可以看到其他事务在它开始之后提交的修改,但在该事务提交之前,其所做的任何修改对其他事务都是不可见的。这个级别仍然会造成不可重复读,这意味着同一事务中两次执行相同语句,可能会看到不同的数据结果,因为第二次读可能读取到其他事务刚刚提交的数据。
可重复读
保证了在同一个事务中多次读取相同行数据的结果是一样的,但还是会造成幻读(读取到了其他事务刚刚插入的行,也就是新数据)。
InnoDB默认为可重复读隔离级别,并且通过间隙锁策略来防止在这个隔离级别上的幻读:InnoDB不只锁定在查询中涉及的行,还会对索引结构中的间隙进行锁定,以防止幻行被插入。
可串行化
最高的隔离级别。该级别通过强制事务按序执行,使不同事务之间不可能产生冲突。
可串行化会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用的问题
级别的不可重复读问题,保证了在同一个事务中多次读取相同行数据的结果是一样的
死锁
死锁是指两个或多个事务相互持有和请求相同资源上的锁,产生了循环依赖。
当多个事务试图以不同的顺序锁定资源时会导致死锁。当多个事务锁定相同的资源时,也可能会发生死锁。
一般的解决方案有死锁检测和锁超时两种,InnoDB目前处理死锁的方式是将持有最少行级排他锁的事务回滚。
事务日志
事务日志有助于提高事务的效率。存储引擎只需要更改内存中的数据副本,而不用每次修改磁盘中的表,这会非常快。然后再把更改的记录写入事务日志中,事务日志会被持久化保存在硬盘上。
MVCC
MVCC是行级锁的一个变种,但是它在很多情况下避免了加锁操作,因此开销更低。
MVCC的工作原理是使用数据在某个时间点的快照来实现的,每一行记录需要记录当前操作这一行的事务ID和旧的数据记录,这样才能做到根据当前事务的可见策略来判断当前行数据是否对这个事务可见。
保存这些数据的好处是大多数读取查询都不再需要获取锁,不好的地方是每一行存储了额外的数据,增加了管理成本。
读未提交与MVCC不兼容,是因为不管怎样都读最新版本。可串行化与MVCC也不兼容,是因为读取会锁定它们返回的每一行。