Transaction learning notes

Posted by MarineX69 on Wed, 09 Mar 2022 13:00:20 +0100

Transaction

What is a transaction?

A smallest non separable work unit;

Usually, a transaction corresponds to a complete business (for example, bank account transfer business, which is the smallest work unit)
A complete business requires batch DML(insert, update, delete) statements to be jointly completed

Transactions are only related to DML statements, or DML statements have transactions. This is related to business logic. Different business logic leads to different numbers of DML statements

Characteristics of things (ACID):

  1. Atomicity: transaction is the smallest unit and cannot be further divided; Either all execution is successful or all failures are rolled back.

  2. Consistency: the transaction must change the database from a consistent state to another consistent state, that is, the state before and after the transaction must be in a consistent state.

    Inconsistency includes three points: dirty reading, non repeatable reading and unreal reading

  3. Isolation: transaction A and transaction B are isolated;

  4. The end of the transaction is the sign of the persistence of the data in the hard disk;

Some terms about transactions

  • Start Transaction: Start Transaction
  • End of transaction: End Transaction
  • Commit Transaction
  • Rollback Transaction: Rollback Transaction

Two important SQL statements (TCL)

  • Commit: commit
  • Rollback: rollback

Opening flag:

  • The execution of any DML statement (insert, update, delete) marks the start of the transaction

End flag (commit or rollback):

  • Submit: after successful completion, synchronize the operation history of all DML statements with the underlying hard disk data
  • Rollback: at the end of failure, clear all DML statement operation history records

Lock mechanism

MySQL can be roughly divided into the following three types of locks:

Table lock: low overhead and fast locking; There will be no deadlock; The locking granularity is large, the probability of lock conflict is the highest, and the concurrency is the lowest.
Row level lock: high overhead and slow locking; Deadlock will occur; The locking granularity is the smallest, the probability of lock conflict is the lowest, and the concurrency is the highest.
Page lock: the cost and locking time are bounded between table lock and row lock; Deadlock will occur; The locking granularity is between table lock and row lock, and the concurrency is general

Table lock:

If the user requests to read or write the same table, the same table will not be blocked;

Writing to the M yISAM table will block other users' read and write requests to the same table;

Read: Yes

Read write: not allowed

Write: not allowed

InnoDB adopts row lock

InnoDB implements the following two types of row locks.

Shared lock (s): allows a transaction to read a row and prevents other transactions from obtaining exclusive locks of the same dataset.
Exclusive lock (x): it allows the transaction that obtains the exclusive lock to update data, and prevents other transactions from obtaining the same data set, shared read lock and exclusive write lock.

InnoDB row locks are implemented through index entries on the index.

The implementation features of InnoDB row lock mean:

InnoDB will use row level locks only when data is retrieved through index conditions. Otherwise, InnoDB will use table locks!

For UPDATE, DELETE and INSERT statements, InnoDB will automatically add exclusive locks to the data sets involved;

For ordinary SELECT statements, InnoDB does not apply any locks.

Clearance lock (next key lock)

When we retrieve data with range conditions instead of equal conditions and request shared or exclusive locks, InnoDB will lock the index entries of existing data that meet the conditions;

For records whose key value is within the condition range but does not exist, it is called "gap". InnoDB will also lock this "gap". This locking mechanism is the so-called gap lock.

For example, all existing or nonexistent data in between and will be locked. Can prevent unreal reading.

MVCC (lock free implementation) multi version concurrency control mechanism

Maintain an initial snapshot of data for each transaction

Database isolation level

1. Uncommitted reading:

  Read/Read / write: transactions do not perform any isolation operations

   Write: obtain the exclusive lock of records. It cannot be performed at the same time unless a transaction is committed or rolled back

2. Read submitted:

(if other transactions are committed or rolled back, it will read it immediately)

 Read: the transaction reads the initial snapshot of the transaction mvcc mechanism

 Read / write: read snapshot data and write snapshot data mvcc mechanism

 Write: obtain the exclusive lock of records. It cannot be performed at the same time unless a transaction is committed or rolled back

