欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

mysql 事务管理

程序员文章站 2022-05-09 15:59:45
...

什么是事务

简单的来说,事务是一组业务操作,这些业务操作要么全部执行成功,要么全部执行不成功。

事务的特性

事务具有四个特性

  • 原子性:事务是一个不可分割的基本单位,事务的业务操作要么全部成功,要么全部不成功
  • 一致性:事务执行前后数据的完整性必须一致
  • 隔离性:多个事务,事务的隔离性是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离。
  • 持久性:持久性是指一个事务一旦被提交,它对数据库中数据的改变就是 永久性的,接下来即使数据库发生故障也不应该对其有任何影响。
    参考链接:https://blog.csdn.net/qq_20042935/article/details/88944340

mysql 中常见事务操作

开启事务

start TRANSACTION;
# or
BEGIN;

提交事务

commit

回滚

rollback

关闭自动提交事务

SET autocommit = 0;

提交事务演示事例

现有一个数据库表,其中的数据如下:
mysql 事务管理
为了更好的呈现出实验效果,我又在终端连接到了mysql数据库
所以说现在一共有两个连接,一个是mysqlworkbench,一个是终端。
我在终端开启事务并向表中插入一条语句,然后在终端查看表中的数据
注意:我没有提交事务
mysql 事务管理
可以看到,在终端中显示插入数据成功。
下面我们看一下mysqlworkbench中的表情况:
mysql 事务管理
可以看到,workbench中并没有新插入的行,这是由于没有提交事务造成的。
我们再在终端提交事务,然后再次查看workbench中的数据。
mysql 事务管理
发现插入成功,这说明一旦开启事务,需要手动commit,不然sql语句是不会生效的。
我们也可以设置关闭自动提交事务来避免每次都要手动开启事务,这样我们每次写完sql语句都需要手动commit才能使sql语句生效。
mysql 事务管理
这里我设置关闭自动提交事务,然后执行一条更新语句,之后查询数据发现更新成功。
下面我们看下workbench中的情况。

mysql 事务管理
发现并没有更新成功,之后我们commit。
mysql 事务管理
发现更新成功。所以说我们需要手动提交才能生效。

事务隔离问题

脏读: 一个事务读到另一个事务未提交的数据

不可重复读: 一个事务读到另一个事务已提交的数据(update)

虚读(幻读): 一个事务读到另一个事务已提交的数据(insert)

mysql 事务隔离级别

read uncommitted(读未提交): 3个问题都没解决

read committed(读已提交):解决脏读

repeatable read(可重复读):解决脏读,不可重复读

serializable(串行化):都解决,单事务处理模式
注意:隔离级别越高,则性能越差

解决脏读

在这里说一下查看数据库的隔离级别:

select @@transaction_isolation

设置数据库的隔离级别:

set session transaction isolation level XXX;

首先我们先把两个数据库会话的隔离级别调成read uncommited;
之后我们在终端插入一条数据,然后查询数据,终端插入成功(并未进行commit)
mysql 事务管理
然后我们查看workbench中的数据
mysql 事务管理
发现也能显示插入的数据,然而终端并没有执行commit操作,我们将这种情况称为脏读。
接下来我在终端运行rollback操作。
mysql 事务管理
发现,workbench也发生了变化。
我们将workbench 中的隔离级别改成read commited。
然后重复上面操作。
mysql 事务管理
mysql 事务管理
发现没有出现脏读的情况,说明read commited确实解决了脏读问题。

解决不可重复读

在workbech中隔离级别为 read committed 下,我们做如下实验:
在终端中更新一条数据,然后提交。
mysql 事务管理
然后在workbench中开启事务,查询数据:
mysql 事务管理
没有发现问题。
然后再在终端中重复上述步骤。
mysql 事务管理
然后再次在workbench查询数据
mysql 事务管理
发现workbech中的数据也发生了更新。在一个事务中重复读数据发现出现了不同的结果,说明还是受到了其他事务的影响,这违背了事务的隔离性,我们称这个问题为不可重复读。
解决这个问题我们可以设置workbench中的隔离级别为repeatable read。
设置workbench中的隔离级别为repeatable read之后,我们再次重复上面的实验。
第一次更新
mysql 事务管理
查看workbech数据中的情况
mysql 事务管理
第二次更新
mysql 事务管理
workbench中的情况
mysql 事务管理
我们发现没有发生变化,repeatable read 解决了不可重复读问题。

解决幻读

现在workbench中隔离级别为 repeatable read。
我们先在workbench中开启事务查看数据
mysql 事务管理
一共10条数据
然后我们再在终端开启事务,向数据表中添加一条记录并提交。
mysql 事务管理
此时,查看workbench中的数据
mysql 事务管理
还是原来的10条数据
但是我们再workbench中执行更新操作,却发现影响了11条数据。
mysql 事务管理
我们再将workbench中的事务提交,然后再查看数据
mysql 事务管理
发现新插入的最后一条数据的sage也发生了更改,这就是幻读。
我们将workbench和终端中的隔离级别设为serializable
我们将最新一条的数据删掉,这样原表中还是10条数据。
然后重复上面的步骤
我们先在workbench中开启事务,查询数据,然后在终端插入一条数据。
mysql 事务管理
终端在执行insert当中卡住了,之后又报错了。
原来serializable在同一时间仅允许一个事务执行,别的事务会被锁住,当此事务提交之后别的事务才会被执行。
一般在工作中,会将隔离级别设为repeatable read,因为serializable效率低下。

java 执行mysql事务操作的基本格式

ABCD 一个事务

Connection conn = null

try

{
    // 1. 获得连接
    conn = ...;
    // 2.开启事务
    conn.setAutoCommit(false);
    A
    B
    C
    D
    //3.提交事务
    conn.commit();

}catch(){
    //4.回滚事务
    conn.rollback();
}

java 执行mysql事务操作的基本格式----savepoint

AB(必须)CD(可选)
Connection conn = null;
//保存点,记录当前操作位置,之后可以回滚到指定位置(可以回滚一部分)
Savepoint savepoint = null;
try
{
   	// 1. 获得连接
    conn = ...;
    // 2.开启事务
    conn.setAutoCommit(false);
    A
    B
    savepoint = conn.setSavepoint();
    C
    D
    //3.提交事务
    conn.commit();  
}catch()
{
    if(savepoint!=null) //CD异常
    {
        conn.rollback(savepoint); // 回滚到CD之前
        conn.commit();  // 提交AB
    }
    else
    {
        conn.rollback(); // 回滚AB
    }
}