What is a transaction

Simply speaking, transaction is a logical unit in the process of database management system execution. It can ensure that either a group of database operations are all executed successfully or all failed. The principle of doing this is the four PROPERTIES of ACID transaction.

ACID

  • Atomicity
    • Atomicity refers to the fact that a transaction is an indivisible unit of work (such as the overall relationship between a nucleus and an electron) and that all operations in a transaction either succeed or fail. For example, SQL statements in the same transaction either all execute successfully or all execute fail.
  • Consistency
    • Transactions must change the database from one correct state to another.
    • For example, if I transfer money, I have 200, you have 200, I transfer 100 to you, if the transfer is successful, you are 300, I am 100; I’m still 200, you’re still 200, you can’t have me 100, you’re 200
  • Isolation
    • The isolation of transactions means that when multiple users concurrently access the database, the transactions opened by the database for each user cannot be disturbed by the operation data of other transactions, and multiple concurrent transactions must be isolated from each other.
  • “Durability”
    • Persistence means that once a transaction is committed, its changes to the data in the database are permanent and should not be affected by subsequent database failures.

Isolation level

Isolation level Dirty read Unrepeatable read Phantom read
READ UNCOMMITTED y y y
READ COMMITTED n y y
REPEATABLE READ n n y
SERIALIZABLE n n n

Noun explanation

  • Dirty read: Modified data can be seen by other objects before the object is committed
    • Query for other things modified but not committed to data
  • Non-repeatable read: A query that uses the same criteria for one item yields different data because it was modified by another item at the same time (committed item). The second query returns the data modified by another item.
    • Is the content of the same record (a piece of data) modified by other things, focus on the update, delete operations on a piece of data.
  • Illusionary reading: when one object A queries multiple data using the same criteria, other objects add or delete data matching the criteria in object A. At this time, when object A queries again, it will find more or less data, which is different from the results of the previous query.
    • It is the increase or decrease of rows in a query range (multiple rows) due to insert and DELETE operations.

  • Read uncommitted: When an item is not committed, other items can see changes to the data in that item.
  • Read Committed: Changes to data made by an object cannot be seen by other objects until the object is committed. This is sometimes called unrepeatable reads, because two queries with the same condition may yield different results.
  • Repeatable read: the result of multiple queries under the same conditions in the same transaction is the same. Innodb storage engine solves the illusion problem with multi-version concurrency control.
  • Serializable: Putting things on a queue to execute sequentially, one at a time, can close three exception problems, but at the expense of concurrency performance.

Single transaction

  • In traditional single application architecture, our business data is usually stored in a database, and each module in the application directly operates on the database. In this scenario, transactions are guaranteed by acid-based features provided by the database.

Distributed transactions for microservices

  • In the microservice state, a single application is divided into multiple services, for example, the mall system is divided into commodities, orders, inventory and other services. The corresponding database of each service is different, and the transaction of each service can only guarantee the ACID of the local transaction. If the order service orders successfully, the inventory service deducts the amount, and each service only manages its own transaction, the inventory rollback does not cause the order rollback, resulting in data inconsistency and other problems.

What are distributed transactions

2PC Phase 2 submission

  • 2PC :(two-phase commit protocol).
    • In the field of computer networks and databases, two-phase Commit refers to an Algorithm designed to make transactions Commit consistently among all nodes based on distributed system architecture. Commonly, two-phase commit is also called a Protocol. In a distributed system, although each node can know the success or failure of its own operation, it cannot know the success or failure of other nodes’ operation. When a transaction across multiple nodes, in order to keep the ACID characteristic of transaction, need to introduce a unity as coordinator component to control all the nodes (referred to as participants) operating results and eventually indicating whether the node should submit the operating results are real (for example, the updated data to disk, etc.). Therefore, the algorithm idea of two-stage submission can be summarized as follows: participants will inform the coordinator of the success or failure of the operation, and then the coordinator will decide whether to submit the operation or stop the operation according to the feedback information of all participants.
    • In human language, there is a coordinator between two or more services, the service is called participant, the coordinator asks the participant whether the transaction can be committed, both parties answer yes, the transaction can be committed, if one party answers otherwise all back

Two phases

  • Stage one: voting
    • All successes or one or more failures
  • Phase 2: Commit or rollback
    • On all success, the transaction is committed, and on failure, it is rolled back

Two-stage problem

  • Performance issues
Both during the first phase and during the second phase, all participant and coordinator resources are locked, and only when all nodes are ready will the transaction coordinator notify the global commit and the participant will release the resources after the local transaction commit. This process takes a long time and has a significant impact on performance.Copy the code
  • A single point of the problem
Because of the importance of the coordinator, if the coordinator fails. The participants will keep blocking. Especially in phase 2, when the coordinator fails, all participants are still locked in the transaction resources and cannot continue to complete the transaction. (Although the coordinator fails, you can re-elect a coordinator, but it does not solve the problem of participants being blocked because the coordinator is down)Copy the code

