微服务seata解析部署使用全流程

devtools/2024/10/9 1:14:14/

官网地址: Seata 是什么? | Apache Seata

1、Seata术语

用来管理分布式事务,由阿里巴巴出品。

【1、TC (Transaction Coordinator) - 事务协调者】

用来维护事务的,包括主事务和分支事务。

【2、TM (Transaction Manager) - 事务管理器】

管理事务的,决定了事务什么时候开启,什么时候结束,提交还是回滚。

【3、RM (Resource Manager) - 资源管理器】

监视分支事务的状态,和TC做数据交互,把分支事务状态告诉TC。

2、SEATA 的分布式事务解决方案

seata提供了4种方式,分别是AT、TCC、SGAG、XA
        ①AT----该模式是Seata中最常用的模式,使用于大多数业务场景,特别是对性能要求较高时,(通过记录数据的快照,来支持事务的回滚)
            原理:事务执行中Seata会保存数据执行前的快照,提交时,如果发生异常时就会回滚
            优点:实现简单,性能高,适用于读多写少的场景
            缺点:对数据库要求高
        ②TCC---适用于业务逻辑复杂,需要严格控制使用场景
            三个阶段:try(尝试)、confirm(确认)、Cancke(取消)
            原理:try执行预留操作,锁定必要资源、confirm如果操作都成功,确认操作、Cancle如果某操作失败,则取消所有操作
            优点:控制力强,适合需要一致性的复杂业务场景
            缺点:实现复杂
        ③Saga---长事务处理方式
            使用:适用于微服务架构中,将大事务分解成小事务,实现事务的一致性和可靠性,具有继承性,可靠性和强一致性
            优点:性能高,无锁,参与者可异步,高吞吐,实现简单
            缺点:没有锁,会哟脏写,时效性差
        ④AX----使用两个阶段提交,保证事务的一致性和可靠性
            原理:所有参与的资源管理器再提交前准备好,记录事务状态
            优点:一致性、可靠性、标准化
            缺点:会引入额外的性能开销,导致延迟增加、有阻塞风险,影响系统的吞吐量和并发性、易发生单点故障
 

3、安装seata 【13分钟】

1、基于操作系统环境安装

Releases · apache/incubator-seata · GitHub

2、拉取docker镜像

docker pull seataio/seata-server:1.3.0

3、拷贝配置文件

先启动seata,将seata-server目录下的文件拷贝到虚拟机中

#执行命令1:
docker run -d --name seata -p 8091:8091 seataio/seata-server:1.3.0

虚拟机里,创建/usr/local/docker/seata目录。

然后执行拷贝任务,拷贝seata:/seata-server 里的资源到/usr/local/docker/seata目录下。

#执行命令2:
docker cp seata:/seata-server /usr/local/docker/seata

【file.conf文件】

file.conf 文件,用来告诉seata 数据存放再哪里的。

修改store.mode为db,修改db相关配置,保存该文件在宿主机本地

## transaction log store, only used in seata-server
store {
  ## store mode: file、db、redis
  mode = "db"

  ## file store property
  file {
    ## store location dir
    dir = "sessionStore"
    # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
    maxBranchSessionSize = 16384
    # globe session size , if exceeded throws exceptions
    maxGlobalSessionSize = 512
    # file buffer size , if exceeded allocate new buffer
    fileWriteBufferCacheSize = 16384
    # when recover batch read size
    sessionReloadReadSize = 100
    # async, sync
    flushDiskMode = async
  }

  ## database store property
  db {
    ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.
    datasource = "druid"
    ## mysql/oracle/postgresql/h2/oceanbase etc.
    dbType = "mysql"
    driverClassName = "com.mysql.cj.jdbc.Driver"
    url = "jdbc:mysql://192.168.43.8:3306/seata"
    user = "root"
    password = "123456"
    minConn = 5
    maxConn = 30
    globalTable = "global_table"
    branchTable = "branch_table"
    lockTable = "lock_table"
    queryLimit = 100
    maxWait = 5000
  }

  ## redis store property
  redis {
    host = "127.0.0.1"
    port = "6379"
    password = ""
    database = "0"
    minConn = 1
    maxConn = 10
    queryLimit = 100
  }

}