3. Repeatable reading (jdbc default isolation level)

 Read: transactions read snapshot data mvcc mechanism

 Read / write: what is read is snapshot data, and what is written is snapshot data (unless the current transaction is committed or rolled back, what is accessed is snapshot data) mvcc mechanism

 Write: obtain the exclusive lock of records. It cannot be performed at the same time unless a transaction is committed or rolled back

4. Serialization

Read: multiple transactions sharing locks can be acquired at the same time

Reading and writing : Shared lock and exclusive lock

Write: exclusive lock and exclusive lock

Spring transaction

Spring transactions can be divided into two types:

  • Programming transactions (implementing transactions through code)
  • Declarative transactions (transactions are implemented by configuration)

Programming transactions are relatively simple to implement in Spring, while declarative transactions are much more difficult to implement because they encapsulate a large number of things (generally, they are simple to use and very complex inside).

Spring transactions are based on Spring AOP, which is a dynamic proxy used at the bottom of Spring AOP. There are two ways of dynamic proxy:

  • Interface based agent (JDK agent)

    • Based on interface proxy, all class methods are not public modified or modified with static keyword,
    • None of these methods can be enhanced by Spring AOP
  • CGLib based agent (subclass agent)

    • Based on the subclass proxy, the methods of all classes are decorated with private, static and final,
    • None of these methods can be enhanced by Spring AOP

Those methods that cannot be enhanced by Spring AOP do not work in a transactional environment. As long as they are called by the outer transaction method, due to the propagation level of Spring transaction management, the internal method can also work in the transaction context started by the external method.

Spring transactions can be divided into two types:

  • Programming transactions (implementing transactions through code)
  • Declarative transactions (transactions are implemented by configuration)

Programming transactions are relatively simple to implement in Spring, while declarative transactions are much more difficult to implement because they encapsulate a large number of things (generally, they are simple to use and very complex inside).

Programming transaction

The interface PlatformTransactionManager defines the behavior of transaction operations, which depends on the TransactionDefinition and TransactionStatus interfaces

PlatformTransactionManager:

Transaction manager interface

(a set of behaviors are defined, and the specific implementation is completed by different persistence Frameworks - similar to JDBC)

Perform commit or rollback operations according to the given transaction rules

Public interface PlatformTransactionManager()...{ 
 // Return a currently active transaction or create a new one, according to the specified propagation behavior
 TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; 
 // Commit the given transaction, with honor to its status
 Void commit(TransactionStatus status) throws TransactionException; 
 // Perform a rollback of the given transaction
 Void rollback(TransactionStatus status) throws TransactionException; 
 } 

AbstractPlatformTransactionManager abstract class implements the standard process of Spring transactions, and its subclass DataSourceTransactionManager is the JDBC single data source transaction manager we use more

<!-- Transaction manager -->
	<bean id="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<!-- data source -->
		<property name="dataSource" ref="dataSource" />
	</bean>

Transaction manager interface PlatformTransactionManager

Get a transaction through gettransaction (transactiondefinition) method,

The parameter in this method is the TransactionDefinition class, which defines some basic transaction attributes.

TransactionDefinition:

Transaction definition information

Defines Spring compatible transaction attributes

(such as transaction isolation level, transaction propagation, transaction timeout, read-only status)

public interface TransactionDefinition {
 // The propagation behavior of the returned transaction is propagation by default_ REQUIRED
 int getPropagationBehavior(); 
 // Returns the isolation level of the transaction, according to which the transaction manager controls which data in this transaction can be seen by another transaction
 // The transaction isolation level is valid only when a new transaction is created, that is, it only corresponds to the propagation attribute PROPAGATION_REQUIRED and promotion_ REQUIRES_ NEW
 int getIsolationLevel(); 
 // Returns the number of seconds a transaction must complete. This is also valid only when a new transaction is created
 int getTimeout(); 
 //Returns the name of the transaction. The default value in declarative transactions is "fully qualified name of class. Method name"
 String getName();

 // Returns whether to optimize as a read-only transaction.
 boolean isReadOnly();
}

(1) Transaction isolation level

(defines the extent to which a transaction may be affected by other concurrent transactions)

