【MySQL】事务?隔离级别?锁?详解MySQL并发控制机制

本文涉及的产品
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
简介: 【MySQL】事务?隔离级别?锁?详解MySQL并发控制机制

1.先理清一下概念

所谓并发控制指的是在对数据库进行并发操作时如何保证数据的一致性和正确性。在数据库中与并发控制相关的概念有如下几个:

  • 事务
  • 隔离界别

这几个概念大家应该都知道,但是我猜很多人没有把它们串在一起搞明白他们之间的关系,导致这三个概念各是各的,造成记忆负担,最后对整个数据库并发控制的体系也云里雾里的。


锁与事务的关系:


在计算机科学中,做并发控制都是用的“标志位”来实现的,说直白一点就是锁,我们基本上可以说计算机科学中并发控制的底层都是锁的思想。在数据库中也不例外,也是通过锁来实现并发控制的。使用上锁之后,整个数据库的读写都会对外呈现出一些特质,这些特质就是事务(ACID),原子性、一致性、隔离性、持久性。


锁与隔离级别的关系:


当然锁有很多,在数据库中有行锁、表锁、读锁、写锁等等......不同级别的锁,ACID这些特质的强弱不同,这个强弱的级别就是“隔离级别”。


所以综合起来说就是用锁来实现并发控制,对外会呈现出事务(ACID),锁的级别的不同,ACID的强弱也不同,隔离级别对应也不同。


三个东西的关系理清楚后,来回顾一下事务和隔离级别的具体内容。


事务:


事务是为了保证SQL之间不产生脏数据。innodb中默认没有被包裹在事务中的一个单条SQL就是一个事务。事务是一类特征的总称,合起来为ACID:


原子性 (Atomicity):一个事务是一个不可分割的工作单元;事务中的所有操作要么全部成功执行,要么全部不执行。如果事务中的任何部分失败,则整个事务将被回滚到事务开始前的状态。

一致性 (Consistency):在事务开始和结束时,数据库都必须处于一致状态。这意味着事务的执行不会违反任何数据库约束或规则,并且会保持业务逻辑上的一致性。一旦事务完成,它应该使数据库从一个有效状态变为另一个有效状态。

隔离性 (Isolation):并发执行的多个事务之间互不干扰,如同它们是按顺序独立执行一样。这防止了脏读(读取未提交的数据)、不可重复读(在同一事务内多次读取同一数据时结果不同)和幻读(在事务中多次执行相同的查询时,由于其他已提交事务的插入或删除操作导致结果集发生变化)等现象。

持久性 (Durability):当事务成功提交后,其对数据库所做的修改将会永久保存,即使系统发生故障(如崩溃、重启等)也不会丢失这些修改。持久性通常是通过日志记录和恢复机制来保证的。

隔离级别:


事务内具有原子性,但是事务间不具有原子性,并发情况下,多事务间仍然会存在脏数据的问题。


1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据。


2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。


3、幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。

隔离级别就是为了保证事务之间不产生脏数据。

2.锁

2.1.分类

按照对数据的操作类型分类,分为:读锁、写锁。

按照对数据操作的粒度分类,分为:行锁、表锁、

表锁:


开销小、加锁快,无死锁,锁的粒度大,发生锁冲突的概率最高,并发度最低。


读锁:


又叫共享锁,针对同一份数据,多个读操作可以同时进行而不互相影响。针对被锁表,所有客户端都可以进行读操作,所有客户端都无法进行写操作,加锁方和其他客户端的区别是,加锁方直接不允许进行写操作,而其他客户端的写操作允许进行,只是会被阻塞挂起。锁解开后,所有挂起的操作线程会去重新争抢资源。


写锁:


又叫排它锁,当前写操作没有完成前,它会阻断其他写锁和读锁。针对被锁表,加锁方可以读写,其他客户端不行。其他客户端的写操作会直接失败,读操作会被阻塞挂起,解锁以后,被挂起的线程会重新去争抢资源。


保护机制:


读锁、写锁中,加锁方都只能读当前被自己锁定的表,这是MySQL的一个保护机制,为的就是强制要求加锁方给出一个说法,到底准备锁多久,不给说法不让走。

MySQL中不同引擎支持不同级别的锁,myIsam支持表锁,innodb支持行锁。

2.2.表锁

