Photo by Ivana Cajina on Unsplash

高性能 MySQL 读书笔记 - 架构和历史

author's avatar
igaozp
未来已来,只是分布不均

并发控制

MySQL 中只要存在多个查询同时修改数据的场景,就会产生并发控制的问题。MySQL 通常在两个层面进行并发控制,一是服务器层面,二是存储引擎层面。

读写锁

并发控制中一类经典的问题就是:如何避免对同一条数据进行并发地修改和查询操作,以避免产生不可预测的结果。

这里可以通过构建一个锁系统来解决,这个系统有两种锁构成:共享锁(shared lock)和排他锁(exclusive lock),也被称为读锁(read lock)和写锁(write lock)。

通常读锁是共享的,意味着多个客户端可以同时读取同一数据资源,因为读操作不会对数据产生副作用,数据不会产生变化。 而写锁是排他的、非共享的,一个写锁会阻塞其他的写锁和读锁,只有这样才能保证其他的写操作和当前的写操作不会产生冲突,读操作不会读取到正在写入的内容。

这些锁定操作在 MySQL 内部频繁地发生着,MySQL 会自动管理这些锁的锁定和释放,对我们开发者来说是透明的,无需去干预。

锁粒度

提高共享资源并发性的一种方式是让锁定的对象更具有选择性,尽可能对数据精确地锁定,锁定的数据越少,系统的整体并发程度越高。 但是锁的管理也需要消耗系统资源,为了精确地锁定资源系统可能会额外消耗资源去检索定位数据,系统的整体性能也会收到影响。

所以需要一种策略去平衡锁的开销和数据的安全性

表锁

表锁是 MySQL 中最基本的锁策略,且性能开销最小。 用户对表进行写操作时(插入、删除、更新)需要获得写锁锁定整张表,阻塞其他用户对该表的所有读写操作。 当没有写锁时才能获取到读锁,且读锁之间不会相互阻塞。另外,写锁比读锁有着更高的优先级。

行级锁

行级锁能够最大程度地支持并发处理,同时也意味着在锁处理上会带来最大程度的性能开销。与表锁不同行级锁只能由存储引擎来提供实现。

事务

说到数据库就不能不提事务,简单来说事务内的语句要么全部执行成功,要么全部失败。

事务必须具备原子性(atomicity)、一致性(consistency)、隔离性(isolation)、持久性(durability),即ACID

MySQL 默认采用自动提交事务,即每个查询都被当作一个事务执行并提交。

隔离级别

隔离级别 脏读可能性 不可重复读可能性 幻读可能性
READ UNCOMMITTED YES YES YES
READ COMMITTED NO YES YES
REPEATABLE READ (DEFAULT) NO NO YES
SERIALIZABLE NO NO NO

何为脏读,简单来说指一个事务中访问到了另外一个事务未提交的数据。

又何为幻读,即一个事务读取两次,但得到的记录条数不一致,如果记录的数据不一致,那则称为不可重复读。

死锁

死锁是指多个事务在同一资源上相互占用,并且请求锁定对方占用的资源,从而导致恶性循环的现象。

InnoDB 目前解决死锁的方法为:将持有最少行级排他锁的事务进行回滚

多版本并发控制

多版本并发控制(MVCC)是行级锁的一个变种,在很多情况下避免了加锁操作,且写操作只锁定必要的行,MVCC 通过保存数据在某个时间点的快照来实现。

MVCC 一般由乐观锁(optimistic lock)和悲观锁(pessimistic lock)来控制。

乐观锁假设不会发生并发冲突,只在提交操作时检查数据是否被其他人修改过,适用于读多写少的应用场景。

悲观锁假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作,每次在读取数据的时候都会上锁。乐观锁大多使用版本号进行控制。