MySQL事务学习笔记(三) 甚欢篇

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS MySQL,高可用系列 2核4GB
简介: MySQL事务学习笔记(三) 甚欢篇

会出问题的情况

只是读的话,是不会出现并发问题的,那我们首先将这种情况忽略,那么问题出现的重灾区就是:

  • 写 - 写

即并发事务相继对一条记录进行改动,在这种情况就可能发生脏写。在任何隔离级别下MySQL都不允许脏写发生。所以在多个并发事务对相同记录进行改动的时候,MySQL会让他们排队执行,这个排队执行事实上是通过锁来实现的,类似于现在的面试(不考虑群面这种情况),一般面试官面试完这个人之后,才会叫HR让下个人进来。在MySQL中当一个事务要对某条事务进行改动的时候,首先会看内存中有没有与这条记录关联的锁结构,当没有的时候就会在内存中生成一个锁结构与之相关联。

  • 读 - 写 或 写 - 读

在这种情况下,可能发生脏读、不可重复读、幻读的问题。这里注意一下幻读问题的产生是因为某个事务读取了一个范围的记录,之后其他事务又在该范围内插入新记录,该事务再次读取该范围的记录的时候,可以读到新插入的记录。所以幻读并不是因为读取和写入一条相同的记录而产生。

在前面我们已经唠叨过在不同的隔离级别下可能会发生的问题了,这里再简单的提一下,不再过多的进行赘述,不同数据库厂商对SQL标准的支持可能都不一样,MySQL在 REPEATABLE READ隔离级别实际上就已经解决了幻读问题。

解决方案在MySQL也就是两个:

  • MVCC: 读利用多版本并发控制(MVCC), 写操作进行加锁。
  • 读、写都采取加锁方式

在一些业务场景下,我们不允许读取记录的旧版本,而是每次都必须去读取记录的最新版本。比方说在银行的存款事务中,我们就需要先读取账户余额,然后将其加上本次存款的数额,最后再写入到数据库中。在将账户余额读取出来后,就不想让别的事务再访问该余额。直到本次存款事务执行完成,其他事务才可以访问账户的余额。这样在读取记录的时候也就需要对其进行加锁操作。

为什么呢,我们来分析一下,我们讨论的还是在可重复读这个级别上进行讨论,假设我们以转账业务为例,如果允许读旧的账户余额会发生什么样的问题。

如果是像下面的语句来执行:

UPDATE Student Set money = money + 50 where id = '1';

两次提交并不会产生问题,因为两个事务排队执行。我原先的想法是在执行UPDATE的时候MySQL也读了,这样读到事务提交之前的记录,最终就会是转了两次账,只加了五十块钱。那如果是如果是做运算呢,比如说我们在代码里面做运算,然后最后的更新语句像下面这样就会有问题:

UPDATE Student Set money = 60 where id = '1';

后提交的事务就会把先提交的事务覆盖掉,这并不算脏写,只是两次独立的更新操作。

采用MVCC方式的话,读-写操作并不冲突,性能更好,采用加锁方式的话,需要排队执行,影响性能。一般情况下我们更愿意采取MVCC来解决事务并发执行带来的问题,但是业务在某些情况下,不能接受MVCC,在MySQL里面似乎也只剩下了加锁这一个选项。

事务利用MVCC进行的读取操作称之为一致性读,或者一致性无锁读、快照读。所有的SELECT语句在READ COMMITTED、REPEATABLE READ隔离级别都算是一致性读。

锁概念浅析

行共享锁和独占锁

在MySQL中锁可以大致分为两类:

  • 共享锁  英文名 Shared Locks,简称为S锁。

如果一个事务某个记录上有共享锁, 如果另一个事务也想获取这把锁,也是可以的,这就意味着两个事务在该记录上可以同时持有该锁。

  • 独占锁 也常称 排他锁,英文名: Exclusive Locks , 简称为S锁,

如果事务T1已经获取了该记录的S锁,事务T2想要再获取一条记录的X锁,那么此操作就会被阻塞,直到T1释放掉S锁。

MySQL提供了两种语法来让我们在读取记录的时候就可以获取该记录的X锁、S锁:

SELECT ... LOCK IN SHARE MODE; 
在读取记录的时候获取该记录的共享锁
SELECT ... FOR UPDATE;
在该事务中获取该记录的X锁