Five constants representing the isolation level are defined in the TransactionDefinition interface:

  • TransactionDefinition.ISOLATION_DEFAULT:

    Use the default isolation level of the back-end database

    Mysql uses repeatable by default_ Read isolation level

    Oracle uses read by default_ Committed isolation level

  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED:

    The lowest isolation level that allows reading of uncommitted data changes

    It may cause dirty reading, unreal reading or non repeatable reading

  • TransactionDefinition.ISOLATION_READ_COMMITTED:

    Allow reading data that has been committed by concurrent transactions

    Unreadable or unreadable, but unreadable

  • TransactionDefinition.ISOLATION_REPEATABLE_READ:

    The results of multiple reads of the same field are consistent, unless the data is modified by its own transaction

    Dirty reading and non repeatable reading can be prevented, but phantom reading can still occur.

  • TransactionDefinition.ISOLATION_SERIALIZABLE:

    The highest isolation level, completely subject to the isolation level of ACID.

    All transactions are executed one by one, so that there is no interference between transactions

    This level prevents dirty reads, non repeatable reads, and unreal reads.

    However, this will seriously affect the performance of the program. This level is not normally used.

(2) Transaction propagation behavior

(to solve the transaction problem of calling each other between business layer methods)

Current transaction support:

  • TransactionDefinition.PROPAGATION_REQUIRED:

    If a transaction currently exists, join the transaction; If there is no current transaction, a new transaction is created.

  • TransactionDefinition.PROPAGATION_SUPPORTS:

    If a transaction currently exists, join the transaction; If there are currently no transactions, continue to run in a non transactional manner.

  • TransactionDefinition.PROPAGATION_MANDATORY:

    If a transaction currently exists, join the transaction; If there is no current transaction, an exception is thrown. (mandatory: mandatory)

The current transaction is not supported:

  • TransactionDefinition.PROPAGATION_REQUIRES_NEW:

    Create a new transaction. If there is a current transaction, suspend the current transaction.

  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED:

    Run in non transactional mode. If there is a transaction currently, suspend the current transaction.

  • TransactionDefinition.PROPAGATION_NEVER:

    Run in non transactional mode. If there is a transaction currently, an exception will be thrown.

Other situations:

  • TransactionDefinition.PROPAGATION_NESTED:

    If a transaction currently exists, create a transaction to run as a nested transaction of the current transaction; If there is no transaction at present, this value is equivalent to transactiondefinition PROPAGATION_ REQUIRED.

    PROPAGATION_ The transaction started by nested is embedded in the external transaction (if there is an external transaction). At this time, the embedded transaction is not an independent transaction. It depends on the existence of the external transaction. Only through the external transaction submission can the internal transaction be committed, and the nested sub transaction cannot be committed separately.

(3) Transaction timeout attribute

(maximum time allowed to execute a transaction)

The so-called transaction timeout refers to the maximum time allowed for the execution of a transaction. If the time limit is exceeded but the transaction has not been completed, the transaction will be rolled back automatically.

In the TransactionDefinition, the timeout time is represented by the value of int, and its unit is seconds.

(4) Transaction read-only attribute

(whether to perform read-only operation on transaction resources)

The read-only attribute of a transaction refers to the read-only operation or read-write operation on transactional resources.

The so-called transactional resources refer to those resources managed by transactions, such as data sources, JMS resources, custom transactional resources and so on.

If we decide to only perform read-only operations on transactional resources, we can mark transactions as read-only to improve the performance of transaction processing.

In the TransactionDefinition, boolean type is used to indicate whether the transaction is read-only.

(5) Rollback rule

(define transaction rollback rules)

These rules define which exceptions will cause the transaction to roll back and which will not.

By default, transactions are rolled back only when they encounter run-time exceptions, but not when they encounter check exceptions (this behavior is consistent with the rollback behavior of EJB s).

However, you can declare that when a transaction encounters a specific check exception, it will roll back as if it encountered a run-time exception.

Similarly, you can also declare that the transaction will not roll back when it encounters specific exceptions, even if these exceptions are run-time exceptions.

TransactionStatus:

Transaction running status

Represents the specific running state of the transaction

