本文共 3034 字,大约阅读时间需要 10 分钟。
简单的来说,事务是一组业务操作,这些业务操作要么全部执行成功,要么全部执行不成功。
事务具有四个特性
start TRANSACTION;# orBEGIN;
commit
rollback
SET autocommit = 0;
现有一个数据库表,其中的数据如下:
为了更好的呈现出实验效果,我又在终端连接到了mysql数据库 所以说现在一共有两个连接,一个是mysqlworkbench,一个是终端。 我在终端开启事务并向表中插入一条语句,然后在终端查看表中的数据 注意:我没有提交事务 可以看到,在终端中显示插入数据成功。 下面我们看一下mysqlworkbench中的表情况: 可以看到,workbench中并没有新插入的行,这是由于没有提交事务造成的。 我们再在终端提交事务,然后再次查看workbench中的数据。 发现插入成功,这说明一旦开启事务,需要手动commit,不然sql语句是不会生效的。 我们也可以设置关闭自动提交事务来避免每次都要手动开启事务,这样我们每次写完sql语句都需要手动commit才能使sql语句生效。 这里我设置关闭自动提交事务,然后执行一条更新语句,之后查询数据发现更新成功。 下面我们看下workbench中的情况。 发现并没有更新成功,之后我们commit。 发现更新成功。所以说我们需要手动提交才能生效。脏读: 一个事务读到另一个事务未提交的数据
不可重复读: 一个事务读到另一个事务已提交的数据(update)
虚读(幻读): 一个事务读到另一个事务已提交的数据(insert)
read uncommitted(读未提交): 3个问题都没解决
read committed(读已提交):解决脏读
repeatable read(可重复读):解决脏读,不可重复读
serializable(串行化):都解决,单事务处理模式
注意:隔离级别越高,则性能越差在这里说一下查看数据库的隔离级别:
select @@transaction_isolation
设置数据库的隔离级别:
set session transaction isolation level XXX;
首先我们先把两个数据库会话的隔离级别调成read uncommited;
之后我们在终端插入一条数据,然后查询数据,终端插入成功(并未进行commit) 然后我们查看workbench中的数据 发现也能显示插入的数据,然而终端并没有执行commit操作,我们将这种情况称为脏读。 接下来我在终端运行rollback操作。 发现,workbench也发生了变化。 我们将workbench 中的隔离级别改成read commited。 然后重复上面操作。 发现没有出现脏读的情况,说明read commited确实解决了脏读问题。在workbech中隔离级别为 read committed 下,我们做如下实验:
在终端中更新一条数据,然后提交。 然后在workbench中开启事务,查询数据: 没有发现问题。 然后再在终端中重复上述步骤。 然后再次在workbench查询数据 发现workbech中的数据也发生了更新。在一个事务中重复读数据发现出现了不同的结果,说明还是受到了其他事务的影响,这违背了事务的隔离性,我们称这个问题为不可重复读。 解决这个问题我们可以设置workbench中的隔离级别为repeatable read。 设置workbench中的隔离级别为repeatable read之后,我们再次重复上面的实验。 第一次更新 查看workbech数据中的情况 第二次更新 workbench中的情况 我们发现没有发生变化,repeatable read 解决了不可重复读问题。现在workbench中隔离级别为 repeatable read。
我们先在workbench中开启事务查看数据 一共10条数据 然后我们再在终端开启事务,向数据表中添加一条记录并提交。 此时,查看workbench中的数据 还是原来的10条数据 但是我们再workbench中执行更新操作,却发现影响了11条数据。 我们再将workbench中的事务提交,然后再查看数据 发现新插入的最后一条数据的sage也发生了更改,这就是幻读。 我们将workbench和终端中的隔离级别设为serializable 我们将最新一条的数据删掉,这样原表中还是10条数据。 然后重复上面的步骤 我们先在workbench中开启事务,查询数据,然后在终端插入一条数据。 终端在执行insert当中卡住了,之后又报错了。 原来serializable在同一时间仅允许一个事务执行,别的事务会被锁住,当此事务提交之后别的事务才会被执行。 一般在工作中,会将隔离级别设为repeatable read,因为serializable效率低下。ABCD 一个事务Connection conn = nulltry{ // 1. 获得连接 conn = ...; // 2.开启事务 conn.setAutoCommit(false); A B C D //3.提交事务 conn.commit();}catch(){ //4.回滚事务 conn.rollback();}
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 }}
转载地址:http://odaen.baihongyu.com/