- 921. Minimum Add to Make Parentheses Valid 使括号有效的最少添加
- 915. Partition Array into Disjoint Intervals 分割数组
- 932. Beautiful Array 漂亮数组
- 940. Distinct Subsequences II 不同的子序列 II
Sharing-JDBC结合Seata的AT模式实现分布式事务,实现机制如下:
AT模式是Seata的默认模式,满足两阶段提交协议:
一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
二阶段:
提交异步化,非常快速地完成。
回滚通过一阶段的回滚日志进行反向补偿。
一阶段:
1、 解析SQL,获取执行的SQL语句的信息;
2、 根据解析得到的条件信息,执行查询语句,获取数据,生成前镜像;
3、 执行SQL语句,并且根据前镜像中的主键查询数据,生成后镜像;
4、 把前后镜像数据以及业务SQL相关的信息组成一条回滚日志记录,插入到UNDO_LOG表中;
5、 在提交事务前,向TC注册分支事务,并且根据主键值获取全局锁;
6、 提交本地事务,包含业务SQL、UNDO_LOG日志生成SQL;
7、 将本地事务提交的结果上报给TC;
说明:前后镜像实质是一个json串,记录了sql语句中的字段信息,比如:
"beforeImage": {
"rows": [{
"fields": [{
"name": "id",
"type": 4,
"value": 1
}]
}],
"tableName": "product"
}
二阶-事务提交
1、 分支收到TC的事务提交请求,上报提交成功,并且异步的删除全局锁和UNDOLOG记录;
二阶-事务回滚
1、 通过XID和BranchID查找到相应的UNDOLOG记录;
2、 将UNDOLOG中的后镜与当前数据进行比较,验证数据是否有被第三方篡改;
3、 根据UNDOLOG中的前镜像和业务SQL的相关信息生成并执行回滚语句;
4、 提交本地事务,将分支事务回滚的结果上报给TC;
优点:
一阶段完成直接提交事务,释放数据库资源,性能比较好。
利用全局锁实现读写隔离。
没有代码侵入,框架自动完成回滚和提交。
缺点:
两阶段之间属于软状态,无法保证数据强一致性,只能是数据最终一致性。
需要额外维护undo_log
表。
创建两个SpringBoot工程,分别为storage-service
与order-service
,模拟从在order-service
服务中新增订单,然后调用storage-service
服务新增库存扣减记录;分别创建两个数据库,不同服务连接不同的数据库,并且实现分表的配置;
-- 数据库名称: sharding-tx-order.sql
-- 订单表
CREATE TABLE tb_order_1
(
id int(11) NOT NULL COMMENT '主键',
count int(11) NULL DEFAULT 0 COMMENT '下单数量',
money int(11) NULL DEFAULT 0 COMMENT '金额',
PRIMARY KEY (id) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = COMPACT;
CREATE TABLE tb_order_2
(
id int(11) NOT NULL COMMENT '主键',
count int(11) NULL DEFAULT 0 COMMENT '下单数量',
money int(11) NULL DEFAULT 0 COMMENT '金额',
PRIMARY KEY (id) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = COMPACT;
CREATE TABLE tb_order_3
(
id int(11) NOT NULL COMMENT '主键',
count int(11) NULL DEFAULT 0 COMMENT '下单数量',
money int(11) NULL DEFAULT 0 COMMENT '金额',
PRIMARY KEY (id) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = COMPACT;
-- undo_log表
CREATE TABLE undo_log
(
branch_id bigint(20) NOT NULL COMMENT 'branch transaction id',
xid varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'global transaction id',
context varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'undo_log context,such as serialization',
rollback_info longblob NOT NULL COMMENT 'rollback info',
log_status int(11) NOT NULL COMMENT '0:normal status,1:defense status',
log_created datetime(6) NOT NULL COMMENT 'create datetime',
log_modified datetime(6) NOT NULL COMMENT 'modify datetime',
UNIQUE INDEX ux_undo_log(xid, branch_id) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = 'AT transaction mode undo table' ROW_FORMAT = Compact;
-- 数据库名称: sharding-tx-storage.sql
-- 库存表
CREATE TABLE tb_storage_1
(
id int(11) NOT NULL COMMENT '主键',
order_id int(11) NOT NULL COMMENT '订单ID',
count int(11) NOT NULL DEFAULT 0 COMMENT '库存',
PRIMARY KEY (id) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = COMPACT;
-- 库存表
CREATE TABLE tb_storage_2
(
id int(11) NOT NULL COMMENT '主键',
order_id int(11) NOT NULL COMMENT '订单ID',
count int(11) NOT NULL DEFAULT 0 COMMENT '库存',
PRIMARY KEY (id) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = COMPACT;
-- 库存表
CREATE TABLE tb_storage_3
(
id int(11) NOT NULL COMMENT '主键',
order_id int(11) NOT NULL COMMENT '订单ID',
count int(11) NOT NULL DEFAULT 0 COMMENT '库存',
PRIMARY KEY (id) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = COMPACT;
-- undo_log表
CREATE TABLE undo_log
(
branch_id bigint(20) NOT NULL COMMENT 'branch transaction id',
xid varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'global transaction id',
context varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'undo_log context,such as serialization',
rollback_info longblob NOT NULL COMMENT 'rollback info',
log_status int(11) NOT NULL COMMENT '0:normal status,1:defense status',
log_created datetime(6) NOT NULL COMMENT 'create datetime',
log_modified datetime(6) NOT NULL COMMENT 'modify datetime',
UNIQUE INDEX ux_undo_log(xid, branch_id) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = 'AT transaction mode undo table' ROW_FORMAT = Compact;
server:
port: 8082
spring:
application:
name: order-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
namespace: 64ed9ca7-d705-4655-b4e4-f824e420a12a
group: test
sharding-jdbc 水平分表规则配置
数据源名称,多数据源逗号隔开
shardingsphere:
datasource:
names: m1
m1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:3307/sharding-tx-order?useUnicode=true&useSSL=false&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
username: root
password: lhzlx
水平分表:tb_order_1/2/3,多个表进行分表时,依次在tables标签后写逻辑
tb_order_1/2/3 为数据库中的事实表
tb_order为编写SQL中操作的逻辑表,sharding-jdbc会自动根据策略操作事实表
配置节点分布情况
sharding:
tables:
tb_order:
actual-data-nodes: m1.tb_order_$->{
1..3}
指定tb_order表的主键生成策略为SNOWFLAKE
key-generator.column: id
key-generator.type: SNOWFLAKE
指定tb_order表的分片策略,分片策略包括分片键和分片算法, tb_order_1/2/3 所有对3取余
table-strategy.inline.sharding-column: id
table-strategy.inline.algorithm-expression: tb_order_$->{
id % 3+1}
打开sql输出日志
props:
sql:
show: true
seata:
enabled: true
application-id: ${
spring.application.name}
事务组的名称,对应service.vgroupMapping.default_tx_group=xxx中配置的default_tx_group
tx-service-group: default_tx_group
配置事务组与集群的对应关系
service:
vgroup-mapping:
default_tx_group为事务组的名称,default为集群名称
default_tx_group: default
disable-global-transaction: false
registry:
type: nacos
nacos:
application: seata-server
server-addr: 162.14.115.18:8848
group: SEATA_GROUP
namespace: 64ed9ca7-d705-4655-b4e4-f824e420a12a
username: nacos
password: nacos
cluster: default
config:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
group: SEATA_GROUP
namespace: 64ed9ca7-d705-4655-b4e4-f824e420a12a
username: nacos
password: nacos
data-id: seataServer.properties
上游服务通过@GlobalTransactional
注解开启全局事务,使用storageClient
进行feign调用
@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
@Resource
private StorageClient storageClient;
@Resource
private OrderMapper orderMapper;
/**
* 创建订单
*
* @param order
* @return
*/
@Override
@GlobalTransactional
public Long create(Order order) {
// 创建订单
long id = new Random().nextInt(999999999);
order.setId(id);
orderMapper.insert(order);
try {
// 记录库存信息
storageClient.deduct(order.getId(), order.getCount());
// 模拟异常
// int a = 1 / 0;
} catch (FeignException e) {
log.error("下单失败,原因:{}", e.contentUTF8(), e);
throw new RuntimeException(e.contentUTF8(), e);
}
return order.getId();
}
}
@FeignClient("storage-service")
public interface StorageClient {
/**
* 扣减库存
*
* @param orderId
* @param count
*/
@PostMapping("/storage")
void deduct(@RequestParam("orderId") Long orderId, @RequestParam("count") Integer count);
}
server:
port: 8081
spring:
application:
name: storage-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
namespace: 64ed9ca7-d705-4655-b4e4-f824e420a12a
group: test
在dev环境进行debug时,可以将时间设置长一些
heart-beat-interval: 1000心跳间隔。单位为毫秒,默认5*1000
heart-beat-timeout: 300000心跳暂停,收不到心跳,会将实例设为不健康。单位为毫秒,默认15*1000
ip-delete-timeout: 4000000Ip删除超时,收不到心跳,会将实例删除。单位为毫秒,默认30*1000
sharding-jdbc 水平分表规则配置
数据源名称,多数据源逗号隔开
shardingsphere:
datasource:
names: m1
m1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:3307/sharding-tx-storage?useUnicode=true&useSSL=false&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
username: root
password: lhzlx
水平分表:tb_storage_1/2/3,多个表进行分表时,依次在tables标签后写逻辑
tb_storage_1/2/3 为数据库中的事实表
tb_storage为编写SQL中操作的逻辑表,sharding-jdbc会自动根据策略操作事实表
配置节点分布情况
sharding:
tables:
tb_storage:
actual-data-nodes: m1.tb_storage_$->{
1..3}
指定tb_storage表的主键生成策略为SNOWFLAKE
key-generator.column: id
key-generator.type: SNOWFLAKE
指定tb_storage表的分片策略,分片策略包括分片键和分片算法, tb_storage_1/2/3 所有对3取余
table-strategy.inline.sharding-column: id
table-strategy.inline.algorithm-expression: tb_storage_$->{
id % 3+1}
打开sql输出日志
props:
sql:
show: true
seata:
enabled: true
application-id: ${
spring.application.name}
事务组的名称,对应service.vgroupMapping.default_tx_group=xxx中配置的default_tx_group
tx-service-group: default_tx_group
配置事务组与集群的对应关系
service:
vgroup-mapping:
default_tx_group为事务组的名称,default为集群名称
default_tx_group: default
disable-global-transaction: false
registry:
type: nacos
nacos:
application: seata-server
server-addr: 127.0.0.1:8848
group: SEATA_GROUP
namespace: 64ed9ca7-d705-4655-b4e4-f824e420a12a
username: nacos
password: nacos
cluster: default
config:
type: nacos
nacos:
server-addr: 162.14.115.18:8848
group: SEATA_GROUP
namespace: 64ed9ca7-d705-4655-b4e4-f824e420a12a
username: nacos
password: nacos
data-id: seataServer.properties
@Slf4j
@Service
public class StorageServiceImpl implements StorageService {
@Resource
private StorageMapper storageMapper;
/**
* 扣除存储数量
*
* @param orderId
* @param count
*/
@Override
public void deduct(Long orderId, int count) {
log.info("开始记录库存信息");
try {
long id = new Random().nextInt(999999999);
Storage storage = new Storage();
storage.setId(id);
storage.setOrderId(orderId);
storage.setCount(count);
storageMapper.insert(storage);
// 模拟异常
// int a = 1 / 0;
} catch (Exception e) {
throw new RuntimeException("扣减库存失败,可能是库存不足!", e);
}
log.info("库存信息记录成功");
}
}
测试时没有做截图进行演示,只说明了结果,可以运行代码设置异常进行验证
在order-service
服务中正常,在storage-service
服务的service中抛出异常,观察数据是否成功回滚;如果tb_order
与tb_storage
都不存在数据,则表示全局事务成功;
order-service
服务在执行storageClient.deduct()
方法后抛出异常,在storage-service
服务中正常,观察数据是否成功回滚;如果tb_order
与tb_storage
都不存在数据,则表示全局事务成功;
我们可以在上游服务执行完orderMapper.insert(order);``后马上进入断点,测试去观察数据库会发现tb_order
中存在数据,再放行断点使程序执行异常,再次观察数据库会发现tb_order
中的数据已经被删除了;
Seata值AT模式代码实现:《sharding-tx-seata》
问题情景 混淆群内的小伙伴遇到这么个问题,Mailivery 这个网站登录后,明明提交的表单(邮箱和密码也正确)、请求头等等都没问题,为啥一直重定向到登录页面呢?唉,该出手时就出手啊,我也看看咋回事
实战-行业攻防应急响应 简介: 服务器场景操作系统 Ubuntu 服务器账号密码:root/security123 分析流量包在/home/security/security.pcap 相
背景 最近公司将我们之前使用的链路工具切换为了 OpenTelemetry. 我们的技术栈是: OTLP C
一 同一类的方法都用 synchronized 修饰 1 代码 package concurrent; import java.util.concurrent.TimeUnit; public c
一 简单例子 1 代码 package concurrent.threadlocal; /** * ThreadLocal测试 * * @author cakin */ public class T
1. 问题背景 问题发生在快递分拣的流程中,我尽可能将业务背景简化,让大家只关注并发问题本身。 分拣业务针对每个快递包裹都会生成一个任务,我们称它为 task。task 中有两个字段需要
实战环境 elastic search 8.5.0 + kibna 8.5.0 + springboot 3.0.2 + spring data elasticsearch 5.0.2 +
Win10下yolov8 tensorrt模型加速部署【实战】 TensorRT-Alpha 基于tensorrt+cuda c++实现模型end2end的gpu加速,支持win10、
yolov8 tensorrt模型加速部署【实战】 TensorRT-Alpha 基于tensorrt+cuda c++实现模型end2end的gpu加速,支持win10、linux,
目录如下: 为什么需要自定义授权类型? 前面介绍OAuth2.0的基础知识点时介绍过支持的4种授权类型,分别如下: 授权码模式 简化模式 客户端模式 密码模式
今天这篇文章介绍一下如何在修改密码、修改权限、注销等场景下使JWT失效。 文章的目录如下: 解决方案 JWT最大的一个优势在于它是无状态的,自身包含了认证鉴权所需要的所有信息,服务器端
前言 大家好,我是捡田螺的小男孩。(求个星标置顶) 我们日常做分页需求时,一般会用limit实现,但是当偏移量特别大的时候,查询效率就变得低下。本文将分四个方案,讨论如何优化MySQL百万数
前言 大家好,我是捡田螺的小男孩。 平时我们写代码呢,多数情况都是流水线式写代码,基本就可以实现业务逻辑了。如何在写代码中找到乐趣呢,我觉得,最好的方式就是:使用设计模式优化自己
我们先讲一些arm汇编的基础知识。(我们以armv7为例,最新iphone5s上的64位暂不讨论) 基础知识部分: 首先你介绍一下寄存器: r0-r3:用于函数参数及返回值的传递 r4-r6
一 同一类的静态方法都用 synchronized 修饰 1 代码 package concurrent; import java.util.concurrent.TimeUnit; public
DRF快速写五个接口,比你用手也快··· 实战-DRF快速写接口 开发环境 Python3.6 Pycharm专业版2021.2.3 Sqlite3 Django 2.2 djangorestfram
一 添加依赖 org.apache.thrift libthrift 0.11.0 二 编写 IDL 通过 IDL(.thrift 文件)定义数据结构、异常和接口等数据,供各种编程语言使用 nam
我正在阅读 Redis in action e-book关于semaphores的章节.这是使用redis实现信号量的python代码 def acquire_semaphore(conn, semn
自定义控件在WPF开发中是很常见的,有时候某些控件需要契合业务或者美化统一样式,这时候就需要对控件做出一些改造。 目录 按钮设置圆角
师父布置的任务,让我写一个服务练练手,搞清楚socket的原理和过程后跑了一个小demo,很有成就感,代码内容也比较清晰易懂,很有教育启发意义。 代码 ?
我是一名优秀的程序员,十分优秀!