网站建设公司的职责,工商网站,想学电商运营在哪里学,快速搭建网站服务器分布式架构
分布式事务产生的场景#xff1a;
跨JVM进程产生的分布式事务 单体系统访问多个数据库实例 多服务访问同一个数据库实例
CAP理论 C#xff1a;一致性#xff0c;指写操作后的读操作可以读取到最新的数据状态#xff0c;当数据分布在多个节点上#xff0…分布式架构 分布式事务产生的场景
跨JVM进程产生的分布式事务 单体系统访问多个数据库实例 多服务访问同一个数据库实例
CAP理论 C一致性指写操作后的读操作可以读取到最新的数据状态当数据分布在多个节点上从任意节点读取到的数据都是最新状态。 写入主数据库后要将数据同步到从数据库 写入主数据库后在向从数据库同步期间要将从数据库锁定待同步完成后再释放锁以免在新数据写入成功后向从数据库查询到旧的数据
A可用性指任何事务操作都可以得到响应结果且不会出现响应超时或响应错误。 写入主数据库后要将数据同步到从数据库 由于要保证从数据库的可用性不可将从数据库中的资源进行锁定 即使数据还没有同步过来从数据库也要返回要查询的数据哪怕是旧数据如果连旧数据也没有则可以按照约定返回一个默认的信息但不能返回错误或响应超时。
P分区容忍性网络分区的不同结点出现的网络问题导致节点之间通信失败此时仍然可以对外提供服务。 尽量使用异步取代同步操作使用异步方式将数据从主数据库同步到从数据库这样节点之间能够有效的实现松耦合 添加从数据库节点其中一个节点挂掉后其他从节点提供服务。
CAP组合
Base理论 分布式事务解决方案
2PC 两阶段提交
XA方案 需要本地数据库支持XA协议 资源锁需要等到两个阶段结束才释放性能较差
Seata方案 阿里中间件分布式事务框架 提供AT模式2PC以及TCC模式的分布式事务解决方案 把分布式事务理解为一个包含了若干分支事务的全局事务全局事务的职责是协调其下管辖的分支事务达成一致 要么一起成功提交要么一起失败回滚。 程序实现 配置文件 application.yml application-local.yml file.conf
dtx-seata-demo-bank1 张三账户减少金额开启全局事务 远程调用bank2向李四转账
//Dao MyBatis框架进行
Mapper
Component
public interface AccountInfoDao{Update(update account_info set account_blance account_balance #{amount} where account_no #{accountNo})int updateAccountBalnce(Param(accountNo) String accountNo,Param(amount) Double amount);
}//张三扣减金额
public interface AccountInfoService{public void updateAccountBalance(String accountNo,Double amount);
}Service
Slf4j
public class AccountInfoServiceImpl implements AccountInfoService{AutowiredAccountInfoDao accountInfoDao;AutowiredBank2Client bank2Client;TransactionalBlobalTransactional//开启全局事务Overridepublic void updateAccountBalance(String accountNo,Double amount{//扣除张三的金额accountInfoDao.updateAccountBalance(accountNo,amount * -1);//调用李四的微服务来转账String transfer bank2Client.transfer(amount);if(fallback.equals(transfer)){//调用李四微服务异常throw new RuntimeException(调用李四微服务异常);}}
}//FeignClient
FeignClient(valueseata-demo-bank2,fallbackBank2ClientFallack.class)//如果调用失败降级
public interface Bank2Client{//远程调用李四的微服务GetMapping(/bank2/transfer)public String transfer(RequestParam(amount) Double amout);
}//调用失败降级
Component
public class Bank2ClientFallback implements Bank2Client{Overridepublic String transfer(Double amount){return fallback;}
}//启动类
SpringBootApplication
EnableDiscoveryClient
EnableHystrix
EnableFeignClients(basePackages {cn.michael.bank1.spring})//专门扫描客户端的包
public class Bank2Server{public static void main(String[] args){SpringApplication.run(Bank2Server.class,args);}
}dtx-seata-demo-bank2
//李四账号增加金额
//分支事务不适用GlobalTransactional
Mapper
Component
public interface AccountInfoDao{Update(UPDATE account_info SET account_balance account_balance #{amount} WHERE account_no)int updateAccountBalance(Param(accountNo) String accountNo,Param(amount) Double amount);
}public interface AccountInfoService{public void updateAccountBalance(String accountNo,Double amount);
}Service
Slf4j
public class AccountInfoServiceImpl implements AccountInfoService{AutowiredAccountInfoDao accountInfoDao;//不使用GlobalTransactionalTransactionalOverridepublic void updateAccountBalance(String accountNo,Double amount){accountaInfoDao.updateAccountBalance(accountNo,amount);}
} 分布式ID 一、数据库自增
①、创建一个数据库表
stub 字段无意义只是为了占位便于插入或者修改数据。并且给 stub 字段创建了唯一索引保证其唯一性。
CREATE TABLE sequence_id (id bigint(20) unsigned NOT NULL AUTO_INCREMENT,stub char(10) NOT NULL DEFAULT ,PRIMARY KEY (id),UNIQUE KEY stub (stub)
) ENGINEInnoDB DEFAULT CHARSETutf8mb4;②、通过replace into 来插入数据
插入数据这里没有使用 insert into 而是使用 replace into 来插入数据具体步骤是这样的
尝试把数据插入到表中如果主键或唯一索引字段出现重复数据错误而插入失败时先从表中删除含有重复关键字值的冲突行然后再次尝试把数据插入到表中
BEGIN;
REPLACE INTO sequence_id (stub) VALUES (stub);
SELECT LAST_INSERT_ID();
COMMIT;优点实现起来比较简单、ID 有序递增、存储消耗空间小 缺点支持的并发量不大、存在数据库单点问题可以使用数据库集群解决不过增加了复杂度、ID 没有具体业务含义、安全问题比如根据订单 ID 的递增规律就能推算出每天的订单量商业机密啊、每次获取 ID 都要访问一次数据库增加了对数据库的压力获取速度也慢
二、数据库号段模式
数据库主键自增这种模式每次获取 ID 都要访问一次数据库ID 需求比较大的时候性能问题
可以批量获取然后存在在内存里面需要用到的时候直接从内存里面拿就舒服了这也就是我们说的 基于数据库的号段模式来生成分布式 ID 数据库的号段模式也是目前比较主流的一种分布式 ID 生成方式。像滴滴开源的Tinyid[1] 就是基于这种方式来做的。不过TinyId 使用了双号段缓存、增加多 db 支持等方式来进一步优化。
①、创建一个数据表
current_max_id 字段和step字段主要用于获取批量 ID获取的批量 id 为current_max_id ~ current_max_idstep version 字段主要用于解决并发问题乐观锁,biz_type 主要用于表示业务类型。
CREATE TABLE sequence_id_generator (id int(10) NOT NULL,current_max_id bigint(20) NOT NULL COMMENT 当前最大id,step int(10) NOT NULL COMMENT 号段的长度,version int(20) NOT NULL COMMENT 版本号,biz_type int(20) NOT NULL COMMENT 业务类型,PRIMARY KEY (id)
) ENGINEInnoDB DEFAULT CHARSETutf8mb4;②、先插入一行数据
INSERT INTO sequence_id_generator (id, current_max_id, step, version, biz_type)
VALUES(1, 0, 100, 0, 101);③、通过 SELECT 获取指定业务下的批量唯一 ID
SELECT current_max_id, step,version FROM sequence_id_generator where biz_type 101④、不够用的话更新之后重新 SELECT 即可。
UPDATE sequence_id_generator SET current_max_id 0100, versionversion1 WHERE version 0 AND biz_type 101
SELECT current_max_id, step,version FROM sequence_id_generator where biz_type 101相比于数据库主键自增的方式数据库的号段模式对于数据库的访问次数更少数据库压力更小。 另外为了避免单点问题你可以从使用主从模式来提高可用性。
优点ID 有序递增、存储消耗空间小 缺点存在数据库单点问题可以使用数据库集群解决不过增加了复杂度、ID 没有具体业务含义、安全问题比如根据订单 ID 的递增规律就能推算出每天的订单量商业机密啊
三、NoSQL
通过 Redis 的 incr 命令即可实现对 id 原子顺序递增
为了提高可用性和并发可以使用 Redis Cluster。Redis Cluster 是 Redis 官方提供的 Redis 集群解决方案3.0版本。
除了 Redis Cluster 之外也可以使用开源的 Redis 集群方案Codis[2] 大规模集群比如上百个节点的时候比较推荐。
除了高可用和并发之外 Redis 基于内存我们需要持久化数据避免重启机器或者机器故障后数据丢失。Redis 支持两种不同的持久化方式快照snapshottingRDB、只追加文件append-only file, AOF。并且Redis 4.0 开始支持 RDB 和 AOF 的混合持久化默认关闭可以通过配置项 aof-use-rdb-preamble 开启。
优点性能不错并且生成的 ID 是有序递增的 缺点和数据库主键自增方案的缺点类似
除了 Redis 之外MongoDB ObjectId 经常也会被拿来当做分布式 ID 的解决方案。 MongoDB ObjectId 一共需要 12 个字节存储
0~3时间戳3~6代表机器 ID7~8机器进程 ID9~11自增值
优点性能不错并且生成的 ID 是有序递增的 缺点需要解决重复 ID 问题当机器时间不对的情况下可能导致会产生重复 ID、有安全性问题ID 生成有规律性
四、算法UUID
UUID 是 Universally Unique Identifier通用唯一标识符 的缩写。UUID 包含 32 个 16 进制数字8-4-4-4-12
JDK 就提供了现成的生成 UUID 的方法一行代码就行了。
//输出示例cb4a9ede-fa5e-4585-b9bb-d60bce986eaa
UUID.randomUUID()不同的版本对应的 UUID 的生成规则是不同的
版本 1 : UUID 是根据时间和节点 ID通常是 MAC 地址生成版本 2 : UUID 是根据标识符通常是组或用户 ID、时间和节点 ID 生成版本 3、版本 5 : 版本 5 - 确定性 UUID 通过散列hashing名字空间namespace标识符和名称生成版本 4 : UUID 使用随机性或伪随机性生成 JDK 中通过 UUID 的 randomUUID() 方法生成的 UUID 的版本默认为 4
UUID uuid UUID.randomUUID();
int version uuid.version();// 4UUID 可以保证唯一性因为其生成规则包括 MAC 地址、时间戳、名字空间Namespace、随机或伪随机数、时序等元素计算机基于这些规则生成的 UUID 是肯定不会重复的。
虽然UUID 可以做到全局唯一性但是我们一般很少会使用它。比如使用 UUID 作为 MySQL 数据库主键的时候就非常不合适
数据库主键要尽量越短越好而 UUID 的消耗的存储空间比较大32 个字符串128 位UUID 是无顺序的InnoDB 引擎下数据库主键的无序性会严重影响数据库性能
优点生成速度比较快、简单易用 缺点存储消耗空间大32 个字符串128 位、 不安全基于 MAC 地址生成 UUID 的算法会造成 MAC 地址泄露)、无序非自增、没有具体业务含义、需要解决重复 ID 问题当机器时间不对的情况下可能导致会产生重复 ID
五、算法雪花
Snowflake 是 Twitter 开源的分布式 ID 生成算法。Snowflake 由 64 bit 的二进制数字组成这 64bit 的二进制被分成了几部分每一部分存储的数据都有特定的含义
sign(1bit):符号位标识正负始终为 0代表生成的 ID 为正数timestamp (41 bits):一共 41 位用来表示时间戳单位是毫秒可以支撑 2 ^41 毫秒约 69 年datacenter id worker id (10 bits):一般来说前 5 位表示机房 ID后 5 位表示机器 ID实际项目中可以根据实际情况调整。这样就可以区分不同集群/机房的节点。sequence (12 bits):一共 12 位用来表示序列号。序列号为自增值代表单台机器每毫秒能够产生的最大 ID 数(2^12 4096),也就是说单台机器每毫秒最多可以生成 4096 个 唯一 ID。
在实际项目中一般也会对 Snowflake 算法进行改造最常见的就是在 Snowflake 算法生成的 ID 中加入业务类型信息。
优点生成速度比较快、生成的 ID 有序递增、比较灵活可以对 Snowflake 算法进行简单的改造比如加入业务 ID 缺点需要解决重复 ID 问题ID 生成依赖时间在获取时间的时候可能会出现时间回拨的问题也就是服务器上的时间突然倒退到之前的时间进而导致会产生重复 ID、依赖机器 ID 对分布式环境不友好当需要自动启停或增减机器时固定的机器 ID 可能不够灵活。
有很多基于 Snowflake 算法的开源实现比如美团 的 Leaf、百度的 UidGenerator后面会提到并且这些开源实现对原有的 Snowflake 算法进行了优化性能更优秀还解决了 Snowflake 算法的时间回拨问题和依赖机器 ID 的问题。
并且Seata 还提出了“改良版雪花算法”针对原版雪花算法进行了一定的优化改良解决了时间回拨问题大幅提高的 QPS。
分布式ID开源框架百度、滴滴等 分布式 事务解决方案TCC TCC需要主义三种异常处理空回滚 幂等 悬挂 启动类
SpringBootApplication
EnableDiscoveryClient
EnableHystrix
EnableAspectAutoProxy //
EnableFeignClients(basePackages {cn.....spring})
ComponentScan(xxx.bank1/bank2,org.dromarn.hmily)//要扫描的hmily
public class BankTccServer{public static void main(String[] args){StringApplication.run(BankTccServer.class,args);}
}