数据库事务的四种隔离级别

这篇具有很好参考价值的文章主要介绍了数据库事务的四种隔离级别。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

数据库事务的四种隔离级别

引言

事务

数据库事务(简称:事务)是数据库管理系统执行过程中的一个逻辑单元,由一个有限的数据库操作序列构成。——维基百科
简而言之:一系列数据库操作语句组成事务。
数据库事务的隔离级别有四种:

  • 读未提交(Read Uncommitted):事务中的修改可以被其他事务读取,即一个事务可以读取到另一个未提交事务修改的数据。

简而言之:一个事务可以到其他事务修改了但未提交的数据。

  • 读已提交(Read Committed):事务只能读取已经提交的数据,不能读取未提交的数据。在该隔离级别下,事务只能读取到已经提交的数据,因此会避免脏读的情况。(脏读的概念可以参考本栏其他博客)

简而言之:数据的读取只能读取已经提交过的数据,和读未提交相比,读未提交可以读取修改了单位提交的数据。而读已提交则不行,因此避免了脏读的情况。

  • 可重复读(Repeatable Read):在一个事务中多次读取同一个数据时,能够保证读取到的数据一致,即使其他事务修改了该数据。在隔离级别下,事务在读取数据时会锁定该数据,其他事务不能修改该数据,因此可以避免脏读和不可重复读的情况。

本人理解:应该用锁将写操作锁定,可以重复读取且数据保持一致。

  • 串行化(Serializable,序列化):最高的隔离级别,它保证所有事务之间的执行顺序按照某个顺序执行,避免了所有并发问题。在该隔离级别下,事务之间互相等待,直到前一个事务执行完成后才能执行下一个事务,因此可以避免脏读、不可重复读和幻读的情况。

将事务串行化,一次只能按照特定顺序执行一个事务,因为只执行一个事务,会避免很多问题,但是肯定会降低执行效率。


读未提交(Read Uncommitted)

读未提交的隔离级别意味着一个事务可以读取另一个未提交事务的修改,因此在读未提交的情况下,可能会出现脏读的问题。

读未提交相关的简单Java代码可以参考如下,已经做了很详细的注释。

// 假设有一个账户表 Account,有两个字段:id 和 balance

// 在一个事务中,将 id 为 1 的账户余额增加 100
Connection conn1 = DriverManager.getConnection(url, username, password);
//关掉自动提交,因为需要在读未提交的状态下,另一个事务读取未提交事务的修改。
conn1.setAutoCommit(false);
PreparedStatement pstmt1 = conn1.prepareStatement("UPDATE Account SET balance = balance + 100 WHERE id = 1");
pstmt1.executeUpdate();

// 在另一个事务中,读取 id 为 1 的账户余额,此时可以读到未提交事务的修改
Connection conn2 = DriverManager.getConnection(url, username, password);
conn2.setAutoCommit(false);
PreparedStatement pstmt2 = conn2.prepareStatement("SELECT balance FROM Account WHERE id = 1");
ResultSet rs = pstmt2.executeQuery();
if (rs.next()) {
    System.out.println("balance: " + rs.getInt("balance"));
}

在上述代码中,第一个事务对账户表的某个账户余额进行了修改,并提交了事务。在第二个事务中,查询了同一个账户的余额,此时可以读取到第一个事务修改后的结果。

然而如果再第一个事务中调用’conn1.rollback()‘方法回滚事务,那么在第二个事务中读取到的余额将是未提交事务前的余额。此说明在读未提交的隔离级别下,读取的数据可能是不确定的,因此需要谨慎使用该隔离级别。


读已提交(Read Committed)

在读已提交隔离级别下,一个事务只能读取已经提交的数据,因此不会出现脏读的问题,

以下是一个简单的Java代码示例,演示了读未提交隔离级别:

// 假设有一个账户表 Account,有两个字段:id 和 balance

