4 characteristics of transactions, transaction isolation level, transaction propagation behavior

1. Four characteristics of transactions

  • Atomicity: In a transaction, like adding, deleting and modifying (DML), either all succeed or all fail.

  • Consistency: When the transaction is completed, all data must be kept in a consistent state. For example, in a bank transfer, A transfers 1000 to B, A deducts 1000, and B adds 1000 to succeed or fail together.

  • Isolation: The execution of multiple transactions does not interfere with each other.

  • Persistence: Once a transaction is committed or rolled back, its changes to the data in the database are permanent.

2. What are the problems caused by concurrent transactions?

In a typical application, multiple transactions run concurrently, often operating on the same data to complete their respective tasks (multiple users operate on the same data). Although concurrency is necessary, it may cause the following problems.

Update loss : Two transactions are updated at the same time, and the rollback of the second transaction will overwrite the data updated by the first transaction, resulting in loss of updates. Or transaction 1 performs an update operation, and transaction 2 is also updated before transaction 1 ends, then the update result of transaction 1 is overwritten by transaction 2.

Dirty read : When a transaction is accessing data and modifying the data, and this modification has not been submitted to the database, another transaction also accesses the data, and then uses this data. Because this data is uncommitted data, the data read by another transaction is "dirty data", and the operation based on "dirty data" may be incorrect.

Unrepeatable read (Unrepeatable read) : Refers to reading the same data multiple times within a transaction. While this transaction is not over, another transaction also accesses the data. Then, between the two reads in the first transaction, the data read by the first transaction may be different due to the modification of the second transaction. This happens that the data read twice in a transaction is different, so it is called non-repeatable read.

Phantom read : Phantom read is similar to non-repeatable read. It happens when one transaction (T1) reads a few rows of data, and then another concurrent transaction (T2) inserts some data. In subsequent queries, the first transaction (T1) will find some more records that do not exist, as if hallucinations have occurred, so it is called phantom reading.

The difference between non-repeatable read and phantom read:

The focus of non-repeatable reading is modification, such as reading a record multiple times and finding that some column values ​​have been modified. The focus of phantom reading is adding or deleting. For example, reading a record multiple times and finding that the number of records has increased or decreased .

3. The level of transaction isolation

To this end, we need to provide different types of "lock" mechanisms for different degrees of concurrent access control for database transactions, resulting in different transaction isolation levels: isolation level (low -> high). The SQL and SQL2 standards define four isolation levels:

●Read Uncommitted

Meaning explanation: Only restrict the same data write transaction and prohibit other write transactions. Solve "update lost". (When a transaction is written, other transactions are prohibited from writing)

Name Explanation: Uncommitted data can be read

Required lock: exclusive write lock

●Read Committed

Explanation of meaning: only restrict the same data write transaction and prohibit other read and write transactions. Solve "dirty read" and "lost update". (When a transaction writes, other transactions are prohibited from reading and writing)

Name explanation: the data must be submitted later to be read

Required locks: exclusive write lock, momentary shared read lock

●Repeatable Read (Repeatable Read)

Explanation of meaning: Restricting the same data write transaction prohibits other read and write transactions, and read transactions prohibit other write transactions (reading is allowed). Solve "non-repeatable read", "lost update" and "dirty read". (When one transaction is writing, other transactions are prohibited from reading and writing, and when one transaction is reading, other transactions are prohibited from writing)

Note that phantom reading is not solved. The solution to phantom reading is to increase the range lock (range lock) or table lock.

Name Explanation: Can be read repeatedly

Required locks: exclusive write lock, shared read lock

●Serializable

Explanation of meaning: All read and write transactions must be serialized. It requires serialized execution of transactions, and transactions can only be executed one after another, but not concurrently. If transaction serialization cannot be achieved only through "row-level locks", other mechanisms must be used to ensure that the newly inserted data will not be accessed by the transaction that just executed the query operation. (When a transaction is written, other transactions are prohibited from reading and writing, and when a transaction is read, other transactions are prohibited from reading and writing)

Required locks: range locks or table locks

The following table shows the control capabilities of each isolation level for various exceptions.

update lost

dirty read

non-repeatable read

Phantom reading

RU (read uncommitted)

avoid

RC (read committed)

avoid

avoid

RR (repeatable read)

avoid

avoid

avoid

S (serialization)

avoid

avoid

avoid

avoid

The highest of the above four isolation levels is the Serializable level, and the lowest is the Read uncommitted level. Of course, the higher the level, the better the data integrity, but the lower the execution efficiency. A level like Serializable is to lock the table (similar to the lock in Java multithreading) so that other threads can only wait outside the lock, so the isolation level usually selected should be based on the actual situation.

4. Transaction propagation behavior

Transaction propagation behavior refers to how a transaction method works when it is called by another transaction method.

For example: There are two transaction methods, one is method A, the other is method B, and method B is called in method A, then whether method B starts a transaction or runs in the transaction in method A is determined by method B Transaction propagation behavior controlled.

Seven transaction propagation behaviors are defined in Spring:

communication behavior

meaning

PROPAGATION_REQUIRED