如果当前事务执行了获取共享锁的语句(SELECT ... LOCK IN SHARE MODE),那么它会为读取到的记录加S锁,这样允许别的事务继续获取这些记录的S锁(如果其他事务也使用 SELECT ... LOCK IN SHARE MODE;来读取这些记录),但是不能获取这些记录的X锁(比方说使用SELECT ... FOR UPDATE来读取这些记录,或者修改这些记录 ) 如果别的事务想要获取这些记录的X锁,那么它们会阻塞,直到当前事务提交之后将这些记录尚的S锁释放掉。

如果事务中执行了SELECT ... FOR UPDATE, 那么它会为读取到的记录加X锁,别的事务即无法获取这个记录的S锁和X锁,如果事务想要获取这些记录的S锁和X锁,那么它们会阻塞,直到当前事务提交之后将这些记录上的X锁释放掉。

表共享锁和独占锁

上面我们提到的可以认为是行锁,也就是针对若干行进行加锁,这个锁的粒度是比较细的。其实一个事务也可以在表级别进行加锁,自然就被称之为表级锁或者表锁,对表加锁影响整个表中的记录,给表加的锁也可以被分为共享锁(S锁)和独占锁(X锁):

如果一个事务给表加了S锁:

  • 其他事务可以继续获取得该表的S锁
  • 其他事务可以继续获取该表中记录的S锁
  • 别的事务无法获取该表的X锁
  • 如果该行记录上面有S锁,那么其他事务无法获得该行记录的X锁

如果一个事务给表加了X锁(独占这个表):

  • 其他事务无法获得该表的S锁
  • 如果该表中的记录上有X锁,那么其他事务无法获得该记录的S锁。
  • 如果该表中的记录上有S锁,那么其他事务无法获得该记录的X锁
  • 其他事务不可以继续获得该表中的某些记录的X锁。

但这事实上有两个问题,如果MySQl想对表整体上S锁,首先需要确保表中的记录不能有X锁,遍历表的记录?MySQL的开发人员设计了意向锁:

  • 意向共享锁  Intention Shared Lock, 简称为IS锁。当事务准备在某条记录尚加S锁时,需要先在表加上IS锁。
  • 意向独占锁,  Intention Exclusive Lock,简称为IX锁。当事务准备在某条记录上加X锁,需要先在表级别加一个IX锁。

这样就可以避免全表扫表。

表锁概述

在对某个表执行一些ALTER TABLE、DROP TABLE这里的DDL语句时,其他事务对这个表并发执行增删改查会发生阻塞,同理,某个事务对某个表执行SELECT、INSERT、DELETE、UPDATE语句时,其他会话中执行DDL语句也会发生阻塞。这个过程其实是通过Server层使用一种元数据锁(Metadata Locks ,简称为MDL)来实现的,一般情况下也不会使用InnoDB存储引擎提供的表级别S锁、X锁。所以这个表级的S、X锁颇有种鸡肋的感觉。我们重点关注行锁。上面我们唠叨的意向锁也属于表锁。再有就是MySQL的自增属性,在MySQL有两种方式实现自增:

  • 采用AUTO-INC锁,也就是在执行插入语句的时候就在表上加上一个AUTO-INC锁,然后为每条自增属性的赋值。一个事务持有AUTO—INC锁,其他插入事务会被阻塞。插入语句执行之后就被释放。

不确定插入的记录数量可采取AUTO-INC锁。

  • 轻量级锁,在为插入语句生成自增属性列的值之后,就把该轻量级锁释放掉,并不需要等到整个插入语句执行完成才释放锁。
    如果语句执行前就可以确定插入记录数量,一般采用轻量级锁的方式对自增列赋值。

那么该如何选择自增方式呢,InnoDB中有一个innodb_autoinc_lock_mode的系统变量来控制哪两种方式来为自增列赋值,为0时,一律采用AUTO-INC锁,为2时,一律采用轻量级锁(可能会造成不同事务中的插入语句的自增列是交叉的,在有主从复制的场景是不安全的)。当为1时,插入记录数量确定时,采取轻量级锁,不确定时采取AUTO-INC锁。

总结

MySQL锁的概念比我原想的要复杂,知也无涯,生也有涯哉,本来打算这一节将MySQL中的锁大致过一遍,但是到现在还是没有梳理出一条主线将这些内容连在一起。到最后就是只介绍了行锁、表锁的基本概念。