// 在一个事务中,将 id 为 1 的账户余额增加 100
Connection conn1 = DriverManager.getConnection(url, username, password);
//关闭自动提交
conn1.setAutoCommit(false);
PreparedStatement pstmt1 = conn1.prepareStatement("UPDATE Account SET balance = balance + 100 WHERE id = 1");
pstmt1.executeUpdate();
conn1.commit();

// 在另一个事务中,读取 id 为 1 的账户余额,此时只能读取到已提交的修改
Connection conn2 = DriverManager.getConnection(url, username, password);
conn2.setAutoCommit(false);
conn2.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); // 设置隔离级别为读提交
PreparedStatement pstmt2 = conn2.prepareStatement("SELECT balance FROM Account WHERE id = 1");
ResultSet rs = pstmt2.executeQuery();
if (rs.next()) {
    System.out.println("balance: " + rs.getInt("balance"));
}

在上述代码中,第一个事务对账户表中的某个账户余额进行了修改,并提交了事务。在第二个事务中,查询了同一个账户的余额,由于设置了读提交隔离级别,因此只能读取到已经提交的修改结果。因此,无论在第一个事务是否提交或者回滚,第二个事务都只会读取到已提交的数据。

需要注意的是,虽然读提交隔离级别避免了脏读问题,但是仍然可能出现不可重复读和幻读问题。如果需要避免这些问题,可以选择更高的隔离级别,如可重复读或串行化。

第一个事务需要提交,不然第二个事务读取不到数据。


可重复读(Repeatable Read)

在可重复读隔离级别下,一个事务内多次读取同一数据,将得到一致的结果,即事务读取的数据与其他事务修改的数据是分离的。一下是一个简单的Java代码示例,演示了可重复读隔离级别:

// 假设有一个账户表 Account,有两个字段:id 和 balance

// 在一个事务中,读取 id 为 1 的账户余额,记录当前余额
Connection conn1 = DriverManager.getConnection(url, username, password);
//关闭自动提交
conn1.setAutoCommit(false);
conn1.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ); // 设置隔离级别为可重复读
PreparedStatement pstmt1 = conn1.prepareStatement("SELECT balance FROM Account WHERE id = 1");
ResultSet rs1 = pstmt1.executeQuery();
int balance1 = 0;
if (rs1.next()) {
    balance1 = rs1.getInt("balance");
}
//事务一没有提交,conn1.commit()

// 在另一个事务中,将 id 为 1 的账户余额增加 100
Connection conn2 = DriverManager.getConnection(url, username, password);
conn2.setAutoCommit(false);
PreparedStatement pstmt2 = conn2.prepareStatement("UPDATE Account SET balance = balance + 100 WHERE id = 1");
pstmt2.executeUpdate();
conn2.commit(); //事务二提交了

// 在第一个事务中,再次读取 id 为 1 的账户余额,与之前记录的余额进行比较
PreparedStatement pstmt3 = conn1.prepareStatement("SELECT balance FROM Account WHERE id = 1");
ResultSet rs2 = pstmt3.executeQuery();
int balance2 = 0;
if (rs2.next()) {
    balance2 = rs2.getInt("balance");
}
if (balance1 == balance2) {
    System.out.println("Balance is consistent");
} else {
    System.out.println("Balance is inconsistent");
}

在上述代码中,第一个事务中读取了一个账户的余额,记录了当前余额,并提交了事务。在第二个事务中对同一个账户的余额进行了修改,并提交了事务。在第一个事务中再次读取同一个账户的余额,与之前记录的余额进行比较,如果两次读取的结果相同,则打印“Balance is consistent”,否则打印”Balance is inconsistent”。

由于设置了可重复读隔离级别,第一个事务的两次读取操作将得到一致的结果。因此,无论在第二个事务是否提交或回滚,第一个事务都只会读取到第一次读取的余额,而不会读取到其他事务的修改结果。


串行化(Serializable)

在串行化隔离级别下,所有并发事务都会被强制串行执行,这意味着任何时候只有一个事务能够访问数据库。