【registry.conf】

registry.conf 告诉seata,我们的注册中心和配置中心用的是什么,保存该文件在宿主机本地。

修改类型为nacos,修改nacos相关配置,包括registry和config的两组。在nacos中为seata创建一个属于自己的命名空间,它的配置比较多,避免和其他配置公用。

[Linux启动nacos,配置新的命名空间]

registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  type = "nacos"

  nacos {
    application = "seata-server"
    serverAddr = "192.168.43.8:8848"
    group = "SEATA_GROUP"
    namespace = "6bb9bfd3-2af4-4fd8-8f75-c91482ba43f8"
    cluster = "default"
    username = "nacos"
    password = "nacos"
  }
  eureka {
    serviceUrl = "http://localhost:8761/eureka"
    application = "default"
    weight = "1"
  }
  redis {
    serverAddr = "localhost:6379"
    db = 0
    password = ""
    cluster = "default"
    timeout = 0
  }
  zk {
    cluster = "default"
    serverAddr = "127.0.0.1:2181"
    sessionTimeout = 6000
    connectTimeout = 2000
    username = ""
    password = ""
  }
  consul {
    cluster = "default"
    serverAddr = "127.0.0.1:8500"
  }
  etcd3 {
    cluster = "default"
    serverAddr = "http://localhost:2379"
  }
  sofa {
    serverAddr = "127.0.0.1:9603"
    application = "default"
    region = "DEFAULT_ZONE"
    datacenter = "DefaultDataCenter"
    cluster = "default"
    group = "SEATA_GROUP"
    addressWaitTime = "3000"
  }
  file {
    name = "file.conf"
  }
}

config {
  # file、nacos 、apollo、zk、consul、etcd3
  type = "nacos"

  nacos {
    serverAddr = "192.168.43.8:8848"
    group = "SEATA_GROUP"
    namespace = "6bb9bfd3-2af4-4fd8-8f75-c91482ba43f8"
    username = "nacos"
    password = "nacos"
  }
  consul {
    serverAddr = "127.0.0.1:8500"
  }
  apollo {
    appId = "seata-server"
    apolloMeta = "http://192.168.1.204:8801"
    namespace = "application"
  }
  zk {
    serverAddr = "127.0.0.1:2181"
    sessionTimeout = 6000
    connectTimeout = 2000
    username = ""
    password = ""
  }
  etcd3 {
    serverAddr = "http://localhost:2379"
  }
  file {
    name = "file.conf"
  }
}
 

4、创建seata在nacos配置中心的配置文件

官方文件模板所在路径: https://github.com/seata/seata/blob/develop/script/config-center/config.txt

创建一个config.txt文件,将以下内容改成自己的信息,粘贴进去保存。

#For details about configuration items, see https://seata.io/zh-cn/docs/user/configurations.html
#Transport configuration, for client and server
transport.type=TCP
transport.server=NIO
transport.heartbeat=true
transport.enableTmClientBatchSendRequest=false
transport.enableRmClientBatchSendRequest=true
transport.enableTcServerBatchSendResponse=false
transport.rpcRmRequestTimeout=30000
transport.rpcTmRequestTimeout=30000
transport.rpcTcRequestTimeout=30000
transport.threadFactory.bossThreadPrefix=NettyBoss
transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker
transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler
transport.threadFactory.shareBossWorker=false
transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector
transport.threadFactory.clientSelectorThreadSize=1
transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread
transport.threadFactory.bossThreadSize=1
transport.threadFactory.workerThreadSize=default
transport.shutdown.wait=3
transport.serialization=seata
transport.compressor=none

#Transaction routing rules configuration, only for the client
# ---------------------------------------------------
service.vgroupMapping.my_tx_group=default
# ---------------------------------------------------
#If you use a registry, you can ignore it
service.default.grouplist=127.0.0.1:8091
service.enableDegrade=false
service.disableGlobalTransaction=false