3PC three phase commit protocol

  • 3PC: Three-phase commit in Chinese. The emergence of 2PC is to solve some problems of 2PC. Compared with 2PC, it also introduces timeout mechanism among participants, and adds a new stage for participants to use this stage to unify their states.
    • 3PC consists of three phases: preparation phase, pre-commit phase, and commit phase. The corresponding English versions are CanCommit, PreCommit, and DoCommit

TCC

  • TCC: TCC stands for try-confirm-cancel. Also known as compensation transactions. The idea is: “Register a corresponding acknowledgement and compensation (undo action) for each action.”
    • Try indicates reservation, that is, resource reservation and lock.
    • Confirm means to Confirm the operation, which is actually performed.
    • Cancel: Cancel: Cancel: Cancel: Cancel: Cancel: Cancel: Cancel: Cancel

Distributed transaction pragmatic operation Alibaba open source distributed transaction solution – Seata

  • The document: seata. IO/useful – cn/docs /…
  • The install guide: seata. IO/useful – cn/docs /…

introduce

Seata is an open source distributed transaction solution dedicated to providing high performance and easy to use distributed transaction services. Seata will provide users with AT, TCC, SAGA and XA transaction modes to create a one-stop distributed solution for users.

The installation

  • You can install it by following the install Guide steps, or you can clone it directly and start it locallyseata-serve.
  • Github address: github.com/seata/seata…

Alibaba Seata distributed transaction example

spring cloud + nacos + feign + seata demo

Version dependency

Github.com/alibaba/spr…

jar version

Spring-cloud-alibaba-dependencies 2.2.6.RELEASE Spring-cloud-dependencies hoxton. SR9 nacos 1.3.0 seata 1.3.0Copy the code

The first step

Download seata – server

Download the server version of the JAR package

Download link: github.com/seata/seata…

Docker compose way

Seata. IO/useful – cn/docs /…

version: "3"
services:
  seata-server:
    image: seataio/seata-server
    hostname: seata-server
    ports:
      - "8091:8091"
    environment:
      - SEATA_PORT=8091
      - STORE_MODE=file
Copy the code

The second step

Configuration seata – server config

  • registry.conf
