⭐️前面的话⭐️
本文已经收录到《Spring框架全家桶系列》专栏,本文将介绍有关数据库事务的特点以及隔离级别。
📒博客主页:未见花闻的博客主页
🎉欢迎关注🔎点赞👍收藏⭐️留言📝
📌本文由未见花闻原创,CSDN首发!
📆首发时间:🌴2023年5月20日🌴
✉️坚持和努力一定能换来诗与远方!
💭推荐书籍:📚《无》
💬参考在线编程网站:🌐牛客网🌐力扣🌐acwing
博主的码云gitee,平常博主写的程序代码都在里面。
博主的github,平常博主写的程序代码都在里面。
🍭作者水平很有限,如果发现错误,一定要及时告知作者哦!感谢感谢!
📌导航小助手📌
- 1.事务的基本特性
- 1.1事务的概念
- 1.2事务的使用
- 2.数据库的隔离级别
- 2.1并发操作的时候的问题
- 2.1.1脏读
- 2.1.2不可重复读
- 2.1.3幻读
- 2.1.4丢失更新
- 2.2事务的隔离级别
- 2.2.1读未提交
- 2.2.2读已提交
- 2.2.3可重复读
- 2.2.4串行化执行
- 2.2.5总结
1.事务的基本特性
1.1事务的概念
事务诞生的目的就是将多个独立的操作视作一个整体,要么全部执行,要么全部不执行。
事务的四大特性:ACID
- 原子性:对于一个事务中的所有操作要么全部执行,要不都不执行。事务中任何一个SQL语句执行失败,那么已经执行成功的SQL语句也必须回滚,数据库状态应该退回到执行事务前的状态。
- 一致性:事务执行之前与执行之后都是合理合法的,例如转账,转账支出与转账收入应该相当。
- 持久性:事务提交之后,数据写入硬盘,无论你重启程序还是关机,数据依然存在。
- 隔离性:隔离性描述的是并发执行的时候,出现的情况。
并发执行事务带来的问题:
1.2事务的使用
第一步,开启事务
start transaction;
第二步,执行多条sql语句
# 若干条sql语句
第三步,提交或回滚事务
如果想要完成事务的提交使用commit
,想要完成事务的回滚使用rollback
。
commit/rollback;
2.数据库的隔离级别
2.1并发操作的时候的问题
2.1.1脏读
脏读指的是事务A读到了另外一个事务B未提交的数据,如果事务B发生了回滚,就会造成事务A获取到的数据是脏数据,这就是脏读。
下面来举一个例子说明,账户余额一开始为800,事务A表示有人转入了100元到账户,事务B表示转出300元但失败了,最终账户余额为900才是合理的,但是如果事务A读取到事务B未提交的数据,就会发生错误,这就是脏读可能会造成的危害。
事务A表示转入100元,但是事务B转出操作失败了,但事务A读取到了事务B未提交的数据,导致最终账户余额多扣了300元,事务A读取到事务B未提交的数据,这就是脏读。
2.1.2不可重复读
不可重复读其实和脏读有一点像,脏读读到的是另外一个事务未提交的数据,不可重复读,它读到的是另外一个事务已经提交的数据,造成读取的值不一致的情况。
展开来说就是,事务A读取了某一数据,然后事务B将该数据修改并提交了,事务A再次又读了该数据,但是得到的结果与上一次结果不同,这种现象就是不可重复读。
举个例子,一开始账户余额为500,事务A读取余额为500,然后事务B将钱转出了300元,余额变为200,事务A再次查询余额得到200元,在同一事务中,由于读取到另外一个事务提交的数据发生与第一次数据读取不一致的情况即不可重复读现象。
事务A首先查询到账户余额为500元,然后事务B转出余额300元,导致余额变为200元,事务A再一次查询,得到的查询结果发生了变化,变成200元了,这种读取相同数据不一致的现像,就是不可重复读。
2.1.3幻读
幻读即事务A第一次查询到数据表中符合要求的结果有n
条,然后事务B插入了若干条数据,最后事务A再次同一调条件进行查询,符合要求的结果为m
条,同一事务中,多次查询到合法数据结果不一致的现象就是幻读现象。
举个例子,一开始在用户表中事务A查询到了有3个名叫张三的人,然后事务B向数据库注册了一批新用户,事务A又查询了名叫张三的人,发现查询结果有6条,这就是幻读现象。
事务A首先查询张三有3人,然后事务A执行其他业务的时候,事务B注册了一批新用户,最后事务A再次查询张三的时候,出现了6人,这种在同一事务同样条件查询结果不一致的情况,即幻读。
2.1.4丢失更新
当两个或多个事务操作同一个数据表的时候,可能会发生一个事务没有感应到另外一个事务的更新操作,二造成更新出现错误的现象。
两个事务均进行更新操作,相互影响,某一事务撤销影响最终结果的准确性。
事务B覆盖了事务A的更新,影响最终结果的准确性。
这几种情况在并发的条件下都有可能造成不利后果,如脏读会导致转账的数目不对,不可重复读和幻读都可能会造成最终统计的结果不可靠,第一,二类更新丢失都会造成账户余额更新出现致命错误。
2.2事务的隔离级别
数据库并发访问所产生的问题,在有些场景下可能是允许的,但是有些场景下可能是致命的,数据库通常会通过锁机制来解决数据并发访问问题,按锁对象不同分为表级锁和行级锁;按并发事务锁定关系分为共享锁和独占锁。直接使用锁非常麻烦,为此数据库为用户提供了自动锁机制,用户指定会话的事务隔离级别,数据库就会通过分析SQL语句然后为事务访问的资源加上合适的锁,此外,数据库还会维护这些锁通过各种手段提高系统的性能,这些对用户来讲都是透明的。
简单说,鱼和熊掌不可兼得,速度和可靠性总得有牺牲,为了解决并发时,因地制宜,数据库设置了不同的几种隔离级别,让程序员自己根据实际情况选择一个最佳的事务隔离级别。
数据库事务隔离级别一共有四种,读未提交,读已提交,可重复读,串行化执行,并发程度依次降低,最后一种其实就没有了并发的特性了,但是串行化执行确实是最安全可靠的。
事务的隔离级别如下:
- 读已提交 READ_COMMITTED
- 读未提交 READ_UNCOMMITTED
- 可重复读 REPEATABLE_READ
- 串行化 SERIALIZABLE
- 默认值 DEFAULT: (MySQL: 可重复读、Oracle: 读已提交)
2.2.1读未提交
读已提交( READ_UNCOMMITTED)指的是一个事务能够读取到另外一个事务未提交的数据,脏读,不可重复读,幻读,两类数据丢失更新都有可能发生,但大部分数据库在该事务隔离级别下会使用加锁去避免第一类丢失更新问题。
2.2.2读已提交
读已提交(READ_COMMITTED)指的是一个事务只能等另外一个更新事务提交数据后才能读取数据,保证事务读取的数据一定是已提交的数据,避免了脏读的问题,但是不可重复读和幻读问题依然存在,两类数据丢失更新都有可能发生,但大部分数据库会使用锁机制避免了第一类丢失更新问题。
Oracle等大部分数据库默认隔离级别。
2.2.3可重复读
可重复读(REPEATABLE_READ)指的是一个事务读取某数据时,其他的事务都不能修改该条数据,保证并发时,对于某一行数据,读取到的结果都是一样的,这样就解决了脏读,不可重复读的问题,但由于事务读取数据时,还能插入数据,所以幻读问题依然存在。其实两类数据丢失更新仅仅靠隔离级别的隔离逻辑分析还是会发生,但大部分数据库在可重复读的隔离级别下保证不会发生两类丢失更新的情况。
MySQL默认隔离级别。
2.2.4串行化执行
串行化(SERIALIZABLE)指的是一个事务执行完才能执行另外一个事务,即按照顺序执行,没有并发性,因为效率低,一般不采用该隔离级别。脏读,不可重复读,幻读均不会发生,数据库保证了在该隔离级别下,两种丢失更新不会发生。
四种隔离级别只能解决脏读,不可重复读,幻读三类读取的问题,丢失更新的问题需要靠加锁来解决,仅靠隔离级别是无法解决的。
2.2.5总结
隔离级别和可能发生的现象总结如下:
隔离级别 | 脏读 | 不可重复读 | 幻读 | 第一类丢失更新 | 第二类丢失更新 |
---|---|---|---|---|---|
未提交读 | 允许 | 允许 | 允许 | 不允许 | 允许 |
读写提交 | 不允许 | 允许 | 允许 | 不允许 | 允许 |
可重复读 | 不允许 | 不允许 | 允许 | 不允许 | 不允许 |
串行化 | 不允许 | 不允许 | 不允许 | 不允许 | 不允许 |
注意:事务的隔离级别和数据库并发性是成反比的,隔离级别越高,并发性越低。所以应该根据实际情况选择事务的隔离级别。