(obtain the information of transaction running status, or indirectly roll back transactions and other operations through this interface)

The TransactionStatus interface is used to record the status of transactions. It defines a set of methods to obtain or judge the corresponding status information of transactions

PlatformTransactionManager. The gettransaction (...) method returns a TransactionStatus object.

The returned TransactionStatus object may represent a new or existing transaction (if there is a qualified transaction on the current call stack).

The contents of the TransactionStatus interface are as follows:

public interface TransactionStatus{
 boolean isNewTransaction(); // Is it something new
 boolean hasSavepoint(); // Whether there are recovery points. In the nested transaction scenario, judge whether the current transaction contains savepoints
 void setRollbackOnly(); // Set to rollback only
 boolean isRollbackOnly(); // Rollback only
 boolean isCompleted; // Completed (committed or rolled back)
 flush; // Refresh the modification in the underlying session to the database, which is generally used to refresh the session such as Hibernate/JPA. Whether it takes effect is determined by the implementation of specific transaction resources;
} 

Declarative transaction

Spring uses AOP (aspect oriented programming) to implement declarative transactions

By default, Spring uses JDK dynamic proxy for interface implementation and CGLIB for specific classes. At the same time, it also supports global configuration and CGLIB to generate proxy objects.

The implementation of declarative transaction is to start the transaction before the execution of the target method and commit or roll back the transaction after the execution of the target method through surround enhancement,

Surround enhancement (org.aopalliance.intercept.MethodInterceptor): enhancement is performed before and after the target method is executed;

@transactional annotation

Spring transaction propagation mechanism

