目录
场景(两个用户之间进行转账操作):
需要的操作步骤:
事务
事务的四大特性:
一、原子性
(1)什么是回滚操作
(2)数据库恢复操作,如何知道数据恢复如初?(如何知道数据回滚正确)
二、一致性
三、持久性
四、隔离性(重点)
1. 脏读
解决脏读:
2. 不可重复读
解决不可重复读:
3. 幻读
解决幻读:
总结并发处理事务的三个典型问题:
MySQL内置的机制(对应上述几种隔离级别)
前言
数据库事务的相关知识也是面试常考的问题,这篇文章介绍为什么要有事务以及事务的概念,事务的特性,以及数据库基于隔离性(特性)产生并发执行所引出的问题,如何解决这个问题。
场景(两个用户之间进行转账操作):
用户A要给用户B进行转账,就要操作数据库中的数据表;如下图:
需要的操作步骤:
(1)update acccount set balance = balance - 500 where id = 1; |
(2)update account set balance = balance + 500 where id = 2; |
假设在执行转账的过程中,执行完sql语句(1)之后,数据库崩溃了/主机宕机了,此时用户1的钱扣了,但是2的钱没到账。
那针对这样的问题数据库该如何解决?
事务
事务就是为了解决上述问题,事务的本质就是把多个sql语句给打包成一个整体,要么全部执行成功,要么就一个都不执行,而不会出现 ”执行一半“ 这样的中间状态。(这就是事务出现的初心)
事务的四大特性:
1. 原子性(回滚) | 上述 ”一点都补执行“ 不是真正的没有执行,而是在中间状态进行了 ”回滚“ 操作 |
2. 一致性 | 数据进行修改之后,要和之前的数据能够对应上。 |
3. 持久性 | 数据持久保存 |
4.隔离性(重点介绍) | 这个特性是为了解决事务 ”并发执行“ 引起的问题 |
一、原子性
原子性是数据库设计事务的初心,这点要清楚,上述场景出现后,事务就是为了解决这个问题,
事务的本质就是把多个要执行的sql语句打包成一个整体,使事务变成要执行的一个基本单位,不能再分。(要么就不执行这个事务,要么就一次执行完事务)
(1)什么是回滚操作
当一个事务执行到一半时,此时数据库崩溃了,数据库在下一次启动时就要把未执行完的数据恢复到之前的状态,这个恢复数据的操作,称为 ”回滚“。
注:上述 ”事务要么一点都不执行“,不是真正的没有执行,而是说看起来好像没有执行一样,事务执行一半,数据库崩溃,数据库再次启动后,此时选择恢复数据,就是把未执行完的数据还原成了未执行之前的状态了。
(2)数据库恢复操作,如何知道数据恢复如初?(如何知道数据回滚正确)
数据库中有专门记录事务的日志,用日志来记录事务的操作步骤。日志就是写到磁盘上的文件,断电之后,日志也不会消失,所以数据库重新启动后,就把数据进行正确的恢复。
注:正因如此,使用事务后,执行sql语句的开销也就更大了,效率也更低了。
二、一致性
执行sql语句前后的数据要能够保证正确性。
数据执行的前后,数据时靠谱的,也就是能够和正确的操作数据能够对应上,如:用户1给用户2转账500,此时2的账户上就多了500,不能多出来5000,50000。
没有引入事务的时候,这个 特性就是要必须保证的,有了事务之后,这个特性也不能丢失。
三、持久性
事务修改的内容是写到硬盘上的,持久存在的,数据库重启之后,也能保证数据没有丢失。
还是转账这个场景,此时如果数据库崩溃之后,所有的用户的余额都是保存在数据库中的,此时不能说数据库崩了,用户的钱也跟着一起没了,这是不靠谱的,所以说数据库中的数据是持久保存的。
四、隔离性(重点)
解决事务 “ 并发”执行引起的问题。
注:数据库是客户端服务器这样的一个结构,数据库也是服务器;就有可能出现多个客户端同时提交给数据库多个事务,此时数据库就需要并发处理多个事务。
如果是修改的同一张表中的同一个数据,此时就有可能出现问题,如:多个客户尝试对同一个账户进行转账操作,就有可能把这个数据搞乱。
并发执行事务可能产生的问题:
(1)脏读问题 |
(2)不可重复读问题 |
(3)幻读问题 |
1. 脏读
一个事务 正在对数据进行修改的过程中,还没有提交数据之前,另外一个事务B也对同一个数据进行读取,此时B的读数据操作称为 “脏读”,读到的数据也是称为 “脏数据”,(脏的意思是无效,就是读到的数据是无效的数据)
解决脏读:
mysql引入“给写操作加锁” 的机制
(多线程中同步锁问题,详解可以看这篇文章:锁策略和 cas 优化过程_良月初十♧的博客 )
加锁之后写操作和读操作就不能并发执行了(不能同时进行),给写操作加锁,就降低了并发的程度(也降低了效率),提高了隔离性(提高了数据的准确性)。
2. 不可重复读
事务1已经提交了数据,此时事务2开始去读数据,在读的过程中,事务3又提交了新的数据,此时意味着同一个事务2之内,多次读数据读出来的结果是不一样的(预期是一个事务中,多次读取的结果是一样的),此时就称为 “不可重复读”。
解决不可重复读:
给读操作也进行加锁,通过给读加锁,又进一步降低了事务的并发处理能力(处理效率也降低),提高事务的隔离性(数据的准确性又提高了)。
3. 幻读
在给读和写数据加锁的前提下,一个事务两次读取同一个数据,发现读取的数据内容是一样的,但是结果集不一样,此时称为 “幻读”。
解决幻读:
数据库使用串行化的方式来解决幻读,彻底放弃并发执行,一个接着一个的串行的处理事务,此时并发程度是最低的(效率是最慢的),隔离性是最高的(数据的准确性也是最高的)。、
总结并发处理事务的三个典型问题:
1. 脏读问题 | 给读加锁 |
2. 不可重复读问题 | 给写加锁 |
3. 幻读 | 彻底执行串行化 |
MySQL内置的机制(对应上述几种隔离级别)
1. read uncommitted | 没有进行任何话锁限制并发最高(效率最高),隔离性最低(数据的准确性最低) |
2. read committed | 给写加锁了,并发程度 降低,隔离性提高了(数据准确性提高) |
3. repeatable read | 给写和读都进行加锁,并发程度进一步降低,隔离性进一步提高 |
4. serializable | 串行化,并发程度最低,隔离性最高 |
可以通过修改MySQL的配置文件来设置当前MySQL是工作在哪种情况下。