MySQL 并发控制概述

禁止码迷,布布扣,豌豆代理,码农教程,爱码网等第三方爬虫网站爬取!

MySQL 逻辑架构

Mysql 逻辑架构可以分为 3 层,最上层的服务对于大多数基于网络的工具或服务都有类似的架构,例如连接处理、授权认证、安全等等。
大多数 MySQL 的核心服务功能都在第二层,例如查询解析、优化、缓存即所有内置函数。所有跨存储引擎的功能也在第二层实现,例如存储过程、触发器、视图等功能。
第三层包含了存储引擎,负责 MySQL 中数据的存储和提取。每个引擎都有其优势和劣势,服务器通过 API 与存储引擎进行通信。

并发控制

当有多个查询在同一时刻修改数据,就会引发并发控制的问题,这个问题的解法就是并发控制

读写锁

处理兵法的读或写时,可以通过一个由 2 种类型的锁组成的锁系统来解决,2 种锁被称之为共享锁排他锁,或读锁写锁。读锁是共享的,多个客户在同一时刻可以同时读取一个资源,互不干扰。写锁是排他的,写锁会阻塞其他的写锁和读锁,这样就能确保在一段时间内只有一个用户进行写入。
当某个用户在修改某一部分数据时,MySQL 会通过锁定防止其他用户读取同一数据。

锁粒度

尽量只锁定需要修改的部分,而不是所有的资源,会带来更好的贡献资源并发性。在给定的资源上,锁定的数据量越少,则系统的并发程度越高。加锁同样需要消耗资源,如果系统花费大量时间来管理所,则存储数据的资源就会受影响,进而影响系统的性能。每种 MySQL 存储引擎都可以是实现自己的锁策略和锁粒度,所谓锁策略就是在锁的开销和数据安全性之间的平衡策略。将锁粒度控制在某个级别,可以为某些特定场景提供更好的性能,但是也会失去对其他场景的支持。

表锁

表锁是 MySQL 中开销最小的锁策略,表锁将锁定整张表。用户对表进行增删改等操作时,需要先获得写锁,写锁会阻塞其他用户对表的读写操作。没有写锁时其他用户才能获得读锁,读锁之间不会进行阻塞。

行级锁

行级锁可以锁定某一行,它可以最大限度地支持并发处理,同时锁的开销也会增大。

事务

事务是一组原子性的 SQL 查询,可以理解为一个独立的工作单元。事物的特点是:如果任何一条语句因崩溃或其他原因无法执行,则所有的语句都不会执行。事务机制的存在非常必要,如果一个系统允许不完整的操作制造的数据存在,则数据库中将充满大量错误的数据,这种情况往往不能被容忍。

ACID

一个良好的事务处理系统,必须具备 ACID 特性,分别是原子性、一致性、隔离性和持久性。

  1. 原子性:对于一个事务而言,不可能只执行其中的一部分;
  2. 一致性:数据库总是从一个一致性的状态,转换到另一个一致性状态;
  3. 隔离性:事务所做的所有修改,在最终提交之前对其他事物都是不可见的;
  4. 持久性:一旦事务提交,则其所做的修改就会永久保存到数据库中。

一个实现 ACID 的数据库,相比没有实现的数据库会需要更强的 CPU 处理能力、更大的内存和更多的磁盘空间。对于一些不需要事务的查询类应用,选择一个非事务型的存储引擎,可以得到更高的性能。

隔离级别

对于隔离性,SQL 在标准中定义了 4 种隔离级别。较低级别的隔离通常可以执行更高的并发,系统的开销也更低。

  1. READ UNCOMMITTED:未提交读,事务的修改即使没有提交,对其他事物也都是可见的。事务可以读取未提交的数据的行为,被称之为脏读
  2. READ COMMITTEN:提交读,一个失误从开始到提交之前,所做的任何修改对其他事物都是不可见的。因为 2 次相同的查询可能得到不同的结果,该级别也可以称之为不可重复读
  3. REPEATABLE READ:可重复读:在同一个事务中,多次读取相同记录的结果是一样的。但是这种级别无法阻止幻读的出现,所谓幻读就是某个事物在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录。可重复读是 MySQL 的默认隔离级别。
  4. SERIALIZABLE:可串行化,该级别可以强制事务串行执行。在读取的每一行数据都加上所,可能导致大量的超时和锁争用的问题。

死锁

死锁指两个以上的事务在同一资源上相互占用,并且请求锁定对方占用的资源导致的恶性循环现象。死锁可能会因为数据的冲突而产生,也会由存储引擎的实现方式导致。
为了解决这类问题,数据库系统实现了各种死锁检测和死锁超时机制,例如 InnoDB 存储引擎能够检测到死锁的循环依赖,并且返回一个错误。InnoDB 处理死锁使用了较为简单的算法:将持有最少行级排他锁的事物回滚

事务日志

事务日志可以使存储引擎修改表的数据时,只需要修改其内存拷贝,然后把该数据的修改行为记录到硬盘上的事务日志中。这种手法采用追加的形式,不需要每次都把修改的数据都保存到硬盘上。事务日志持久化时,内存中被修改的数据在后台会慢慢保存入磁盘。
若数据的修改已经记录到日志中,而数据本身还没有保存到磁盘,此时即使系统崩溃存储引擎也能在重启时恢复数据。

MySQL 事务

MySQL 默认采用自动提交模式,如果不是显式地开始一个事务,则每个查询都会被认为是一个事务执行提交操作。可以通过设置 AUTOCOMMIT 变量来启用或禁用:

SHOW VARIABLES LIKE 'AUTOCOMMIT';
SET AUTOCOMMIT = 1;

MySQL 可以通过该命令设置隔离级别,MySQL 可以识别 4 种级别。

SET TRANSACTION ISOLATION LEVEL

事务是由下层存储引擎实现的,因此一个事务使用多种存储引擎是不可靠的。例如事务需要回滚,非事务型的表上的变更无法撤销,这就导致数据库处于不一致的状态。

隐式锁与显式锁

InnoDB 采用两阶段锁定协议,事务执行时随时都可以执行锁定,锁只有在执行 COMMIT 或者 ROLLBACK 时才会释放,并且是同时释放所有的锁,这类锁都是隐式锁定。无论是使用什么存储引擎,都不建议使用 LOCK TABLES 指定显式锁。

多版本并发控制

MVCC 可以认为是行级锁的一个变种,在很多情况下避免了枷锁的操作,大多数 MVCC 都实现了非阻塞的读操作,写操作也只锁定必要的行。MVCC 是通过保存数据在某个时间点的快照来实现,不管执行多长时间,每个事务看到的数据都是一致的。根据事务开始的时间不同,每个事务在同一时刻对于同一张表看到的数据可能不一致。
InnoDB 的 MVCC 是通过每行记录后面保存 2 个隐藏的列来实现,一个列保存行的创建时间,一个列保存行的过期时间。时间的记录形式是系统版本号,每开始一个新事务系统版本号就会自动递增。保存系统版本号使得大部分读操作都不用加锁,这么做使得读取数据操作简单且性能好。
MVCC 仅兼容可重复读和提交读这 2 中隔离级别。

参考资料

《高性能 MySQL》[美]Baron Schwartz,Peter Zaitsev,Vadim Tkachenko 著,宁海元、周振兴、彭立勋、翟卫祥 等译,电子工业出版社
《MySQL Crash Course》[英] Ben Forta 著,刘晓霞 钟鸣 译,人民邮电出版社

posted @ 2020-07-09 19:32  乌漆WhiteMoon  阅读(268)  评论(0编辑  收藏  举报