Calling other methods inside the current transaction containing method (whether the method contains transactions or not) belongs to the knowledge point category of Spring transaction propagation mechanism.

  1. REQUIRED(Spring's default transaction propagation type)

If there is no current transaction, create a new transaction. If there is a current transaction, join the transaction

(example 1)

//Declare transactions on testMain and testB, and set propagation behavior REQUIRED
@Transactional(propagation = Propagation.REQUIRED)
public void testMain(){    
A(a1);  //Call a to enter parameter a1    
testB();    //Call testB
}
@Transactional(propagation = Propagation.REQUIRED)
public void testB(){    
B(b1);  //Call B input parameter b1    
throw Exception;     //Exception thrown    
B(b2);  //Call B input parameter b2
}
//When the testB method is executed, the transaction of testMain is added (if there is a transaction currently, this transaction is added)

(example 2)

//Declare the transaction on testB and set the propagation behavior REQUIRED
public void testMain(){    
A(a1);  //Call a to enter parameter a1    
testB();    //Call testB
}
@Transactional(propagation = Propagation.REQUIRED)
public void testB(){    
B(b1);  //Call B input parameter b1    
throw Exception;     //Exception thrown    
B(b2);  //Call B input parameter b2
}

2.SUPPORTS

If there is a current transaction, join the current transaction. If there is no current transaction, execute it in a non transaction method

(example 3)

//Only declare transactions on testB and set the propagation behavior SUPPORTS
public void testMain(){
    A(a1);  //Call a to enter parameter a1
    testB();    //Call testB
}
@Transactional(propagation = Propagation.SUPPORTS)
public void testB(){
    B(b1);  //Call B input parameter b1
    throw Exception;     //Exception thrown
    B(b2);  //Call B input parameter b2
}
//There is no transaction when executing testB (if there is no transaction at present, it will be executed in a non transaction method)

When we declare a transaction on testMain and use the REQUIRED propagation method, the execution of testB at this time meets the current transaction. Then join the current transaction. When testB throws an exception, the transaction will be rolled back. The final result is that a1, b1 and b2 will not be stored in the database

3.MANDATORY

If the current transaction exists, join the current transaction. If the current transaction does not exist, throw an exception.

(example 4)

//Only declare transactions on testB and set the propagation behavior MANDATORY
public void testMain(){
    A(a1);  //Call a to enter parameter a1
    testB();    //Call testB
}
@Transactional(propagation = Propagation.MANDATORY)
public void testB(){
    B(b1);  //Call B input parameter b1
    throw Exception;     //Exception thrown
    B(b2);  //Call B input parameter b2
}
//When the testB method is executed, the exception required by the transaction will be thrown directly (if the current transaction does not exist, the exception will be thrown)

4.REQUIRES_NEW

Create a new transaction. If there is a current transaction, suspend it.

(example 5)

//Change the location of the exception to testMain, then declare the transaction to testMain, and set the propagation type to REQUIRED
//testB also declares transactions and sets the propagation type to requirements_ NEW
@Transactional(propagation = Propagation.REQUIRED)
public void testMain(){
    A(a1);  //Call a to enter parameter a1
    testB();    //Call testB
    throw Exception;     //Exception thrown
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void testB(){
    B(b1);  //Call B input parameter b1
    B(b2);  //Call B input parameter b2
}

5.NOT_SUPPORTED

Always execute in a non transactional manner. If a transaction currently exists, suspend the current transaction

(example 6)

// testMain propagation type is set to REQUIRED
// testB propagation type is set to NOT_SUPPORTED, and the exception throw position is in testB
@Transactional(propagation = Propagation.REQUIRED)
public void testMain(){
    A(a1);  //Call a to enter parameter a1
    testB();    //Call testB
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void testB(){
    B(b1);  //Call B input parameter b1
    throw Exception;     //Exception thrown
    B(b2);  //Call B input parameter b2
}
//testMain has transactions, but testB does not use transactions, so testB storage b1 succeeds during execution, and then throws an exception. At this time, testMain detects that the abnormal transaction is rolled back
//Since testB is not in the transaction, only storage a1 of testMain is rolled back, and finally only storage b1 is successful

6.NEVER

No transaction is used. If the current transaction exists, an exception will be thrown

(Example 7)

//testMain sets the propagation type to REQUIRED
//The testB propagation type is set to NEVER, and the exception throwing code in testB is removed
@Transactional(propagation = Propagation.REQUIRED)
public void testMain(){
    A(a1);  //Call a to enter parameter a1
    testB();    //Call testB
}
@Transactional(propagation = Propagation.NEVER)
public void testB(){
    B(b1);  //Call B input parameter b1
    B(b2);  //Call B input parameter b2
}

7.NESTED

If the current transaction exists, it is executed in the nested transaction. Otherwise, the REQUIRED operation is the same (start a transaction)

There are two points to note:

  • And requirements_ New differences

REQUIRES_NEW is to create a new transaction, and the newly opened transaction has nothing to do with the original transaction

NESTED means that a NESTED transaction (called a child transaction) will be opened when the current transaction exists (we call the current transaction the parent transaction).

In the NESTED case, when the parent transaction rolls back, the child transaction will also roll back,

And in requirements_ In the case of new, the original transaction is rolled back and the newly opened transaction will not be affected.

  • And REQUIRED

When REQUIRED, if the caller has a transaction, the callee and the caller use the same transaction,

If the callee has an exception, the transaction will be rolled back no matter whether the caller catch es the exception or not because the callee shares the same transaction
In the NESTED case, when an exception occurs to the callee, the caller can catch the exception, so that only the child transaction is rolled back and the parent transaction is not affected

(example 8)

//testMain is set to REQUIRED, testB is set to needed, and the exception occurs in testMain
@Transactional(propagation = Propagation.REQUIRED)
public void testMain(){
    A(a1);  //Call a to enter parameter a1
    testB();    //Call testB
    throw Exception;     //Exception thrown
}
@Transactional(propagation = Propagation.NESTED)
public void testB(){
    B(b1);  //Call B input parameter b1
    B(b2);  //Call B input parameter b2
}

(Example 9)

//testMain is set to REQUIRED, testB is set to needed, and the exception occurs in testB
@Transactional(propagation = Propagation.REQUIRED)
public void testMain(){
    A(a1);  //Call a to enter parameter a1
    try{
        testB();    //Call testB
    }catch(Exception e){

    }
    A(a2);
}
@Transactional(propagation = Propagation.NESTED)
public void testB(){
    B(b1);  //Call B input parameter b1
    throw Exception;     //Exception thrown
    B(b2);  //Call b parameter b2
}
//As a result, A1 and A2 are stored successfully, while b1 and b2 fail. Because the caller catch es the exception of the called party, only the sub transaction is rolled back.
//Change the propagation type of testB to REQUIRED
//As a result, no data storage was successful. Even if the caller catch es an exception, the whole transaction will be rolled back because the caller and the callee share the same transaction

Transaction behavior

The behavior of transaction includes transaction start, transaction COMMIT and transaction ROLLBACK. All SQL executions of InnoDB users are under transaction control. By default, autocommit is set to true. After a single SQL is executed successfully, MySQL will automatically COMMIT the transaction, or if there is an error in SQL execution, execute transaction COMMIT or ROLLBACK according to the exception type. You can use START TRANSACTION (SQL standard) or BEGIN to start transactions, and COMMIT and ROLLBACK transactions using COMMIT and ROLLBACK; You can also control the transaction behavior by setting the autocommit attribute. When autocommit is set to false, multiple SQL statements executed later will be in one transaction, and will not be committed or rolled back until the COMMIT or ROLLBACK transaction is executed.

Problems caused by concurrent transactions

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

  • Dirty read: when a transaction is accessing data and modifying the data, but the modification has not been committed to the database, another transaction also accesses the data and uses the 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.
  • Lost to modify: when a transaction reads a data, another transaction also accesses the data. After modifying the data in the first transaction, the second transaction also modifies the data. In this way, the modification result in the first transaction is lost, so it is called lost modification.
  • For example: transaction 1 reads data A=20 in a table, transaction 2 also reads A=20, transaction 1 modifies A=A-1, transaction 2 also modifies A=A-1, the final result is A=19, and the modification of transaction 1 is lost.
  • Unrepeatable read: refers to reading the same data multiple times in a transaction. When this transaction is not finished, another transaction also accesses the data. Then, between the two data reads in the first transaction, the data read in the first transaction may be different due to the modification of the second transaction. This occurs when the data read twice in a transaction is different, so it is called non repeatable reading.
  • Phantom read: phantom read is similar to non repeatable reading. It occurs when a transaction (T1) reads several rows of data and then another concurrent transaction (T2) inserts some data. In the subsequent query, the first transaction (T1) will find more records that do not exist originally, just like an illusion, so it is called phantom reading.

Difference between non repeatability and unreal reading:

The key point of non repeatable reading is to modify, and the key point of phantom reading is to add or delete.

AOP enhancement

Talk about dynamic agents and AOP enhancements.

Dynamic proxy is the default way for Spring to implement AOP

There are two kinds: JDK dynamic agent and CGLIB dynamic agent.

JDK dynamic proxy is interface oriented, and generates anonymous implementation classes of the target proxy interface through reflection;

CGLIB dynamic proxy generates proxy subclasses for the target proxy class through inheritance and bytecode enhancement technology (or object class library).

By default, Spring uses JDK dynamic proxy for interface implementation and CGLIB for specific classes. At the same time, it also supports global configuration and CGLIB to generate proxy objects.

Spring can enhance methods in five ways:

  • Pre enhancement (org.springframework.aop.BeforeAdvice): enhancement before the execution of the target method;
  • Post enhancement (org.springframework.aop.AfterReturningAdvice): enhance after the target method is executed;
  • Surround enhancement (org.aopalliance.intercept.MethodInterceptor): enhancement is performed before and after the target method is executed;
  • Exception throwing enhancement (org.springframework.aop.ThrowsAdvice): perform enhancement after the target method throws an exception;
  • Introduction enhancement (org.springframework.aop.IntroductionInterceptor): add new methods and properties to the target class.

The implementation of declarative transaction is to start the transaction before the execution of the target method and commit or roll back the transaction after the execution of the target method through surround enhancement,

Spring transaction interception

We have learned about the process of generating proxy objects by weaving AOP aspects. When Bean methods are called through proxy objects, the corresponding AOP enhanced interceptors will be triggered,

As mentioned earlier, declarative transaction is a surround enhancement. The corresponding interface is MethodInterceptor, and the implementation of this interface by transaction enhancement is TransactionInterceptor

[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-hwp6zo19-1646826313169) (C: \ users \ yc01893 \ appdata \ roaming \ typora user images \ image-20211227111851816. PNG)]

In the invoke method, the transaction interceptor invokes the invokeWithinTransaction method of the parent class TransactionAspectSupport for transaction processing. This method supports declarative transactions and programmatic transactions.

// TransactionInterceptor.class
@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
    // Get targetClass
    ...

    // Adapt to TransactionAspectSupport's invokeWithinTransaction...
    return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
        @Override
        public Object proceedWithInvocation() throws Throwable {
            // Actual implementation target method
            return invocation.proceed();
        }
    });
}

