MySQL是一款主流的开源关系型数据库,提供高性能的数据存储服务。我们做后端开发的时候,性能瓶颈往往不是应用本身,而是数据库层面。因此,掌握MySQL的一些底层原理,有助于我们更好地理解MySQL,对MySQL进行性能调优,开发高性能的后端服务。MySQL的逻辑架构MySQL的逻辑架构如下图所示:顶层处理来自客户端的连接。主要做连接处理,授权认证,安全等。MySQL在这一层维护了一个线程池,用于处理来自客户端的连接。MySQL可以使用用户名和密码身份验证,或基于SSL的X.509证书身份验证。第二层由三部分组成:查询缓存、解析器、优化器。解析器用于解析SQL语句,优化器对解析后的语句进行优化。在解析查询之前,服务器会先检查查询缓存。如果在其中能找到对应的查询结果,则直接返回查询结果,不做查询解析、优化等进一步处理。存储过程、触发器、视图等都在这一层实现。第三层是存储引擎,负责在MySQL中存储数据,提取数据,开启事务等。存储引擎通过API与上层通信,这些API屏蔽了不同存储引擎之间的差异,使这些差异对上层查询过程透明。存储引擎不解析SQL。Mysql最常用的存储引擎是InnoDBMysql并发控制。如果多个线程同时操作数据,可能会造成并发控制问题。本文的下一部分将介绍MySQL是如何控制并发读写的。读写锁如果多个线程只是读数据,其实可以一起读,互不影响。这时候就应该使用“读锁”,也就是共享锁。获得读锁的线程不会互相阻塞,可以同时读取一个资源。如果有线程需要写数据,就应该使用“写锁”,也就是排它锁。写锁阻塞其他写锁和读锁,直到写操作完成。锁粒度首先明确一个概念:在给定的资源上,需要锁定的数据越少,系统能够承载的并发量就越高。但是加锁也会消耗资源。如果系统花费大量时间管理锁而不是访问数据,则系统的性能可能会受到影响。因此,一个好的“锁策略”就是在锁开销和数据安全之间找到一个平衡点。Mysql支持多种存储引擎架构,每个存储引擎可以实现自己的锁策略和锁粒度。表锁和行锁表锁,顾名思义,就是锁住整个表。表锁开销比较小。对表加写锁后,其他用户对这张表的所有读写操作都会被阻塞。在MySQL中,虽然存储引擎可以提供自己的锁,但是MySQL有时也会使用表锁,比如ALTERTABLE这样的语句。写锁的优先级高于读锁,因此可能会在读锁队列的最前面插入一个写锁请求。行级锁锁住整行,可以最大程度的支持并发处理,但是解锁的开销会比较大。行级锁只在存储引擎层实现,所有存储引擎都以自己的方式实现行级锁。MVCMVCC即“多版本并发控制”。可以认为MVCC是行级锁的一种变体,但它在很多情况下避免了加锁操作,因此开销更低。主流的关系型数据库都实现了MVCC,只是实现机制不同而已。其实MVCC并没有一个统一的标准。然而,它们中的大多数实现了非阻塞读操作,而写操作只锁定必要的行。MVCC保证的是每个事务在执行过程中看到的数据是一致的。但是由于不同事务的开始时间不同,同一张表在同一时间看到的数据可能不同。在MySQL的InnoDB引擎中,是通过在每行记录后面保存两个隐藏列来实现的。一个保存行的创建时间,另一个保存行的过期时间(或删除时间)。实际存储的不是实际的时间戳,而是“系统版本号”。每次打开交易时,系统版本号都会增加。事务启动时,系统版本号将作为事务的版本号,用于与查询行的版本号进行比较。下面介绍版本号在常见CRUD操作中的作用:INSERT将当前系统版本号保存为行版本号DELETE将当前系统版本号保存为该行数据的“删除版本”。UPDATE插入新行,将当前系统版本号保存为行版本号,将当前系统版本号保存到原行的“删除版本”。SELECT只查找版本早于当前事务版本的行。这确保事务读取的行要么在事务之前存在,要么被事务本身插入或修改。行的DeleteVersion未定义或大于当前事务版本号。这确保事务读取的行不会在事务之前被删除。MVCC只工作在REPEATABLEREAD和READCOMMITTED这两个隔离级别下,其他两个隔离级别不能工作。因为READUNCOMMITTED总是读取最新的数据,而不是与当前事务版本匹配的数据行。SERIALIZABLE将锁定所有读取的行。
