分布式事务之超详细的Seata实践记录(上)

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: 分布式事务之超详细的Seata实践记录(上)

前两周花了些时间在研究tcc分布式事务的一些相关基础上边,这周来写一篇关于seata的实践文章。


网上关于seata落地的demo其实也蛮多的,自己在结合案例和相关文章进行实际落地的过程中踩了不少坑,所以这篇文章主要记录关于落地案例中遇到的困难。


技术选型



SpringBoot + Dubbo + JdbcTemplate + MySQL + Seata + Nacos


使用场景


购买商品的时候,扣减库存并且同时插入一条订单数据。


ps:简单的模拟场景,没有做锁定库存相关的复杂操作,只是为了验证seata能够保证多库场景下的分布式事务能够生效


网络异常,图片无法展示
|


搭建seata单机部署



首先保证搭建好nacos环境和mysql环境,关于这部分的介绍本文不做过多讲解。

mysql建表


搭建seata使用的数据库:seata


然后在seata数据库中建立三张表:


CREATE TABLE `branch_table` (
  `branch_id` bigint(20) NOT NULL,
  `xid` varchar(128) COLLATE utf8_bin NOT NULL,
  `transaction_id` bigint(20) DEFAULT NULL,
  `resource_group_id` varchar(32) COLLATE utf8_bin DEFAULT NULL,
  `resource_id` varchar(256) COLLATE utf8_bin DEFAULT NULL,
  `lock_key` varchar(128) COLLATE utf8_bin DEFAULT NULL,
  `branch_type` varchar(8) COLLATE utf8_bin DEFAULT NULL,
  `status` tinyint(4) DEFAULT NULL,
  `client_id` varchar(64) COLLATE utf8_bin DEFAULT NULL,
  `application_data` varchar(2000) COLLATE utf8_bin DEFAULT NULL,
  `gmt_create` datetime DEFAULT NULL,
  `gmt_modified` datetime DEFAULT NULL,
  PRIMARY KEY (`branch_id`),
  KEY `idx_xid` (`xid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
复制代码


CREATE TABLE `global_table` (
  `xid` varchar(128) COLLATE utf8_bin NOT NULL,
  `transaction_id` bigint(20) DEFAULT NULL,
  `status` tinyint(4) NOT NULL,
  `application_id` varchar(32) COLLATE utf8_bin DEFAULT NULL,
  `transaction_service_group` varchar(32) COLLATE utf8_bin DEFAULT NULL,
  `transaction_name` varchar(128) COLLATE utf8_bin DEFAULT NULL,
  `timeout` int(11) DEFAULT NULL,
  `begin_time` bigint(20) DEFAULT NULL,
  `application_data` varchar(2000) COLLATE utf8_bin DEFAULT NULL,
  `gmt_create` datetime DEFAULT NULL,
  `gmt_modified` datetime DEFAULT NULL,
  PRIMARY KEY (`xid`),
  KEY `idx_gmt_modified_status` (`gmt_modified`,`status`),
  KEY `idx_transaction_id` (`transaction_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
复制代码


CREATE TABLE `lock_table` (
  `row_key` varchar(128) COLLATE utf8_bin NOT NULL,
  `xid` varchar(96) COLLATE utf8_bin DEFAULT NULL,
  `transaction_id` mediumtext COLLATE utf8_bin,
  `branch_id` mediumtext COLLATE utf8_bin,
  `resource_id` varchar(256) COLLATE utf8_bin DEFAULT NULL,
  `table_name` varchar(32) COLLATE utf8_bin DEFAULT NULL,
  `pk` varchar(36) COLLATE utf8_bin DEFAULT NULL,
  `gmt_create` datetime DEFAULT NULL,
  `gmt_modified` datetime DEFAULT NULL,
  PRIMARY KEY (`row_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
复制代码


网络异常,图片无法展示
|


然后是分别搭建订单数据库和商品数据库mall_ordermall_goods

mall_order数据库中创建一张简单的订单表


CREATE TABLE `t_order` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `order_no` varchar(60) COLLATE utf8_bin DEFAULT NULL,
  `user_id` int(11) DEFAULT NULL,
  `goods_id` int(11) DEFAULT NULL,
  `stock` int(6) DEFAULT NULL COMMENT '库存',
  `unit` varchar(20) COLLATE utf8_bin DEFAULT NULL COMMENT '单位',
  `status` tinyint(3) DEFAULT NULL COMMENT '订单状态 0 提交中,1提交成功,2提交失败',
  `valid_status` tinyint(3) DEFAULT NULL COMMENT '订单是否有效 0有效 2无效',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='订单表';
复制代码


mall_order库中建立一张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=9 DEFAULT CHARSET=utf8;
复制代码


mall_goods数据库中创建一张简单的商品表


CREATE TABLE `t_goods` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `goods_name` varchar(30) COLLATE utf8_bin DEFAULT NULL,
  `stock` int(6) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='商品信息表';
复制代码


mall_goods库中建立一张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=9 DEFAULT CHARSET=utf8;
复制代码


两张undo_log是为了seata专门使用的,和业务核心流程无太多关联,这里我们暂时不用管,下边我会介绍。


接下来是seata的安装

初次学习seata,选择的是搭建单机环境进行部署。


在官方地址下载seata:

github.com/seata/seata…


本人使用的是1.1.0版本安装包,安装环境是mac pro

github.com/seata/seata…


网络异常,图片无法展示
|


安装包结构介绍

下载之后解压会看到以下文件:


网络异常,图片无法展示
|


bin 目录底下是主要的启动文件

conf 底下是相关的配置文件

config.txt 是我后边加入的一份配置文件,这里读者门可以先忽略

lib 是存放一些依赖包的目录


配置文件调整


按照官网的指导,我进入到来conf目录底下然后打开相关的配置文件:

/conf/register.conf


registry {
  # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  # seata的注册中心这里选择使用nacos
  type = "nacos"
  nacos {
    #注册地址
    serverAddr = "localhost:8848"
    #默认写空
    namespace = ""
    #单机版搭建这里写default就可以了
    cluster = "default"
  }
  eureka {
    serviceUrl = "http://localhost:8761/eureka"
    application = "default"
    weight = "1"
  }
  redis {
    serverAddr = "localhost:6379"
    db = "0"
  }
  zk {
    cluster = "default"
    serverAddr = "127.0.0.1:2181"
    session.timeout = 6000
    connect.timeout = 2000
  }
  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
  type = "nacos"
  #同理,这里也这么配置即可
  nacos {
    serverAddr = "localhost:8848"
    namespace = ""
  }
  consul {
    serverAddr = "127.0.0.1:8500"
  }
  apollo {
    app.id = "seata-server"
    apollo.meta = "http://192.168.1.204:8801"
    namespace = "application"
  }
  zk {
    serverAddr = "127.0.0.1:2181"
    session.timeout = 6000
    connect.timeout = 2000
  }
  etcd3 {
    serverAddr = "http://localhost:2379"
  }
  file {
    name = "file.conf"
  }
}
复制代码


这份配置文件主要改动点为config模块选择使用nacos,register模块也选择使用nacos,并且均需要设置相关的ip地址等属性。


然后我们来看到一份config.txt文件,如果没有就新建该文件。


config.txt文件配置如下:


transport.type=TCP
transport.server=NIO
transport.heartbeat=true
transport.enableClientBatchSendRequest=false
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
service.vgroupMapping.my_tcc=default #默认格式为service.vgroupMapping加一个自定义的事务分组名称,这里我取名为my_tcc,然后赋值为default
service.default.grouplist=127.0.0.1:8091 #这里写nacos的配置地址
service.enableDegrade=false
service.disableGlobalTransaction=false
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=false
client.rm.sqlParserType=druid
client.rm.reportSuccessEnable=false
client.rm.sagaBranchRegisterEnable=false
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.support.spring.datasource.autoproxy=true 
store.mode=db #这里默认写的是file模式,现在需要改为db模式
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
store.db.datasource=dbcp #这里默认写的是使用druid数据连接池,这里我改为使用dbcp
store.db.dbType=mysql 
store.db.driverClassName=com.mysql.jdbc.Driver
store.db.url=jdbc:mysql://prod.min.mall.com:3306/seata?useUnicode=true
store.db.user=youruser #数据库账号名
store.db.password=yourpassword #数据库密码
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
store.redis.host=127.0.0.1
store.redis.port=6379
store.redis.maxConn=10
store.redis.minConn=1
store.redis.database=0
store.redis.password=null
store.redis.queryLimit=100
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
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.log.exceptionRate=100
transport.serialization=seata
transport.compressor=none
metrics.enabled=false
metrics.registryType=compact
metrics.exporterList=prometheus
metrics.exporterPrometheusPort=9898
复制代码


ps:配置文件最底部有个无用的空行,部分网友说不删除会有异常,建议删除。


将相关的seata的config.txt配置导入到nacos中

这部分操作在网上搜了很多资料,似乎都是使用以下脚本进行同步数据,这里我把脚本的内容贴出来给到各位查阅:


#!/usr/bin/env bash
# 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"
failCount=0
tempLog=$(mktemp -u)
function addConfig() {
  curl -X POST -H "${contentType}" "http://$nacosAddr/nacos/v1/cs/configs?dataId=$1&group=$group&content=$2&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++ ))
  fi
}
count=0
for line in $(cat $(dirname "$PWD")/config.txt | sed s/[[:space:]]//g); do
  (( count++ ))
  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
复制代码


注意这里需要将config.txt文件拷贝到和bin目录同级别的位置才可以:


网络异常,图片无法展示
|


这里也就应对了文章开头我提及的config.txt文件为何会出现的情况了。


执行脚本


sh nacos-config.sh localhost
复制代码


导入成功之后查看nacos界面


网络异常,图片无法展示
|


然后此时再启动seata-server

简单的启动脚本:


echo "======== prepare to start seata ======="
nohup sh ./seata-server.sh &
echo "======== now, it is starting ======="
复制代码


启动成功会看到日志中输出这么一段话:


2020-11-07 15:31:53.116 INFO [main]io.seata.core.rpc.netty.RpcServerBootstrap.start:155 -Server started ... 
2020-11-07 15:34:43.032 INFO [UndoLogDelete_1]io.seata.server.coordinator.DefaultCoordinator.undoLogDelete:359 -no active rm channels to delete undo log
复制代码


并且nacos中有相关的seata服务注册


网络异常,图片无法展示
|


接下来便进入了java程序编码接入的环节了。

项目完整代码案例:

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
2月前
|
NoSQL 关系型数据库 MySQL
分布式锁:不同实现方式实践测评
分布式锁:不同实现方式实践测评
28 0
|
1月前
|
SQL 关系型数据库 数据库
学习分布式事务Seata看这一篇就够了,建议收藏
学习分布式事务Seata看这一篇就够了,建议收藏
|
2月前
|
关系型数据库 MySQL 数据库
分布式事务Seata
分布式事务Seata
|
2月前
|
Java 数据库连接 API
分布式事物【XA强一致性分布式事务实战、Seata提供XA模式实现分布式事务】(五)-全面详解(学习总结---从入门到深化)
分布式事物【XA强一致性分布式事务实战、Seata提供XA模式实现分布式事务】(五)-全面详解(学习总结---从入门到深化)
54 0
|
2月前
|
存储 Oracle 关系型数据库
分布式事物【Seata实现、下载启动Seata服务、搭建聚合父工程构建】(四)-全面详解(学习总结---从入门到深化)
分布式事物【Seata实现、下载启动Seata服务、搭建聚合父工程构建】(四)-全面详解(学习总结---从入门到深化)
45 0
|
开发框架 Java 数据库连接
分布式事物【XA强一致性分布式事务实战、Seata提供XA模式实现分布式事务】(五)-全面详解(学习总结---从入门到深化)(下)
分布式事物【XA强一致性分布式事务实战、Seata提供XA模式实现分布式事务】(五)-全面详解(学习总结---从入门到深化)
37 0
|
数据库 微服务
分布式事物【XA强一致性分布式事务实战、Seata提供XA模式实现分布式事务】(五)-全面详解(学习总结---从入门到深化)(上)
分布式事物【XA强一致性分布式事务实战、Seata提供XA模式实现分布式事务】(五)-全面详解(学习总结---从入门到深化)
39 0
|
18天前
|
SQL 数据库 Windows
SpringCloud集成seata分布式事务控制
SpringCloud集成seata分布式事务控制
14 0
|
21天前
|
存储 Java 应用服务中间件
【分布式技术专题】「架构实践于案例分析」盘点互联网应用服务中常用分布式事务(刚性事务和柔性事务)的原理和方案
【分布式技术专题】「架构实践于案例分析」盘点互联网应用服务中常用分布式事务(刚性事务和柔性事务)的原理和方案
42 0
|
1月前
|
SQL SpringCloudAlibaba 中间件
SpringCloud Alibaba Seata处理分布式事务--学习笔记
SpringCloud Alibaba Seata处理分布式事务--学习笔记
24 0

热门文章

最新文章