// TransactionInterceptor parent class transactionaspectsupport class
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
        throws Throwable {

    // If the transaction attribute is null, the method is non-transactional.
    // Query the transaction attribute of the target method, determine the transaction manager, and construct the connection point ID (used to confirm the transaction name)
    final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
    final PlatformTransactionManager tm = determineTransactionManager(txAttr);
    final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

    if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
        // Transaction acquisition
        TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
        Object retVal = null;
        try {
            // Execute target method through callback
            retVal = invocation.proceedWithInvocation();
        }
        catch (Throwable ex) {
            // The target method throws an exception and performs transaction commit or rollback operations according to the exception type
            completeTransactionAfterThrowing(txInfo, ex);
            throw ex;
        }
        finally {
            // Clean up the current thread transaction information
            cleanupTransactionInfo(txInfo);
        }
        // The target method was executed successfully and the transaction was committed
        commitTransactionAfterReturning(txInfo);
        return retVal;
    } else {
        // Transaction execution processing with callback is generally used for programmatic transactions
        ...
    }
}

It is mentioned that the core interface of transaction abstraction is platform transaction manager, which is responsible for managing transaction behavior, including transaction acquisition, submission and rollback. In the invokeWithinTransaction method, we can see that createTransactionIfNecessary, commitTransactionAfterReturning and completeTransactionAfterThrowing are all programmed for this interface and do not depend on a specific transaction manager