表锁,我们当然是要基于使用表锁的存储引擎来聊,也就是基于myIsam引擎。

myIsam引擎中的读写锁是自动加的。

myIsam引擎在解锁后的阻塞队列中进行读写锁调度是写优先,这样一旦阻塞队列中大量都是写操作,那么读操作会很难得到锁,变得很慢,从而造成永久堵塞。

当然除了自动加锁,表锁可以通过指令来加锁。

查看所有锁:

show open tables

解锁:

解铃还须系铃人,只有加锁方能解锁。

unlock  tables

加读锁:

lock table 表名 read

加写锁

lock table 表名 write

2.3.行锁(MVCC)

行锁,我们当然是要基于使用行锁的存储引擎来聊,也就是基于innodb引擎。innodb和myIsam最大的不同有两点,一是支持事务,二是采用了行级锁。innodb下读写都是自动加行锁,这没有什么好说的。但是行锁因为粒度太细了,会影响效率的,innodb没有傻傻的只用了行锁,还给出了行锁的优化方案——MVCC。


MVCC,多版本并发控制,本质上就是使用行锁锁定数据。


对于锁的实现方式来说无非就两种


悲观锁: 基于锁的并发控制,认为数据大概率会存在并发问题,所以进行数据更改前先给要操作的数据加上锁。程序员B开始修改数据时,给这些数据加上锁,程序员A这时再读,就发现读取不了,处于等待情况,只能等B操作完才能读数据,这保证A不会读到一个不一致的数据,但是这个会影响程序的运行效率。

乐观锁:基于版本号的并发控制,认为数据大概率不会存在并发问题,所以在进行数据操作的时候不加锁,而是在提交修改操作的时候通过版本号来确定数据有没有被第三方动过。

对于行级锁这种粒度这么细的锁,选用悲观锁性能肯定是不佳的,所以mvcc其实选用的都是乐观锁。


举个例子:


当使用mysql的innodb模式的时候,在mvcc的机制下,当事务A读取了表a的快照,在事务A提交前事务B又去读取了表a并且修改了数据,接下来事务A提交时会有什么效果,直接失败回滚事务还是?


在这种情况下:

事务A提交时,InnoDB会检查事务A对数据进行更新时的数据版本是否与事务A最初读取数据时的版本一致。如果事务B修改的数据行与事务A无关,那么事务A可以正常提交,因为它们操作的是不同的数据行。

如果事务B修改的数据行正好是事务A准备更新的那部分数据,由于事务A处于可重复读隔离级别,它看到的是先前的版本,不会看到事务B的修改。当事务A尝试提交时,InnoDB的事务处理机制会确保事务的一致性,通常有两种可能的结果:

a. 如果InnoDB检测到事务A试图更新的数据已经被事务B更新过(即版本冲突),事务A的更新操作可能会导致一个死锁错误(Deadlock),在这种情况下,InnoDB会选择回滚其中一个事务以解决死锁问题,通常会回滚较晚开始或者较晚修改数据的事务(在这里可能是事务A)。

b. 如果InnoDB的并发控制机制足够强大(例如使用Next-Key Locks来防止幻读),事务A在更新数据时就已经被阻止,也就是说事务A在尝试更新数据时就会被阻塞,直到事务B完成并释放锁,事务A才能继续,进而根据实际情况决定是成功提交还是因数据已改变而回滚。

2.4.如何进行事务间数据的强制同步

MySQL的MVCC是在提交的时候才比对数据是否又修改,那么有没有办法让所有事务间的数据都是强制同步的喃?有!通过关闭自动提交改为手动提交来保证修改的顺序性。


数据库默认开启自动提交,通过set autocommit=0可以关闭自动提交。


关闭后每次执行sql以后,通过commit命令来手动提交,才会对数据库产生影响,否则只会对当前操作方的数据快照有影响。innodb引擎中,其他客户端想查看到最新的数据情况也必须通过commit指令来做一次同步(因为innodb默认隔离级别为可重复读)。


当一个客户端修改某行数据,未commit前,其他客户端对该行数据的修改会阻塞挂起,直到先改那个用户commit为止。

2.5.间隙锁

使用范围条件匹配时,innodb会给符合条件的已有数据记录的索引加“范围锁”(范围锁是特殊的行锁),对于键值在条件范围内但并不存在的记录,叫做间隙(GAP),innodb也会对这个间隙加锁,这种机制叫做“间隙锁”