#Transaction rule configuration, only for the client
client.rm.asyncCommitBufferLimit=10000
client.rm.lock.retryInterval=10
client.rm.lock.retryTimes=30
client.rm.lock.retryPolicyBranchRollbackOnConflict=true
client.rm.reportRetryCount=5
client.rm.tableMetaCheckEnable=true
client.rm.tableMetaCheckerInterval=60000
client.rm.sqlParserType=druid
client.rm.reportSuccessEnable=false
client.rm.sagaBranchRegisterEnable=false
client.rm.sagaJsonParser=fastjson
client.rm.tccActionInterceptorOrder=-2147482648
client.tm.commitRetryCount=5
client.tm.rollbackRetryCount=5
client.tm.defaultGlobalTransactionTimeout=60000
client.tm.degradeCheck=false
client.tm.degradeCheckAllowTimes=10
client.tm.degradeCheckPeriod=2000
client.tm.interceptorOrder=-2147482648
client.undo.dataValidation=true
client.undo.logSerialization=jackson
client.undo.onlyCareUpdateColumns=true
server.undo.logSaveDays=7
server.undo.logDeletePeriod=86400000
client.undo.logTable=undo_log
client.undo.compress.enable=true
client.undo.compress.type=zip
client.undo.compress.threshold=64k
#For TCC transaction mode
tcc.fence.logTableName=tcc_fence_log
tcc.fence.cleanPeriod=1h

#Log rule configuration, for client and server
log.exceptionRate=100

#Transaction storage configuration, only for the server. The file, db, and redis configuration values are optional.
# ---------------------------------------------------
store.mode=db
store.lock.mode=db
store.session.mode=db
# ---------------------------------------------------
#Used for password encryption
store.publicKey=

#If `store.mode,store.lock.mode,store.session.mode` are not equal to `file`, you can remove the configuration block.
store.file.dir=file_store/data
store.file.maxBranchSessionSize=16384
store.file.maxGlobalSessionSize=512
store.file.fileWriteBufferCacheSize=16384
store.file.flushDiskMode=async
store.file.sessionReloadReadSize=100

#These configurations are required if the `store mode` is `db`. If `store.mode,store.lock.mode,store.session.mode` are not equal to `db`, you can remove the configuration block.
store.db.datasource=druid
store.db.dbType=mysql
# ---------------------------------------------------
store.db.driverClassName=com.mysql.cj.jdbc.Driver
store.db.url=jdbc:mysql://192.168.43.8: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.distributedLockTable=distributed_lock
store.db.queryLimit=100
store.db.lockTable=lock_table
store.db.maxWait=5000

#These configurations are required if the `store mode` is `redis`. If `store.mode,store.lock.mode,store.session.mode` are not equal to `redis`, you can remove the configuration block.
store.redis.mode=single
store.redis.single.host=127.0.0.1
store.redis.single.port=6379
store.redis.sentinel.masterName=
store.redis.sentinel.sentinelHosts=
store.redis.maxConn=10
store.redis.minConn=1
store.redis.maxTotal=100
store.redis.database=0
store.redis.password=
store.redis.queryLimit=100

#Transaction rule configuration, only for the server
server.recovery.committingRetryPeriod=1000
server.recovery.asynCommittingRetryPeriod=1000
server.recovery.rollbackingRetryPeriod=1000
server.recovery.timeoutRetryPeriod=1000
server.maxCommitRetryTimeout=-1
server.maxRollbackRetryTimeout=-1
server.rollbackRetryTimeoutUnlockEnable=false
server.distributedLockExpireTime=10000
server.xaerNotaRetryTimeout=60000
server.session.branchAsyncQueueSize=5000
server.session.enableBranchAsyncRemove=false
server.enableParallelRequestHandle=false

#Metrics configuration, only for the server
metrics.enabled=false
metrics.registryType=compact
metrics.exporterList=prometheus
metrics.exporterPrometheusPort=9898

修改的内容

service.vgroupMapping.my_tx_group=default
store.mode=db
store.lock.mode=db
store.session.mode=db
store.db.driverClassName=com.mysql.cj.jdbc.Driver
store.db.url=jdbc:mysql://192.168.43.8:3306/seata?useUnicode=true&rewriteBatchedStatements=true
store.db.user=root
store.db.password=123456

5、创建导入config.txt到nacos的脚本

官方文件模板所在路径: https://github.com/seata/seata/blob/develop/script/config-center/nacos/nacos-config.sh