//TransactionAspectSupport.class
protected TransactionInfo createTransactionIfNecessary(
        PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) {
    ...
    TransactionStatus status = null;
    if (txAttr != null) {
        if (tm != null) {
            // Get transaction
            status = tm.getTransaction(txAttr);
            ...
}

protected void commitTransactionAfterReturning(TransactionInfo txInfo) {
    if (txInfo != null && txInfo.hasTransaction()) {
        ...
        // Commit transaction
        txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
    }
}

protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
    if (txInfo != null && txInfo.hasTransaction()) {
        ...
        if (txInfo.transactionAttribute.rollbackOn(ex)) {
            try {
                // The exception type is rollback exception, and transaction rollback is executed
                txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
            }
            ...
        } else {
            try {
                // The exception type is non rollback exception, and the transaction submission is still executed
                txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
            }
            ...
}

protected final class TransactionInfo {
    private final PlatformTransactionManager transactionManager;
    ...

When obtaining a transaction, the AbstractPlatformTransactionManager#doBegin method is responsible for starting a new transaction. The following code is available in the DataSourceTransactionManager:

@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
    // Get database connection con
    ...
    if (con.getAutoCommit()) {
        txObject.setMustRestoreAutoCommit(true);
        if (logger.isDebugEnabled()) {
            logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
        }
        con.setAutoCommit(false);
    }
    ...
}

MVCC

Multi version concurrency control

MVCC is a method of concurrency control. Generally, it realizes the concurrent access to the database in the database management system.

In the InnoDB engine of Mysql, it refers to

Transactions under the two isolation levels of read committed and repeatable read

For the SELECT operation, the process of accessing records in the version chain.

This allows other transactions to modify this record. Anyway, each modification will be recorded in the version chain.

SELECT can get records from the version chain, which realizes the concurrent execution of read-write and write read, and improves the performance of the system.

Version chain

Let's first understand the concept of version chain.

In the InnoDB engine table, there are two necessary hidden columns in its cluster index record:

trx_id

This id is used to store the transaction id every time a cluster index record is modified.

roll_pointer

Rollback pointer to the Undo Log information of this record.

Every time a cluster index record is modified, the old version will be written to the undo log. This roll_ A pointer is stored in the pointer, which points to the location of the previous version of the clustered index record and obtains the record information of the previous version through it. (note that the undo log of the insert operation does not have this attribute because it does not have the old version)

The difference between committed reads and repeatable reads is that they have different strategies for generating readviews.

ReadView mainly has a list to store the currently active read-write transactions in our system, that is, begin the uncommitted transactions

Suppose the transaction id in the current list is [80100].

If the transaction id of the record version you want to access is 50, which is smaller than the smallest id 80 in the current list, it means that the transaction has been committed before, so it is accessible to the currently active transaction.

If the transaction id of the record version you want to access is 90 and it is found that the transaction is between the maximum and minimum values of the list id, then judge whether it is in the list,

If it is, it means that the transaction has not been committed, so the version cannot be accessed.

If not, the transaction has been committed, so the version can be accessed.

If the transaction id of the record version you want to access is 110, which is larger than the maximum id 100 of the transaction list, it means that this version occurs only after ReadView is generated, so it cannot be accessed.

The read isolation level has been committed. At this time, you will re create a ReadView, and the value in your active transaction list will change to [110].

Transactions under the committed read isolation level will generate an independent ReadView at the beginning of each query

**Repeatable read isolation level. At this time, your ReadView is still the ReadView generated during the first select ion, * * that is, the value of the list is still [100].

The repeatable read isolation level generates a ReadView when reading for the first time, and subsequent reads reuse the previous ReadView

What problems can MVCC solve?

  • For the problem of blocking between reads and writes, MVCC can make reads and writes not blocking each other, reads not blocking each other, and writes not blocking reads, which can improve the ability of data concurrent processing.
  • This reduces the probability of deadlock. This is because MVCC adopts optimistic locking. When reading data, it does not need to lock. When writing, it only needs to lock the necessary rows.
  • It solves the problem of consistent reading. When we take a snapshot of a database at a point in time, we can only see the update results submitted by transactions before the point in time, but not after the point in time.

lock

Optimistic lock:

Optimistic locks are not built into the database, and we need to implement them ourselves.

Optimistic lock refers to the operation of the database (update operation). I am optimistic that this operation will not lead to conflict. When operating the data, I will not carry out any other special processing (i.e. no lock). After the update, I will judge whether there is a conflict.

1. Query the commodity information

select (status,status,version) from t_goods where id=#{id}

2. Generate orders based on commodity information

3. Modify the commodity status to 2

update t_goods

set status=2,version=version+1

where id=#{id} and version=#{version};

Pessimistic lock

The pessimistic lock corresponds to the optimistic lock.

Pessimistic lock means that when operating data, it is considered that there will be data conflict during this operation,

Therefore, each operation can only be performed on the same data by obtaining the lock,

This is very similar to synchronized in java, so pessimistic locking takes more time.

In addition, corresponding to the optimistic lock, the pessimistic lock is realized by the database itself. When we want to use it, we can directly call the relevant statements of the database.

S hared lock:

Share (S) is used for operations that do not change or update data (read-only operations), such as SELECT statements.

If transaction T adds A shared lock to data A, other transactions can only add A shared lock to A, not an exclusive lock. Transactions approved to share locks can only read data and cannot modify data.

By adding lock in share mode after the execution statement, it means that a shared lock is added to some resources

Exclusive lock (X lock):

Used for data modification operations, such as INSERT, UPDATE, or DELETE. Ensure that multiple updates are not made to the same resource at the same time.

If transaction T adds an exclusive lock to data A, other transactions cannot add any type of lock to A. Transactions that are granted exclusive locks can both read and modify data.

For update, insert and delete statements, exclusive locks will be automatically added

Adding for update after the statement to be executed means adding exclusive locks to some resources

Invalid transaction annotation

The default implementation of transactions in Spring uses AOP, that is, the proxy method. If you use code testing, you need to use the injected object to call the methods in the same Service class when calling each other. Do not directly use this Method name, this The method name call is an internal method call of the object and will not pass through the Spring proxy, that is, the transaction will not work

Topics: Java Database Transaction