Registry {# select your own registration method, # file, nacOS, Eureka, Redis, ZK, Consul, ETCD3, SOFA type = "nacos" nacos {application = "seata-server" serverAddr = "nacos.blogyg.cn:9999" group = "DEFAULT_GROUP" namespace = "" cluster = "DEFAULT" username = "nacos" Password = "nacos"} file {name = "file.conf"}} config { # file, nacOS, Apollo, ZK, Consul, etCD3 type = "nacos" file {name = "file.conf"} nacos {serverAddr = "nacos.blogyg.cn:9999" group = "DEFAULT_GROUP" namespace = "" username = "nacos" password = "nacos" cluster = "DEFAULT" }}Copy the code

Configure the configuration file to the NACOS configuration center

Delete the configuration as required and change the configuration value in the configuration file

  • config.txt
service.vgroupMapping.my_test_tx_group=default
service.enableDegrade=false
service.disableGlobalTransaction=false
store.mode=db
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://www.blogyg.cn:3306/seata?useUnicode=true&rewriteBatchedStatements=true
store.db.user=root
store.db.password=123456
store.db.minConn=5
store.db.maxConn=30
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.queryLimit=100
store.db.lockTable=lock_table
store.db.maxWait=5000
Copy the code

Execute the shell script to synchronize the configuration information to nacOS

Change the script information to your NACOS information

  • seata-nacos.sh
#! /bin/sh
# Copyright 1999-2019 Seata.io Group.
#
Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
#You may obtain a copy of the License at,
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.while getopts ":h:p:g:t:u:w:" opt do case $opt in h) host=$OPTARG ;; p) port=$OPTARG ;; g) group=$OPTARG ;; t) tenant=$OPTARG ;; u) username=$OPTARG ;; w) password=$OPTARG ;; ?). echo " USAGE OPTION: $0 [-h host] [-p port] [-g group] [-t tenant] [-u username] [-w password] " exit 1 ;; esac done if [ -z ${host} ]; then host=nacos.blogyg.cn fi if [ -z ${port} ]; then port=9999 fi if [ -z ${group} ]; then group="DEFAULT_GROUP" fi if [ -z ${tenant} ]; then tenant="" fi if [ -z ${username} ]; then username="nacos" fi if [ -z ${password} ]; then password="nacos" fi nacosAddr=$host:$port contentType="content-type:application/json; charset=UTF-8" echo "set nacosAddr=$nacosAddr" echo "set group=$group" urlencode() { length="${#1}" i=0 while [ $length -gt $i ]; do char="${1:$i:1}" case $char in [a-zA-Z0-9.~_-]) printf $char ;; *) printf '%%%02X' "'$char" ;; esac i=`expr $i + 1` done } failCount=0 tempLog=$(mktemp -u) function addConfig() { dataId=`urlencode $1` content=`urlencode $2` curl -X POST -H "${contentType}" "http://$nacosAddr/nacos/v1/cs/configs? dataId=$dataId&group=$group&content=$content&tenant=$tenant&username=$username&password=$password" >"${tempLog}" 2>/dev/null if [ -z $(cat "${tempLog}") ]; then echo " Please check the cluster status. " exit 1 fi if [ "$(cat "${tempLog}")" == "true" ]; then echo "Set $1=$2 successfully " else echo "Set $1=$2 failure " failCount=`expr $failCount + 1` fi } count=0 for line  in $(cat $(dirname "$PWD")/config.txt | sed s/[[:space:]]//g); do count=`expr $count + 1` key=${line%%=*} value=${line#*=} addConfig "${key}" "${value}" done echo "=========================================================================" echo " Complete initialization parameters, total-count:$count , failure-count:$failCount " echo "=========================================================================" if [ ${failCount} -eq 0 ]; then echo " Init nacos config finished, please start seata-server. " else echo " init nacos config fail. " fiCopy the code

The third step

Add the SEATA configuration to the project startup configuration

The client configures the client config

seata:
  enabled: true
  The configuration must be consistent with that on the server
  tx-service-group: my_test_tx_group
  application-id: seata-commodity
  config:
    type: nacos
    nacos:
      namespace: ""
      server-addr: "nacos.blogyg.cn:9999"
      group: "DEFAULT_GROUP"
      username: "nacos"
      password: "nacos"
      cluster: DEFAULT
  registry:
    type: nacos
    nacos:
      application: "seata-server"
      server-addr: "nacos.blogyg.cn:9999"
      group: "DEFAULT_GROUP"
      namespace: ""
      username: "nacos"
      password: "nacos"
      cluster: DEFAULT
Copy the code
  • Data source agent

Shown here use mybatis plus way to seata agent, other orm framework in the same way, you just need to dataSourceProxy to DataSourceTransactionManager management

package com.young.seata.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import io.seata.rm.datasource.DataSourceProxy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;

/**
 * alibaba seata 配置类
 */
@Slf4j
@Configuration
public class SeataAutoConfig {

    @Value("${spring.application.name}")
    String applicationName;

    /** * autowired datasource config */
    @Autowired
    private DataSourceProperties dataSourceProperties;

    /**
     * init durid datasource
     *
     * @Return: druidDataSource  datasource instance
     */
    @Primary
    @Bean("druidDataSource")
    public DruidDataSource druidDataSource(a) {
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setUrl(dataSourceProperties.getUrl());
        druidDataSource.setUsername(dataSourceProperties.getUsername());
        druidDataSource.setPassword(dataSourceProperties.getPassword());
        druidDataSource.setDriverClassName(dataSourceProperties.getDriverClassName());
        druidDataSource.setInitialSize(0);
        druidDataSource.setMaxActive(180);
        druidDataSource.setMaxWait(60000);
        druidDataSource.setMinIdle(0);
        druidDataSource.setValidationQuery("Select 1 from DUAL");
        druidDataSource.setTestOnBorrow(false);
        druidDataSource.setTestOnReturn(false);
        druidDataSource.setTestWhileIdle(true);
        druidDataSource.setTimeBetweenEvictionRunsMillis(60000);
        druidDataSource.setMinEvictableIdleTimeMillis(25200000);
        druidDataSource.setRemoveAbandoned(true);
        druidDataSource.setRemoveAbandonedTimeout(1800);
        druidDataSource.setLogAbandoned(true);
        return druidDataSource;
    }

    /**
     * init datasource proxy
     *
     * @Param: druidDataSource  datasource bean instance
     * @Return: DataSourceProxy  datasource proxy
     */
    @Bean
    public DataSourceProxy dataSourceProxy(DruidDataSource druidDataSource) {
        return new DataSourceProxy(druidDataSource);
    }

    @Bean
    public DataSourceTransactionManager transactionManager(DataSourceProxy dataSourceProxy) {
        return new DataSourceTransactionManager(dataSourceProxy);
    }

    /** * Mybatis Plus requires this configuration, otherwise you cannot use Mybatis Plus **@param dataSource
     * @return* /
    @Bean
    @ConfigurationProperties(prefix = "mybatis")
    public MybatisSqlSessionFactoryBean sqlSessionFactoryBean(@Qualifier("druidDataSource") DataSource dataSource) {
        / / here substitute MybatisSqlSessionFactoryBean SqlSessionFactoryBean, MyBatisPlus will not take effect
        MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
        mybatisSqlSessionFactoryBean.setDataSource(dataSource);
        returnmybatisSqlSessionFactoryBean; }}Copy the code

reference

Seata website: https://seata.io/zh-cn/docs/overview/what-is-seata.html NacOS website: https://nacos.io/zh-cn/docs/quick-start-spring-cloud.html AoBing distributed transaction solution: https://zhuanlan.zhihu.com/p/183753774 transaction isolation level: The https://zhuanlan.zhihu.com/p/117476959 project source: https://github.com/yg-y/seata-exampleCopy the code