Indicates that the current method must run in a transaction, if the current transaction exists, the method will run in the transaction, otherwise, a new transaction will be started

PROPAGATION_SUPPORTS

Indicates that the method does not require a transaction context, but if there is a current transaction, the method will run in this transaction summary

PROPAGATION_MANDATORY

Indicates that the method must be run in a transaction, if the current transaction does not exist, an exception will be thrown

PROPAGATION_REQUIRED_NEW

Indicates that the current method must run in its own transaction. A new transaction will be started. If there is a current transaction, the current transaction will be suspended during the execution of this method. If you use JTATransactionManager, you need to access TransactionManager

PROPAGATION_NOT_SUPPORTED

Indicates that the current method should not run in a transaction. If there is a current transaction, the current transaction will be suspended during the operation of the method. If you use JTATransactionManager, you need to access TransactionManager

PROPAGATION_NEVER

Indicates that the current method should not run in the transaction context, if a transaction is currently running, an exception will be thrown

PROPAGATION_NESTED

Indicates that if a transaction already exists, the method will be run in a nested transaction. Nested transactions can be committed or rolled back independently of the current transaction. If the current transaction does not exist, it behaves like PROPAGATION_REQUIRED. Note that each vendor's support for this propagation behavior is different. You can refer to the documentation of the resource manager to confirm that they are capable of supporting nested transactions.

1、PROPAGATION_REQUIRED

If there is a transaction, the transaction is supported, and if there is no transaction, a transaction is started

Demo code:

@Component
public class Transaction {
 
    @Autowired
    JdbcTemplate jdbcTemplate;
 
    @Transactional(propagation = Propagation.REQUIRED)
    public void methodA() {
        jdbcTemplate.update("insert into users value (12,'wangwu','000000')");
        methodB();
    }
 
    @Transactional(propagation = Propagation.REQUIRED)
    public void methodB() {
        jdbcTemplate.update("insert into actor value (20,'范闲',now())");
        // 此处抛出异常,方法A和方法B的操作都将回滚
        int i = 100 / 0;
    }
}

Call method B separately, because there is no transaction in the context, so a transaction will be opened

When method A is called, a transaction will be opened because there is no transaction in the context. When method B is executed, method B finds that there is a transaction, and method B will not start a new transaction, but a transaction in method A in the implementation.

2、PROPAGATION_SUPPORTS

The current transaction is supported if there is a transaction, and no transaction is run if there is no transaction

Demo code:


@Component
public class Transaction {
 
    @Autowired
    JdbcTemplate jdbcTemplate;
 
    @Transactional(propagation = Propagation.REQUIRED)
    public void methodA() {
        jdbcTemplate.update("insert into users value (12,'wangwu','000000')");
        methodB();
    }
 
    @Transactional(propagation = Propagation.SUPPORTS)
    public void methodB() {
        jdbcTemplate.update("insert into actor value (20,'范闲',now())");
        // 存在事务回滚操作,不存在事务则不回滚
        int i = 100 / 0;
    }

Call method B alone, running without transaction

Call method A, method B joins the transaction of method A, and the operation of the transaction

3、PROPAGATION_MANDATORY

If there is a transaction, the operation of the transaction, if there is no transaction, an exception is thrown

@Component
public class Transaction {
 
    @Autowired
    JdbcTemplate jdbcTemplate;
 
    @Transactional(propagation = Propagation.REQUIRED)
    public void methodA() {
        jdbcTemplate.update("insert into users value (12,'wangwu','000000')");
        methodB();
    }
 
    @Transactional(propagation = Propagation.MANDATORY)
    public void methodB() {
        jdbcTemplate.update("insert into actor value (20,'范闲',now())");
    }
}

Call method B directly, since no transaction will throw an exception

No existing transaction found for transaction marked with propagation 'mandatory'

Call method A, method B will be added to the transaction of method A

4、PROPAGATION_REQUIRED_NEW

To use PROPAGATION_REQUIRES_NEW, you need to use JtaTransactionManager as the transaction manager.

A transaction will be opened, and if there is a transaction, the existing transaction will be suspended

@Component
public class Transaction {
 
    @Autowired
    JdbcTemplate jdbcTemplate;
 
    @Transactional(propagation = Propagation.REQUIRED)
    public void methodA() {
        doSomeTingA();
        methodB();
        doSomeTingB();
    }
 
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void methodB() {
        jdbcTemplate.update("insert into actor value (20,'范闲',now())");
    }
 
    public void doSomeTingA() {
        jdbcTemplate.update("insert into users value (12,'wangwu','000000')");
 
    }
 