以下是一个简单的Java代码示例,演示了串行化隔离级别:

// 假设有一个账户表 Account,有两个字段:id 和 balance

// 在一个事务中,读取 id 为 1 的账户余额,记录当前余额
Connection conn1 = DriverManager.getConnection(url, username, password);
conn1.setAutoCommit(false);
conn1.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); // 设置隔离级别为串行化
PreparedStatement pstmt1 = conn1.prepareStatement("SELECT balance FROM Account WHERE id = 1 FOR UPDATE");
ResultSet rs1 = pstmt1.executeQuery();
int balance1 = 0;
if (rs1.next()) {
    balance1 = rs1.getInt("balance");
}

// 在另一个事务中,试图对同一个账户的余额进行修改,但是因为第一个事务已经获得了排他锁,所以会阻塞等待
Connection conn2 = DriverManager.getConnection(url, username, password);
conn2.setAutoCommit(false);
conn2.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); // 设置隔离级别为串行化
PreparedStatement pstmt2 = conn2.prepareStatement("UPDATE Account SET balance = balance + 100 WHERE id = 1");
pstmt2.executeUpdate();

// 在第一个事务中,再次读取 id 为 1 的账户余额,与之前记录的余额进行比较
PreparedStatement pstmt3 = conn1.prepareStatement("SELECT balance FROM Account WHERE id = 1");
ResultSet rs2 = pstmt3.executeQuery();
int balance2 = 0;
if (rs2.next()) {
    balance2 = rs2.getInt("balance");
}
if (balance1 == balance2) {
    System.out.println("Balance is consistent");
} else {
    System.out.println("Balance is inconsistent");
}

// 提交第一个事务
conn1.commit();

在上述代码中,第一个事务读取了一个账户余额,记录了当前余额,并获得了一个排它锁。在第二个事务中试图对同一个账户的余额进行修改,但因为第一个事务已经获得了排它锁,所以会阻塞等待。在第一个事务中再次读取同一个账户的余额,与之前记录的余额进行比较,如果两次读取的结果相同,则打印"Balance is consistent",否则打印 “Balance is inconsistent”。

由于设置了串行化隔离级别,第二个事务会阻塞等待第一个事务执行完毕后才能执行,因此无论在第二个事务执行前还是执行后,第一个事务所读取的数据都不会受到第二个事务的影响。当第一个事务执行完毕并提交后,第二个事务才会继续执行。

四种隔离级别排序

数据库四种隔离级别按照数据一致性和性能的权衡不同,可以按照以下顺序排序:

  1. 串行化(Serializable):最严格的隔离级别,保证了数据的最高一致性,但是牺牲了并发性能。
  2. 可重复读(Repeatable Read):保证了事务之间不会相互干扰,但是可能会出现幻读问题,读取到未提交的新插入的行。
  3. 读已提交(Read Committed):保证了读取到的数据都是已经提交的,避免了脏读问题,但是可能会出现不可重复读和幻读问题。
  4. 读未提交(Read Uncommitted):最宽松的隔离级别,允许事务读取到其他未提交的数据,可能会出现脏读、不可重复读和幻读等问题。

需要注意的是,隔离级别的选择应该是根据具体的应用场景和对数据一致性和性能的需求进行权衡,在高并发场景下,需要更高的并发性能,但是也要保证数据的一致性,因此需要根据具体情况选择适当的隔离级别。文章来源地址https://www.toymoban.com/news/detail-759456.html