2.6.行锁变表锁

任何需要全表扫描的情况时,行锁都会升级为表锁。

因为MySQL不知道到底该锁哪行,所以会将整个表都锁起来,然后再进行全表扫描。


全表扫描的情况无非两种:

  1. 没建索引。
  2. 索引失效。

2.7.强制锁行

通过 for update 可以在无update操作下,强制锁定一行。

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
目录
相关文章
|
3月前
|
SQL 关系型数据库 MySQL
MySQL锁机制:并发控制与事务隔离
本文深入解析了MySQL的锁机制与事务隔离级别,涵盖锁类型、兼容性、死锁处理及性能优化策略,助你掌握高并发场景下的数据库并发控制核心技巧。
|
4月前
|
存储 监控 Oracle
MySQL事务
MySQL事务具有ACID特性,包括原子性、一致性、隔离性和持久性。其默认隔离级别为可重复读,通过MVCC和间隙锁解决幻读问题,确保事务间数据的一致性和并发性。
MySQL事务
|
5月前
|
存储 SQL 关系型数据库
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
|
7月前
|
SQL AliSQL 关系型数据库
MYSQL的全局锁和表锁
本文介绍了MySQL中的锁机制,包括全局锁、表级锁及其应用场景。全局锁通过`Flush tables with read lock (FTWRL)`实现,主要用于全库逻辑备份,但会阻塞更新和结构变更操作。表级锁分为显式表锁(`lock tables`)和元数据锁(MDL),前者用于控制并发访问,后者自动加锁以确保读写正确性。文章还探讨了如何安全地为小表添加字段,建议通过设置DDL等待时间或使用MariaDB/AliSQL的NOWAIT/WAIT功能避免业务阻塞。这些方法有助于在高并发场景下优化数据库性能与安全性。
204 0
|
2月前
|
关系型数据库 MySQL 数据库
【赵渝强老师】MySQL的事务隔离级别
数据库并发访问时易引发数据不一致问题。如客户端读取到未提交的事务数据,可能导致“脏读”。MySQL通过四种事务隔离级别(读未提交、读已提交、可重复读、可序列化)控制并发行为,默认为“可重复读”,以平衡性能与数据一致性。
298 0
|
3月前
|
关系型数据库 MySQL 数据库
MySql事务以及事务的四大特性
事务是数据库操作的基本单元,具有ACID四大特性:原子性、一致性、隔离性、持久性。它确保数据的正确性与完整性。并发事务可能引发脏读、不可重复读、幻读等问题,数据库通过不同隔离级别(如读未提交、读已提交、可重复读、串行化)加以解决。MySQL默认使用可重复读级别。高隔离级别虽能更好处理并发问题,但会降低性能。
189 0
|
5月前
|
安全 关系型数据库 MySQL
mysql事务隔离级别
事务隔离级别用于解决脏读、不可重复读和幻读问题。不同级别在安全与性能间权衡,如SERIALIZABLE最安全但性能差,READ_UNCOMMITTED性能高但易导致数据不一致。了解各级别特性有助于合理选择以平衡并发性与数据一致性需求。
192 1
|
3月前
|
缓存 关系型数据库 BI
使用MYSQL Report分析数据库性能(下)
使用MYSQL Report分析数据库性能
171 3
|
3月前
|
关系型数据库 MySQL 数据库
自建数据库如何迁移至RDS MySQL实例
数据库迁移是一项复杂且耗时的工程,需考虑数据安全、完整性及业务中断影响。使用阿里云数据传输服务DTS,可快速、平滑完成迁移任务,将应用停机时间降至分钟级。您还可通过全量备份自建数据库并恢复至RDS MySQL实例,实现间接迁移上云。
|
3月前
|
关系型数据库 MySQL 数据库
阿里云数据库RDS费用价格:MySQL、SQL Server、PostgreSQL和MariaDB引擎收费标准
阿里云RDS数据库支持MySQL、SQL Server、PostgreSQL、MariaDB,多种引擎优惠上线!MySQL倚天版88元/年,SQL Server 2核4G仅299元/年,PostgreSQL 227元/年起。高可用、可弹性伸缩,安全稳定。详情见官网活动页。
851 152