- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Java Spring事务的隔离级别详解由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
之前关于事务的文章已介绍了事务的概念以及事务的四个属性(ACID),相信你对事务应该有所认识和了解.
本篇文章是关于事务的隔离性,介绍数据库提供的多种隔离级别.
。
所谓事务的隔离性,其实事务的这个属性是针对数据库访问的并发性问题而言的.
那何谓数据库访问的并发性问题呢?
所谓数据库访问的并发性问题是指多个事务可以同时访问数据库中的数据,而当多个事务在数据库中并发执行(同时执行)时,数据的一致性可能受到破坏,从而导致数据出现问题.
还是举上次转账那个例子吧! 。
假设你的账号上有 1000 元,你转账给朋友 100 元,然后又向账号汇入 100 元,请问你的账号上余额是多少?是不是太简单了,小学生都会算,当然还是 1000 元,对吧。 整个流程如下:
现在,假设你向朋友的转账和汇款是同时(并发)进行的,整个流程可能如下:
那么,结果是现在账号余额居然是 1100 元。显然,正确的结果应该是 1000 元,呵呵,这就是数据库访问的并发性问题.
其实,数据库访问的并发性问题有很多种情况,以上这种情况只是其中的一种叫更新丢失.
如果多个事务同时(并发)对数据库表中的同一条记录进行修改,那么后修改的记录将会覆盖前面修改的记录,前面的修改就丢失掉了,这就叫做更新丢失。如下图:
那遇到这种情况怎么解决呢,其实更新丢失和多线程同步很相似,所以解决方法也是一样的,那就是对行加锁,同时只允许一个事务访问数据库。什么意思呀?很简单,就是加锁以后,两个事务即使同时访问数据库,也只允许加锁的事务先访问,另一个未加锁的在外等待,直到释放锁后才能访问数据库。这样,其实就是将并行访问数据库变成了串行访问数据库,是不是和多线程同步加同步锁一个道理呀.
所谓脏读就是一个事务 A 读取另一个事务 B 修改但尚未提交的数据并在此基础上操作,而事务 B 又执行事务回滚(也就是撤销了事务),那么事务 A 读取到的数据就是脏数据,如下图:
想一想怎么解决这个问题呢?解决办法很简单,就是在第一个事务提交前,任何其他事务不可读取其修改过的值,则可以避免该问题.
所谓不可重复读就是一个事务对同一行数据重复读取两次,但是却得到了不同的结果。事务T1读取某一数据后,事务T2对其做了修改,当事务T1再次读该数据时得到与前一次不同的值,如下图:
解决办法也很简单,如果只有在修改事务完全提交之后才可以读取数据,则可以避免该问题.
所谓幻象读指两次执行同一条 select 语句会出现不同的结果。这个很好理解,当第一次执行 select 语句后,接着另一个事务执行了 insert 语句(也就是插入了一条记录),这时第二次执行相同的 select 语句,返回的结果自然与第一次不同,这就是幻象读。再举个例子吧,目前工资为 10000 元 的员工有 10 人。那么事务 A 中读取所有工资为 10000 元的员工,得到了 10 条记录;这时事务 B 向员工表插入了一条员工记录,工资也为 10000 元;那么事务 A 再次读取所有工资为 10000 元的员工共读取到了 11 条记录,如下图:
解决办法就是如果在操作事务完成数据处理之前,任何其他事务都不可以添加新数据,则可避免该问题.
。
为了解决以上各种数据库访问的并发性问题(更新丢失、脏读、不可重复读、幻象读),为此数据库提供了4种隔离级别.
如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。这样就避免了更新丢失,却可能出现脏读。也就是说事务B读取到了事务A未提交的数据.
读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。该隔离级别避免了脏读,但是却可能出现不可重复读。事务A事先读取了数据,事务B紧接了更新了数据,并提交了事务,而事务A再次读取该数据时,数据已经发生了改变.
可重复读是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,即使第二个事务对数据进行修改,第一个事务两次读到的的数据是一样的。这样就发生了在一个事务内两次读到的数据是一样的,因此称为是可重复读。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。这样避免了不可重复读取和脏读,但是有时可能出现幻象读。(读取数据的事务)这可以通过“共享读锁”和“排他写锁”实现.
提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。如果仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。序列化是最高的事务隔离级别,同时代价也花费最高,性能很低,一般很少使用,在该级别下,事务顺序执行,不仅可以避免脏读、不可重复读,还避免了幻像读.
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大.
对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为 Read Committed(授权读取、读提交)。它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、幻读和丢失更新这些并发性问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制.
大多数数据库的默认级别就是 Read committed(授权读取、读提交),比如Sql Server , Oracle。MySQL的默认隔离级别就是 Repeatable read.
虽然数据库的隔离级别可以解决大多数问题,但是灵活度较差,为此又提出了悲观锁和乐观锁的概念.
悲观锁就是某事务在更新数据过程中将数据锁定,其他任何事务都不能读取或修改,必须修改完成后才能访问数据(类似于Java的线程同步机制)。悲观锁的特点是具有排他性,通常依赖于数据库的锁机制,一般适合短事务处理.
可能你会想,说了半天也没说为何叫悲观锁呀,到底悲观在哪里呀?这个问题问得很好。根据悲观锁的定义可知,当一个事务加了悲观锁,其他任何事务是不能读取或修改数据,也就是只能在外面等待,什么事也干不了,直到悲观锁被释放为止。那么,想象一下,如果有很多事务都要访问数据库(高并发的情况),加了悲观锁就意味所有事务需要排着长长的队,一个一个访问数据库,那么访问数据库的效率是不是非常低呀,你说悲观不悲观呀.
乐观锁相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以只会在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则返回用户错误的信息,让用户决定如何去做。乐观锁的特点是并发性较好,事务修改数据时,其他事务仍可以修改数据.
实现乐观锁一般来说有以下2种方式:
使用数据版本(Version)记录机制实现,这是乐观锁最常用的一种实现方式。何谓数据版本?即为数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据.
乐观锁定的第二种实现方式和第一种差不多,同样是在需要乐观锁控制的table中增加一个字段,名称无所谓,字段类型使用时间戳(timestamp), 和上面的version类似,也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是版本冲突.
说白了,乐观锁其实根本不是一种数据库锁机制,而是一种冲突检测机制,这种冲突检测机制是依赖软件或应用程序实现的.
那乐观锁为何乐观呀,乐观在它的并发性比悲观锁好,一个事务在修改数据时,其他事务仍然可以修改数据.
悲观锁的优点是可以保障数据库的数据是绝对安全的,它是依赖数据库的锁机制,能很好的解决数据库访问的并发性问题,但是缺点就是会导致数据库访问性能低下,所以适合短事务(也就是事务执行时间很短)的情况。你想一想,如果事务执行时间很长,那么后面的事务就得一直排队等待嘛。它的使用场景是对数据安全性要求非常高的场景,比如银行系统、金融系统等.
乐观锁的优点是可以保障并发性比较好,也就景数据库访问性能可以,它是依赖软件的冲突检测机制实现的,但是缺点就是并没彻底解决数据库访问的并发性问题,所以数据库的数据不是绝对安全的。它的使用场景是对数据安全性要求不高而对性能要求很高的场景,比如各种信息管理系统等.
。
1.数据库访问的并发性问题(更新丢失、脏读、不可重复读、幻象读)会导致的数据的一致性被破坏.
2.数据库指定了4种事务的隔离级别,目的是为了解决数据库访问的并发性问题(更新丢失、脏读、不可重复读、幻象读)导致的数据的一致性被破坏.
1.Read uncommitted(未授权读取、读未提交) 。
2.Read committed(授权读取、读提交) 。
3.Repeatable read(可重复读取) MySQL 默认隔离级别 。
4.Serializable(序列化) 。
3.由于数据库的隔离级别灵活度较差,所以又有了悲观锁和乐观锁,也是用于解决数据库访问的并发性问题.
1.悲观锁 。
2.乐观锁 。
本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我的更多内容! 。
原文链接:https://blog.csdn.net/qq_41570658/article/details/120956823 。
最后此篇关于Java Spring事务的隔离级别详解的文章就讲到这里了,如果你想了解更多关于Java Spring事务的隔离级别详解的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
Android 项目中最低(最低 sdk)和最高(目标 sdk)级别是否有任何影响。这些东西是否会影响项目的可靠性和效率。 最佳答案 没有影响,如果您以 SDK 级别 8 为目标,那么您的应用将以 9
我将现有的 android 项目升级到 API 级别 31。我使用 Java 作为语言。我改变了 build.gradle compileSdkVersion 31 defaultConfig {
我正在使用 ionic 3 创建一个 android 应用程序,当我尝试上传到 playstore 时,我收到一个错误,提示我的应用程序以 api 25 为目标,当我检查我的 project.prop
我刚刚尝试将应用程序的目标和编译 API 级别更新为 29 (Android 10),并注意到我无法再编译,因为 LocationManager.addNmeaListener 只接受 OnNmeaM
我的代码没有在 Kitkat 上显示工具栏。 这是我的两个 Android 版本的屏幕截图。 Kitkat 版本: Lollipop 版: 这背后的原因可能是什么。 list 文件
我正在构建面向 API 级别 8 的 AccessabilityService,但我想使用 API 级别 18 中引入的功能 (getViewIdResourceName())。这应该可以通过使用 A
当我想在我的电脑上创建一个 android 虚拟机时,有两个选项可以选择目标设备。它们都用于相同的 API 级别。那么我应该选择哪一个呢?它们之间有什么区别? 最佳答案 一个是基本的 Android,
当我选择 tagret 作为 Android 4.2.2(API 级别 17)时,模拟器需要很长时间来加载和启动。 所以我研究它并通过使用 找到了解决方案Intel Atom(x86) 而不是 ARM
我有一个使用 Android Studio 创建的 Android 项目。我在项目中添加了一些第三方依赖项,但是当我尝试在 Android Studio 中编译时,我遇到了以下错误: Error:Ex
如上所述,如何使用 API 8 获取移动设备网络接口(interface)地址? 最佳答案 NetworkInterface.getInetAddresses() 在 API8 中可用。 关于andr
我想显示 Snackbar并使用图像而不是文本进行操作。 我使用以下代码: val imageSpan = ImageSpan(this, R.drawable.star) val b
我有一个用 python 编写的简单命令行程序。程序使用按以下方式配置的日志记录模块将日志记录到屏幕: logging.basicConfig(level=logging.INFO, format='
使用下面的代码,实现游戏状态以控制关卡的最简单和最简单的方法是什么?如果我想从标题画面开始,然后加载一个关卡,并在完成后进入下一个关卡?如果有人能解释处理这个问题的最简单方法,那就太好了! impor
我想创建一个可以找到嵌套树结构深度的属性。下面的静态通过递归找出深度/级别。但是是否可以将此函数作为同一个类中的属性而不是静态方法? public static int GetDepth(MenuGr
var myArray = [{ title: "Title 1", children: [{ title: "Title 1.1", children: [{
通过下面的代码,实现游戏状态来控制关卡的最简单、最容易的方法是什么?如果我想从标题屏幕开始,然后加载一个关卡,并在完成后进入下一个关卡?如果有人可以解释处理这个问题的最简单方法,那就太好了! impo
我有一个树结构,其中每个节点基本上可以有无限个子节点,它正在为博客的评论建模。 根据特定评论的 ID,我试图找出该评论在树中的深度/级别。 我正在关注 this guide that explains
考虑任何给定的唯一整数的数组,例如[1,3,2,4,6,5] 如何确定“排序度”的级别,范围从 0.0 到 1.0 ? 最佳答案 一种方法是评估必须移动以使其排序的项目数量,然后将其除以项目总数。 作
我如何定义一个模板类,它提供一个整数常量,表示作为输入模板参数提供的(指针)类型的“深度”?例如,如果类名为 Depth,则以下内容为真: Depth::value == 3 Depth::value
我的场景是:文件接收器应该包含所有内容。另一个接收器应包含信息消息,但需要注意的是 Microsoft.* 消息很烦人,因此这些消息应仅限于警告。两个sink怎么单独配置?我尝试的第一件事是: str
我是一名优秀的程序员,十分优秀!