到了这里,关于数据库事务的四种隔离级别的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 数据库事务隔离级别

    读未提交、读已提交、可重复读、串行化 对事务中所有读写的数据加上读锁、写锁、范围锁。所以冲突的事务必须同步执行。 核心是只对事务中所有读写的数据加上读锁、写锁,不加范围锁。 相比于读已提交,由于对整个事务都加上了读锁,避免其他事务可以进行更新,进

    2024年02月08日
    浏览(45)
  • 数据库之事务隔离级别详解

    事务是一个原子操作,要么全部执行成功,要么全部执行失败。 事务的原子性确保一组逻辑操作,要么全部完成,要么完全不起作用。 执行事务前后,数据保持一致,例如转账业务中,无论事务是否成功,转账者和收款人的总额应该是不变的。 事务的隔离性是指在并发执行

    2024年02月07日
    浏览(73)
  • 数据库事务的四大特性与事务的隔离级别

    概要: 事务的四个特性:原子性、一致性、隔离性、持久性 事务不隔离带来的问题:更新丢失、脏读、不可重复读、虚读(幻读)。其中更新丢失就是并发写,这是一定不允许的,因此一定要解决更新丢失问题。 事务隔离的级别:读未提交(1000)、读已提交(1100)、可重

    2023年04月09日
    浏览(52)
  • 【数据库】事务的隔离级别以及实现原理

    经常提到数据库的事务,那你知道数据库还有事务隔离的说法吗,事务隔离还有隔离级别,那什么是事务隔离,隔离级别又是什么呢?本文就帮大家梳理一下。 事务,由一个有限的数据库操作序列构成,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。

    2023年04月26日
    浏览(42)
  • 【MySQL】根据MVCC和Read View分析事务的四种隔离级别在读写场景分别是如何体现其隔离性的

    需要云服务器等云产品来学习Linux的同学可以移步/--腾讯云--/--阿里云--/--华为云--/官网,轻量型云服务器低至112元/年,新用户首次下单享超低折扣。   目录 一、数据库并发的三种场景 二、读写场景的MVCC  1、3个(4个)记录隐藏列字段 2、undo log(撤销日志) 3、模拟MVCC场景

    2024年02月12日
    浏览(44)
  • 【Spring/MySQL数据库系列】数据库事务的特点与隔离级别

    ⭐️ 前面的话 ⭐️ 本文已经收录到《Spring框架全家桶系列》专栏,本文将介绍有关数据库事务的特点以及隔离级别。 📒博客主页:未见花闻的博客主页 🎉欢迎关注🔎点赞👍收藏⭐️留言📝 📌本文由 未见花闻 原创, CSDN 首发! 📆首发时间:🌴2023年5月20日🌴 ✉️坚

    2024年02月05日
    浏览(55)
  • 【后端面经-数据库】MySQL的事务隔离级别简介

    目录 0. 事务的概念 1. 三类问题 2. 事务隔离级别 3. 操作指令 4. 总结 5. 参考博文 事务指的是一连串的集中操作指令,一个事务的执行必须执行完所有的动作才能算作执行结束。事务具有四个特点,简记作 ACID : A -Atomicity: 原子性,事务的执行必须保证所有的动作都执行完毕;

    2024年02月08日
    浏览(49)
  • 聊一聊数据库事务的那些事(隔离级别,传播行为)

      我们平时使用事务的时候,可能脑子里面想到和事务有关的知识点无非就是,ACID,事务隔离级别那一套,使用的事务也就是是通过注解的形式,或者手动开启事务。更细致一点的问题或许没有深究下去,比如事务的传播行为,注解形式和手动事务的区别等,今天我们就这几

    2024年02月07日
    浏览(57)
  • 【Mysql数据库 第13章】MySQL的事务、事务的隔离级别、事务的保存点

    💖Spring中的创建对象的三种方式、第三方资源配置管理详细描述及使用(XML版完结篇) 💖Spring中的bean的配置、作用范围、生命周期详细描述及使用(XML版上篇) 💖

    2023年04月20日
    浏览(56)
  • elasticsearch的查询方式和mysql数据库事务隔离级别的思考

    目录 普通分页 解除查询限制 scroll查询 search_after 官方改进 轻量级试图(pit,Point in time) 总结 项目中用到了 elasticsearch,发现有几种查询方式不太一样,思考了一下,总结如下 等同于关系数据库的分页查询,例如 mysql 的 limit,如下 sql 这种查询方式有一个问题,需要查询

    2024年01月18日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包