    public void doSomeTingB() {
        jdbcTemplate.update("insert into actor value (21,'范思辙',now())");
    }
}

when calling

main(){
    methodA();
}

is equivalent to calling

main(){
    TransactionManager tm = null;
    try{
        //获得一个JTA事务管理器
        tm = getTransactionManager();
        tm.begin();//开启一个新的事务
        Transaction ts1 = tm.getTransaction();
        doSomeThing();
        tm.suspend();//挂起当前事务
        try{
            tm.begin();//重新开启第二个事务
            Transaction ts2 = tm.getTransaction();
            methodB();
            ts2.commit();//提交第二个事务
        } Catch(RunTimeException ex) {
            ts2.rollback();//回滚第二个事务
        } finally {
            //释放资源
        }
        //methodB执行完后,恢复第一个事务
        tm.resume(ts1);
        doSomeThingB();
        ts1.commit();//提交第一个事务
    } catch(RunTimeException ex) {
        ts1.rollback();//回滚第一个事务
    } finally {
        //释放资源
    }
}

Here, ts1 is called the outer transaction, and ts2 is called the inner transaction. As can be seen from the above code, ts2 and ts1 are two independent transactions, which are not related to each other. The success of ts2 does not depend on ts1. If methodA fails in doSomeThingB after calling methodB, the results of methodB are still committed. The results caused by codes other than methodB are rolled back

5、PROPAGATION_NOT_SUPPORTED

PROPAGATION_NOT_SUPPORTED总是非事务的运行,并且挂起任何存在的事务,使用PROPAGATION_NOT_SUPPORTED,也需要使用JtaTransactionManager作为事务管理器。

6、PROPAGATION_NEVER

总是非事务的运行,如果存在一个活动的事务,则抛出异常

7.PROPAGATION_NESTED

如果一个活动的事务存在,则运行在一个嵌套事务中,如果没有活动事务,则按照PROPAGATION_REQUIRED属性执行

这是一个嵌套事务,使用JDBC 3.0驱动时,仅仅支持DataSourceTransactionManager作为事务管理器。

需要JDBC 驱动的java.sql.Savepoint类。使用PROPAGATION_NESTED,还需要把PlatformTransactionManager的nestedTransactionAllowed属性设为true(属性值默认为false)。

@Component
public class Transaction {
 
    @Autowired
    JdbcTemplate jdbcTemplate;
 
    @Transactional(propagation = Propagation.REQUIRED)
    public void methodA() {
        doSomeTingA();
        methodB();
        doSomeTingB();
    }
 
    @Transactional(propagation = Propagation.NESTED)
    public void methodB() {
        jdbcTemplate.update("insert into actor value (20,'范闲',now())");
    }
 
    public void doSomeTingA() {
        jdbcTemplate.update("insert into users value (12,'wangwu','000000')");
 
    }
 
    public void doSomeTingB() {
        jdbcTemplate.update("insert into actor value (21,'范思辙',now())");
    }
}

单独执行方法B,按照PROPAGATION_REQUIRED属性执行

如果调用方法A,相当于如下效果:

main(){
    Connection con = null;
    Savepoint savepoint = null;
    try{
        con = getConnection();
        con.setAutoCommit(false);
        doSomeThingA();
        savepoint = con2.setSavepoint();
        try{
            methodB();
        } catch(RuntimeException ex) {
            con.rollback(savepoint);
        } finally {
            //释放资源
        }
        doSomeThingB();
        con.commit();
    } catch(RuntimeException ex) {
        con.rollback();
    } finally {
        //释放资源
    }
}

当methodB方法调用之前,调用setSavepoint方法,保存当前的状态到savepoint。如果methodB方法调用失败,则恢复到之前保存的状态。但是需要注意的是,这时的事务并没有进行提交,如果后续的代码(doSomeThingB()方法)调用失败,则回滚包括methodB方法的所有操作。嵌套事务一个非常重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚。

PROPAGATION_NESTED 与PROPAGATION_REQUIRES_NEW的区别:

它们非常类似,都像一个嵌套事务,如果不存在一个活动的事务,都会开启一个新的事务。

使用 PROPAGATION_REQUIRES_NEW时,内层事务与外层事务就像两个独立的事务一样,一旦内层事务进行了提交后,外层事务不能对其进行回滚。两个事务互不影响。两个事务不是一个真正的嵌套事务。同时它需要JTA事务管理器的支持。

使用PROPAGATION_NESTED时,外层事务的回滚可以引起内层事务的回滚。而内层事务的异常并不会导致外层事务的回滚,它是一个真正的嵌套事务。DataSourceTransactionManager使用savepoint支持PROPAGATION_NESTED时,需要JDBC 3.0以上驱动及1.4以上的JDK版本支持。其它的JTATrasactionManager实现可能有不同的支持方式。

PROPAGATION_REQUIRES_NEW 启动一个新的, 不依赖于环境的 “内部” 事务. 这个事务将被完全 commited 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行。

另一方面, PROPAGATION_NESTED 开始一个 “嵌套的” 事务, 它是已经存在事务的一个真正的子事务. 潜套事务开始执行时, 它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint. 潜套事务是外部事务的一部分, 只有外部事务结束后它才会被提交。

由此可见, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大区别在于, PROPAGATION_REQUIRES_NEW 完全是一个新的事务, 而 PROPAGATION_NESTED 则是外部事务的子事务, 如果外部事务 commit, 嵌套事务也会被 commit, 这个规则同样适用于 roll back.

Guess you like

Origin blog.csdn.net/weixin_54401017/article/details/129595304