MySQL基础知识点整理 - 事务
来源:SegmentFault
时间:2023-01-27 18:27:51 377浏览 收藏
编程并不是一个机械性的工作,而是需要有思考,有创新的工作,语法是固定的,但解决问题的思路则是依靠人的思维,这就需要我们坚持学习和更新自己的知识。今天golang学习网就整理分享《MySQL基础知识点整理 - 事务》,文章讲解的知识点主要包括MySQL、数据库、PHP、python,如果你对数据库方面的知识点感兴趣,就不要错过golang学习网,在这可以对大家的知识积累有所帮助,助力开发能力的提升。
MySQL基础知识点整理 - 事务
一、简介
1. 什么是事务
事务就是一组原子性的SQL查询,或者说一个独立的工作单元。如果数据库引擎能够成功地对数据库应用该组查询的全部语句,那么就执行该组查询。如果其中有任何一条语句因为崩溃或其他原因无法执行,那么所有的语句都不会执行。也就是说,事务内的语句,要么全部执行成功,要么全部执行失败。在整个过程中,无论事务是否成功完成,总能确保数据的完整性。
2. 如何使用事务
银行应用是解释事务必要性的一个经典例子。
假设一个银行的数据库有两张表:支票(checking)表和储蓄(savings)表。现在要从用户的支票账户转移 200 元到他的储蓄账户,那么需要至少三个步骤:
- 1.检查支票账户的余额高于 200 元。
- 2.从支票账户余额中减去 200 元。
- 3.在储蓄账户余额中增加 200 元。
上述三个步骤的操作必须打包在一个事务中,任何一个步骤失败,则必须回滚所有的步骤。
可以用
START TRANSACTION语句开始一个事务,然后要么使用
COMMIT提交事务将修改的数据持久保留,要么使用
ROLLBACK撤销所有的修改。
事务SQL的样本如下:
/* 开始事务 */ START TRANSACTION; /* 检查支票账户的余额高于 200 元 */ SELECT balance FROM checking WHERE customer_id=10233276; /* 从支票账户余额中减去 200 元 */ UPDATE checking SET balance=balance-200.00 WHERE customer_id=10233276; /* 在储蓄账户余额中增加 200 元 */ UPDATE savings SET balance=balance+200.00 WHERE customer_id=10233276; /* 提交事务 */ COMMIT;
二、事务的 ACID 特性
ACID表示原子性 (atomicity)、一致性(consistency)、隔离性(isolation)和持久性(durability)。一个运行良好的事务处理系统,必须具备这些标准特征。
1. 原子性 (atomicity)
一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚。对于一个事务来说,不可能只执行其中的一部分操作,这就是事务的原子性。
2. 一致性(consistency)
数据库总是从一个一致性的状态转换到另外一个一致性的状态。在前面的例子中,一致性确保了,即使在执行第三、四条语句之间时系统崩溃,支票账户中也不会损失200元,因为事务最终没有提交,所以事务中所做的修改也不会保存到数据库中。
3. 隔离性(isolation)
通常来说,一个事务所做的修改在最终提交以前,对其他事务是不可见的。在前面的例子中,当执行完第三条语句、第四条语句还未开始时,此时有另外一个账户汇总程序开始运行,则其看到的支票账户的余额并没有被减去200元。
4. 持久性(durability)
一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。
三、事务的隔离级别
1. READ UNCOMMITTED(未提交读)
在
READ UNCOMMITTED级别,事务中的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,这也被称为脏读(Dirty Read) 。这个级别会导致很多问题,从性能上来说,
READ UNCOMMITTED不会比其他的级别好太多,但却缺乏其他级别的很多好处,除非真的有非常必要的理由,在实际应用中一般很少使用。
2. READ COMMITTED(提交读)
大多数数据库系统的默认隔离级别都是
READ COMMITTED(但MysQL不是)。
READ COMMITTED满足前面提到的隔离性的简单定义:一个事务开始时,只能“看见”已经提交的事务所做的修改。换句话说,一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的。这个级别有时候也叫做不可重复读(
nonrepeatable read),因为两次执行同样的查询,可能会得到不一样的结果。
3. REPEATABLE READ(可重复读)
REPEATABLE READ解决了脏读的问题。该级别保证了在同一个事务中多次读取同样记录的结果是一致的。但是理论上,可重复读隔离级别还是无法解决另外一个幻读(
Phantom Read)的问题。所谓幻读,指的是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行(
Phantom Row)。
InnoDB和
XtraDB存储引擎通过多版本并发控制(MVCC, Multiversion Concurrency Control)解决了幻读的问题。
可重复读是MySQL的默认事务隔离级别。
4. SERIALIZABLE(可串行化)
SERIALIZABLE是最高的隔离级别。它通过强制事务串行执行,避免了前面说的幻读的问题。简单来说,
SERIALIZABLE会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用的问题。实际应用中也很少用到这个隔离级别,只有在非常需要确保数据的一致性而且可以接受没有并发的情况下,才考虑采用该级别。
四种隔离级别对比
隔离级别 | 脏读可能性 | 不可重复读可能性 | 幻读可能性 | 加锁读 |
---|---|---|---|---|
READ UNCOMMITTED | √ | √ | √ | × |
READ COMMITTED | × | √ | √ | × |
REPEATABLE READ | × | × | √ | × |
SERIALIZABLE | × | × | × | √ |
四、死锁
1. 什么是死锁
死锁是指两个或者多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环的现象。当多个事务试图以不同的顺序锁定资源时,就可能会产生死锁。多个事务同时锁定同一个资源时,也会产生死锁。
2. 死锁实例
设想下面两个事务同时处理 StockPrice 表:
- 事务1:
START TRANSACTION; UPDATE StockPrice SET close=45.50 WHERE stock_id=4 and date='2002-05-01'; UPDATE StockPrice SET close=19.80 WHERE stock_id=3 and date='2002-05-02'; COMMIT;
- 事务2
START TRANSACTION; UPDATE StockPrice SET high=20.12 WHERE stock_id=3 and date='2002-05-02'; UPDATE StockPrice SET high=47.20 WHERE stock_id=4 and date='2002-05-01'; COMMIT;
如果凑巧,两个事务都执行了第一条
UPDATE语句,更新了一行数据,同时也锁定了该行数据,接着每个事务都尝试去执行第二条
UPDATE语句,却发现该行已经被对方锁定,然后两个事务都等待对方释放锁,同时又持有对方需要的锁,则陷入死循环。除非有外部因素介入才可能解除死锁。
3. 如何解决死锁问题
为了解决死锁问题,数据库系统实现了各种死锁检测和死锁超时机制。越复杂的系统,比如
InnoDB存储引擎,越能检测到死锁的循环依赖,并立即返回一个错误。这种解决方式很有效,否则死锁会导致出现非常慢的查询。还有一种解决方式,就是当查询的时间达到锁等待超时的设定后放弃锁请求,这种方式通常来说不太好。
InnoDB目前处理死锁的方法是,将持有最少行级排他锁的事务进行回滚(这是相对比较简单的死锁回滚算法)。
锁的行为和顺序是和存储引擎相关的。以同样的顺序执行语句,有些存储引擎会产生死锁,有些则不会。死锁的产生有双重原因:有些是因为真正的数据冲突,这种情况通常很难避免,但有些则完全是由于存储引擎的实现方式导致的。死锁发生以后,只有部分或者完全回滚其中一个事务,才能打破死锁。对于事务型的系统,这是无法避免的,所以应用程序在设计时必须考虑如何处理死锁。大多数情况下只需要重新执行因死锁回滚的事务即可。
五、MySQL 事务的特点
1. 自动提交(AUTOCOMMIT)
MySQL默认采用自动提交(
AUTOCOMMIT)模式。也就是说,如果不是显式地开始一个事务,则每个查询都被当作一个事务执行提交操作。在当前连接中,可以通过设置
AUTOCOMMIT变量来启用或者禁用自动提交模式:
mysql> SHOW VARIABLES LIKE 'AUTOCOMMIT'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | ON | +---------------+-------+ 1 row in set (0.00 sec) mysql> SET AUTOCOMMIT=0; Query OK, 0 rows affected (0.00 sec) mysql> SHOW VARIABLES LIKE 'AUTOCOMMIT'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | OFF | +---------------+-------+ 1 row in set (0.00 sec) mysql]> SET AUTOCOMMIT=1; Query OK, 0 rows affected (0.00 sec) mysql> SHOW VARIABLES LIKE 'AUTOCOMMIT'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | ON | +---------------+-------+ 1 row in set (0.00 sec)
1 或者 ON 表示启用,0 或者 OFF表示禁用。当
AUTOCOMMIT=0时,所有的查询都是在一个事务中,直到显式地执行
COMMIT提交或者
ROLLBACK回滚,该事务结束,同时又开始了另一个新事务。修改
AUTOCOMMIT对非事务型的表,比如
MyISAM或者内存表,不会有任何影响。对这类表来说,没有
COMMIT或者
ROLLBACK的概念,也可以说是相当于一直处于
AUTOCOMMIT启用的模式。
另外还有一些命令,在执行之前会强制执行
COMMIT提交当前的活动事务。典型的例子,在数据定义语言(DDL)中,如果是会导致大量数据改变的操作,比如
ALTER TABLE,就是如此。另外还有
LOCK TABLES等其他语句也会导致同样的结果。如果有需要,请检查对应版本的官方文档来确认所有可能导致自动提交的语句列表。MySQL可以通过执行
SET TRANSACTION ISOLATION LEVEL命令来设置隔离级别。新的隔离级别会在下一个事务开始的时候生效。可以在配置文件中设置整个数据库的隔离级别,也可以只改变当前会话的隔离级别:
mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
2. 隐式和显式锁定
InnoDB采用的是两阶段锁定协议(two-phase locking protocol)。在事务执行过程中,随时都可以执行锁定,锁只有在执行
COMMIT或者
ROLLBACK的时候才会释放,并且所有的锁是在同一时刻被释放。前面描述的锁定都是隐式锁定,
InnoDB会根据隔离级别在需要的时候自动加锁。
另外,
InnoDB也支持通过特定的语句进行显式锁定,这些语句不属于SQL规范(这些锁定提示经常被滥用,实际上应当尽量避免使用)
- SELECT ... LOCK IN SHARE MODE
- SELECT ... FOR UPDATE
MySQL 也支持
LOCK TABLES和
UNLOCK TABLES语句,这是在服务器层实现的,和存储引擎无关。它们有自己的用途,但并不能替代事务处理。如果应用需要用到事务,还是应该选择事务型存储引擎。
经常可以发现,应用已经将表从
MyISAM转换到
InnoDB,但还是显式地使用
LOCK TABLES语句。这不但没有必要,还会严重影响性能,实际上
InnoDB的行级锁工作得更好。
LOCK TABLES和事务之间相互影响的话,情况会变得非常复杂,在某些 MySQL 版本中甚至会产生无法预料的结果。因此,建议除了事务中禁用了
AUTOCOMMIT,可以使用
LOCK TABLES之外,其他任何时候都不要显式地执行
LOCK TABLES,不管使用的是什么存储引擎。
参考《高性能MySQL》
今天关于《MySQL基础知识点整理 - 事务》的内容就介绍到这里了,是不是学起来一目了然!想要了解更多关于mysql的内容请关注golang学习网公众号!
-
499 收藏
-
244 收藏
-
235 收藏
-
157 收藏
-
101 收藏
-
273 收藏
-
269 收藏
-
265 收藏
-
233 收藏
-
388 收藏
-
475 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习
-
- 虚幻的汉堡
- 这篇文章内容出现的刚刚好,大佬加油!
- 2023-05-10 10:25:01
-
- 曾经的滑板
- 这篇博文真是及时雨啊,很详细,很好,收藏了,关注博主了!希望博主能多写数据库相关的文章。
- 2023-05-03 19:43:57
-
- 复杂的店员
- 这篇技术贴出现的刚刚好,太全面了,很好,码住,关注师傅了!希望师傅能多写数据库相关的文章。
- 2023-04-07 00:28:26
-
- 成就的灰狼
- 赞 👍👍,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢师傅分享文章!
- 2023-03-25 10:19:35
-
- 儒雅的心情
- 细节满满,已加入收藏夹了,感谢作者的这篇技术文章,我会继续支持!
- 2023-02-21 03:33:07
-
- 无奈的外套
- 太给力了,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,帮助很大,总算是懂了,感谢大佬分享文章!
- 2023-02-18 09:47:26
-
- 个性的电脑
- 这篇博文太及时了,作者大大加油!
- 2023-02-13 19:00:17
-
- 殷勤的羊
- 这篇博文出现的刚刚好,好细啊,赞 👍👍,收藏了,关注楼主了!希望楼主能多写数据库相关的文章。
- 2023-02-13 02:30:50
-
- 迷路的跳跳糖
- 太全面了,mark,感谢博主的这篇技术贴,我会继续支持!
- 2023-02-12 17:39:50
-
- 内向的斑马
- 感谢大佬分享,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢up主分享技术贴!
- 2023-01-29 21:38:53