相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
2月前
|
存储 SQL 关系型数据库
Mysql学习笔记(二):数据库命令行代码总结
这篇文章是关于MySQL数据库命令行操作的总结,包括登录、退出、查看时间与版本、数据库和数据表的基本操作(如创建、删除、查看)、数据的增删改查等。它还涉及了如何通过SQL语句进行条件查询、模糊查询、范围查询和限制查询,以及如何进行表结构的修改。这些内容对于初学者来说非常实用,是学习MySQL数据库管理的基础。
134 6
|
2月前
|
存储 SQL 关系型数据库
MySQL的事务隔离级别
【10月更文挑战第17天】MySQL的事务隔离级别
117 43
|
16天前
|
关系型数据库 MySQL
mysql事务特性
原子性:一个事务内的操作统一成功或失败 一致性:事务前后的数据总量不变 隔离性:事务与事务之间相互不影响 持久性:事务一旦提交发生的改变不可逆
|
2月前
|
SQL 关系型数据库 MySQL
Mysql学习笔记(三):fetchone(), fetchmany(), fetchall()详细总结
MySQL中用于数据检索的`fetchone()`, `fetchmany()`, `fetchall()`函数的功能、SQL语句示例和应用场景。
68 3
Mysql学习笔记(三):fetchone(), fetchmany(), fetchall()详细总结
|
2月前
|
SQL Ubuntu 关系型数据库
Mysql学习笔记(一):数据库详细介绍以及Navicat简单使用
本文为MySQL学习笔记,介绍了数据库的基本概念,包括行、列、主键等,并解释了C/S和B/S架构以及SQL语言的分类。接着,指导如何在Windows和Ubuntu系统上安装MySQL,并提供了启动、停止和重启服务的命令。文章还涵盖了Navicat的使用,包括安装、登录和新建表格等步骤。最后,介绍了MySQL中的数据类型和字段约束,如主键、外键、非空和唯一等。
75 3
Mysql学习笔记(一):数据库详细介绍以及Navicat简单使用
|
14天前
|
关系型数据库 MySQL 数据库
MySQL事务隔离级别及默认隔离级别的设置
在数据库系统中,事务隔离级别是一个关键的概念,它决定了事务在并发执行时如何相互隔离。MySQL提供了四种事务隔离级别,每种级别都解决了不同的并发问题。本文将详细介绍这些隔离级别以及MySQL的默认隔离级别。
|
2月前
|
存储 缓存 关系型数据库
MySQL事务日志-Redo Log工作原理分析
事务的隔离性和原子性分别通过锁和事务日志实现,而持久性则依赖于事务日志中的`Redo Log`。在MySQL中,`Redo Log`确保已提交事务的数据能持久保存,即使系统崩溃也能通过重做日志恢复数据。其工作原理是记录数据在内存中的更改,待事务提交时写入磁盘。此外,`Redo Log`采用简单的物理日志格式和高效的顺序IO,确保快速提交。通过不同的落盘策略,可在性能和安全性之间做出权衡。
1674 14
|
3月前
|
存储 Oracle 关系型数据库
Oracle和MySQL有哪些区别?从基本特性、技术选型、字段类型、事务、语句等角度详细对比Oracle和MySQL
从基本特性、技术选型、字段类型、事务提交方式、SQL语句、分页方法等方面对比Oracle和MySQL的区别。
624 18
Oracle和MySQL有哪些区别?从基本特性、技术选型、字段类型、事务、语句等角度详细对比Oracle和MySQL
|
2月前
|
SQL 关系型数据库 MySQL
阿里面试:MYSQL 事务ACID,底层原理是什么? 具体是如何实现的?
尼恩,一位40岁的资深架构师,通过其丰富的经验和深厚的技術功底,为众多读者提供了宝贵的面试指导和技术分享。在他的读者交流群中,许多小伙伴获得了来自一线互联网企业的面试机会,并成功应对了诸如事务ACID特性实现、MVCC等相关面试题。尼恩特别整理了这些常见面试题的系统化解答,形成了《MVCC 学习圣经:一次穿透MYSQL MVCC》PDF文档,旨在帮助大家在面试中展示出扎实的技术功底,提高面试成功率。此外,他还编写了《尼恩Java面试宝典》等资料,涵盖了大量面试题和答案,帮助读者全面提升技术面试的表现。这些资料不仅内容详实,而且持续更新,是求职者备战技术面试的宝贵资源。
阿里面试:MYSQL 事务ACID,底层原理是什么? 具体是如何实现的?
|
2月前
|
关系型数据库 MySQL 数据库
Mysql学习笔记(四):Python与Mysql交互--实现增删改查
如何使用Python与MySQL数据库进行交互,实现增删改查等基本操作的教程。
69 1