创建 nacos-config.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=localhost
fi
if [ -z ${port} ]; then
    port=8848
fi
if [ -z ${group} ]; then
    group="SEATA_GROUP"
fi
if [ -z ${tenant} ]; then
    tenant=""
fi
if [ -z ${username} ]; then
    username=""
fi
if [ -z ${password} ]; then
    password=""
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
COMMENT_START="#"
for line in $(cat $(dirname "$PWD")/config.txt | sed s/[[:space:]]//g); do
    if [[ "$line" =~ ^"${COMMENT_START}".*  ]]; then
      continue
    fi
    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. "
fi

6、导入nacos配置

#循环 不需要确认 删除 某一目录以及该目录下的文件和子目录。
rm -rf seata-server

将 config.txt文件添加到seata文件夹里,同时创建子目录nacos;将nacos-config.sh文件放到nacos目录里。

导入前要启动nacos并关闭防火墙,执行如下命令:为该文件分配一个执行的权限。

#命令1:为该文件分配一个执行的权限。
chmod +x nacos-config.sh

修改下面命令里的内容,包括ip和Id。

#命令2:
sh nacos-config.sh -h 192.168.43.8 -p 8848 -g SEATA_GROUP -t 6bb9bfd3-2af4-4fd8-8f75-c91482ba43f8 -u nacos -w nacos

报错说含/r的错误,是因为windows下文件的换行符合Linux不同导致的,执行以下命令:

#命令3:
sed -i 's/\r//' nacos-config.sh
#然后再重新执行一遍命令2:
sh nacos-config.sh -h 192.168.43.8 -p 8848 -g SEATA_GROUP -t 6bb9bfd3-2af4-4fd8-8f75-c91482ba43f8 -u nacos -w nacos

7、复制配置文件

seata目录下原资源可以全部删掉了。

将file.conf和registry.conf文件复制到/usr/local/docker/seata/config下,为再次创建seata容器做配置准备。

为config目录授权:

chmod 777 config

8、关闭并删除之前的seata容器

停掉刚才启动的seata,而且它以后也用不到了,可以删除。

9、数据库配置

启动seata需要有以下几张表的基础支持,所以在启动前,先创建如下数据库表。启动mysql数据库。

seata需要创建三张表:

全局事务---global_table

分支事务---branch_table

全局锁-----lock_table

建表语句

DROP TABLE
IF
    EXISTS `global_table`;
CREATE TABLE `global_table` (
    `xid` VARCHAR ( 128 ) NOT NULL,
    `transaction_id` BIGINT,
    `status` TINYINT NOT NULL,
    `application_id` VARCHAR ( 32 ),
    `transaction_service_group` VARCHAR ( 32 ),
    `transaction_name` VARCHAR ( 64 ),
    `timeout` INT,
    `begin_time` BIGINT,
    `application_data` VARCHAR ( 2000 ),
    `gmt_create` datetime,
    `gmt_modified` datetime,
    PRIMARY KEY ( `xid` ),
    KEY `idx_gmt_modified_status` ( `gmt_modified`, `status` ),
    KEY `idx_transaction_id` ( `transaction_id` ) 
);
DROP TABLE
IF
    EXISTS `branch_table`;
CREATE TABLE `branch_table` (
    `branch_id` BIGINT NOT NULL,
    `xid` VARCHAR ( 128 ) NOT NULL,
    `transaction_id` BIGINT,
    `resource_group_id` VARCHAR ( 32 ),
    `resource_id` VARCHAR ( 256 ),
    `lock_key` VARCHAR ( 128 ),
    `branch_type` VARCHAR ( 8 ),
    `status` TINYINT,
    `client_id` VARCHAR ( 64 ),
    `application_data` VARCHAR ( 2000 ),
    `gmt_create` datetime,
    `gmt_modified` datetime,
    PRIMARY KEY ( `branch_id` ),
    KEY `idx_xid` ( `xid` ) 
);
DROP TABLE
IF
    EXISTS `lock_table`;
CREATE TABLE `lock_table` (
    `row_key` VARCHAR ( 128 ) NOT NULL,
    `xid` VARCHAR ( 96 ),
    `transaction_id` LONG,
    `branch_id` LONG,
    `resource_id` VARCHAR ( 256 ),
    `table_name` VARCHAR ( 100 ),
    `pk` VARCHAR ( 36 ),
    `gmt_create` datetime,
    `gmt_modified` datetime,
    PRIMARY KEY ( `row_key` ) 
);

10、启动seata

在执行启动命令前要先配置seata数据库

docker run --name seata \
-p 8091:8091 \
-e SEATA_IP=192.168.43.8 \
-e SEATA_PORT=8091 \
-e SEATA_CONFIG_NAME=file:/root/seata-config/registry \
-v /usr/local/docker/seata/config:/root/seata-config  \
-d seataio/seata-server:1.3.0

查看日志信息,能看到端口号8091 就是启动成功了。

#查看上面的启动命令日志:
docker logs -f seata

4、应用seata

1.准备项目:

创建两个seata工程,分别是seata1 和 seata2,还是以openfeign工程做为例子,实现整合mybatis连接数据库两张表的添加功能。

2.准备数据库表:

#建表代码
CREATE TABLE `score`  (
  `id` varchar(50)  NOT NULL,
  `name` varchar(50)  NULL,
  `score` double(7, 2) NULL ,
  PRIMARY KEY (`id`) USING BTREE

CREATE TABLE `user`  (
  `id` varchar(50)  NOT NULL,
  `name` varchar(50)  NULL,
  `password` varchar(50)  NULL,
  PRIMARY KEY (`id`) USING BTREE

##undo_log表的创建语句,在下面的4.2里。

3.准备阶段,运行项目

【正常运行】

没有异常,成功运行,两张表里都有数据。

【添加异常】

添加异常,失败运行,两张表都添加数据成功了居然

【添加Spring事务】

两个工程添加事务注解,成绩添加数据成功,用户添加数据失败

===以上是分布式的情况下,事务没有统一管理。

4.使用seata

以下操作,每个工程项目里都操作一遍。

1、添加依赖

   <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
        </dependency>

2、各自数据库中创建UNDO_LOG表

给每个微服务的数据库都创建这张表才能应用seata

要求:具有InnoDB引擎的MySQL。

-- 注意此处0.3.0+ 增加唯一索引 ux_undo_log

CREATE TABLE `undo_log` (
    `id` BIGINT ( 20 ) NOT NULL AUTO_INCREMENT,
    `branch_id` BIGINT ( 20 ) NOT NULL,
    `xid` VARCHAR ( 100 ) NOT NULL,
    `context` VARCHAR ( 128 ) NOT NULL,
    `rollback_info` LONGBLOB NOT NULL,
    `log_status` INT ( 11 ) NOT NULL,
    `log_created` datetime NOT NULL,
    `log_modified` datetime NOT NULL,
    `ext` VARCHAR ( 100 ) DEFAULT NULL,
    PRIMARY KEY ( `id` ),
UNIQUE KEY `ux_undo_log` ( `xid`, `branch_id` ) 
) ENGINE = INNODB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8;

3、修改启动类

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

4、添加配置文件

在配置文件application.yml中,添加如下配置信息:

seata:
  application-id: ${spring.application.name}
  enabled: true
  tx-service-group: my_tx_group
  registry:
    type: nacos
    nacos:
      server-addr: 172.19.186.247:8848
      namespace: c05aaf00-2701-4d2e-8710-318752481731
      group: SEATA_GROUP
      username: nacos
      password: nacos
  config:
    type: nacos
    nacos:
      server-addr: 172.19.186.247:8848
      namespace: c05aaf00-2701-4d2e-8710-318752481731
      group: SEATA_GROUP
      username: nacos
      password: nacos
  service:
    vgroup-mapping:
      my_tx_group: default
      
feign:
  httpclient:
    connection-timeout: 600000
    connection-timer-repeat: 30000    

tx-service-group: my_tx_group

service: vgroup-mapping: my_tx_group: default

与nacos的配置要一致,nacos的配置是tx-service-group.my_tx_group=default

5、添加数据源配置

com.jr.config包下,创建下面的配置文件类。

import com.alibaba.druid.pool.DruidDataSource;
import io.seata.rm.datasource.DataSourceProxy;
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;


@Configuration
public class DataSourceProxyConfig {

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DruidDataSource druidDataSource() {
        return new DruidDataSource();
    }

    @Bean
    @Primary
    public DataSourceProxy dataSourceProxy(DruidDataSource druidDataSource) {
        return new DataSourceProxy(druidDataSource);
    }

}

6、添加事务注解

更改之前ServletImpl类方法里的事务注解@Transactional(rollbackFor = Exception.class),改为下面注解:

@GlobalTransactional(rollbackFor = Exception.class,timeoutMills = 300000)

7、完成上述配置,运行结果

【有bug代码时】

两张表里都没有添加进数据,事务回滚。

【没有bug代码时】

数据添加成功

8、解决报错


http://www.ppmy.cn/devtools/122421.html

相关文章

【MySQL】查询原理 —— B+树查询数据全过程

使用B树作为索引结构的原因&#xff1a; 一种自平衡树&#xff1a; B树在插入和删除的时候节点会进行分裂和合并操作&#xff0c;以保持树的平衡&#xff0c;存在冗余节点&#xff0c;使得删除的时候树结构变化小&#xff0c;更高效。 高度不会增长过快&#xff0c;查询磁盘I…

项目-坦克大战学习-游戏结束

当boos受到伤害时游戏结束&#xff0c;游戏结束时我们需要将窗体全部绘制从别的画面&#xff0c;这样我们可以在游戏运行类中的update设置条件&#xff0c;在游戏运行类thread创建一个枚举类型定义是否游戏结束 public enum Game { play, over };//定义现在游戏运行状态 如果…

贪心算法.

序幕 贪心算法&#xff08;Greedy Algorithm&#xff09;是一种在求解问题时采取逐步构建解决方案的策略&#xff0c;每一步都选择当前状态下局部最优的解&#xff0c;期望通过局部最优解能够得到全局最优解。 以上为了严谨性&#xff0c;引用了官方用语。 而用大白话总结就是&…

《RabbitMQ篇》Centos7安装RabbitMQ

安装RabbitMQ 安装包网盘下载地址 链接&#xff1a;https://pan.baidu.com/s/1bG_nP0iCdAejkctFp1QztQ?pwd4mlw 先上传安装包到服务器&#xff08;erlang-23.3.4.11-1.el7.x86_64.rpm和rabbitmq-server-3.9.16-1.el7.noarch.rpm&#xff09;然后使用指令安装 # 安装 erlang r…

uniapp中实现评分组件,多用于购买商品后,对商品进行评价等场景

前言 uni-rate是uniapp框架中提供的一个评分组件。它可以用于用户评价、打分等场景。uni-rate组件可以根据设定的星星总数&#xff0c;展示用户评分的效果&#xff0c;用户可以通过点击星星或滑动星星的方式进行评分。同时&#xff0c;uni-rate组件也支持自定义星星图标、星星…

数据结构之AVL树(万字详解)

目录 一、什么是AVL树 1.1 AVL树介绍 1.2 AVL树的结构 二、AVL树的插入 2.1 插入过程 2.2 更新平衡因子 三、AVL树的旋转 3.1 单旋思路 3.2 单旋代码 3.3 为什么要双旋 3.4 双旋思路 3.5 双旋代码 四、C源代码 一、什么是AVL树 1.1 AVL树介绍 AVL树是一种自平衡…

动态内存管理笔试题

目录 1.第一题1.1如何修改 2.第二题2.1题想2.2深刻理解 3.第三题4.第四题 1.第一题 void GetMemory(char* p) {p (char*)malloc(100); } void Test(void) {char* str NULL;GetMemory(str);strcpy(str, "hello world");printf(str); }请问运⾏Test 函数会有什么样的…

算法题总结(七)——栈与队列

1、栈常用操作 &#xff08;1&#xff09;栈定义 Stack<Integer> stack new Stack<Integer>();&#xff08;2&#xff09;栈操作 .栈是否为空 isEmpty(); .查询栈顶元素&#xff0c;不改变栈 peek(); .弹出栈顶元素&#xff0c;改变栈 pop(); .压入栈顶 push(); …