分布式存储-ShardingSphere(读写分离&分布式事务)

分布式存储-ShardingSphere(读写分离&分布式事务)

前面聊到ShardingSphere的一些配置和使用,但是作为一个数据库中间件,它可以做的可不是仅仅进行分库和分表。本篇想聊聊

  • 对mysql读写分离的支持
  • 它支持的分布式事务,默认的管理器是【Atomikos
  • 同时也会搭建一主一从的这样一个mysql服务器【因为有至少两个服务器是shardingshpher读写分离的前提】
  • 也会顺便聊聊2pc,base,cap理论这些和分布式事务相关的理论。

mysql读写分离

我们在读多写少的场景下,对数据库进行读写分离的好处有:

  • 减少共享锁和排他锁的竞争。
  • 减少服务器的压力:当读请求过多的时候,我们可以通过横向扩充读库去减少数据库的压力。
  • 配置不同类型的数据库:大部分情况下我们配置的是innodb(支持事务的操作)。而对于查询我们就可以配置MyISAM引擎从而提升效率

mysql读写分离的配置【binlog】:

  • 当数据库发生事务操作的时候,他就会把操作写进一个日志中,
  • 然后slave节点中有一个io的线程定时发送一个read操作,
  • master会生成一个binlog,并且返回binlog给slave,
  • slave就会把得到的binlog写在自己的relaylog中,然后用一个线程去执行

操作步骤:

【整体流程】

  • mater节点需要开启binlog日志
  • slave节点需要指定某个binlog,以及从那个位置开始读取
  • slave需要指定master节点的ip以及用户名和密码

【实际步骤】

【搭建两台mysql服务器】(我使用的是mysql8.0):

  • 192.168.43.4【master】
  • 192.168.43.3 【slave】

【master节点开启binlog】在mysql的配置文件中加上两行代码

  • log-bin=/var/lib/mysql/mysql-bin
  • server-id=1001
    • log-bin指的是你的logbin文件的位置,
    • server-id指的是你的这台mysql的唯一标识slave在读取的时候需要知道这个东西
  • 然后重启mysql,重启后就发现logbin已经生成在了log-bin的目录中
  • 或者使用sql查询【show variables like 'log_bin%';】发现log-bin已经是on状态了,这也证明已经开启了

【在master节点创建一个用户】:相当于一个白名单,只有这个用户可以复制数据从master上

  • -- 创建用户,其中repl表示用户名, 192.168.43.3表示slave库的ip地址,也就是只允许这个ip通过repl用户访问master库
    • create user 'repl'@'192.168.43.3' identified with mysql_native_password by'123456';   
  • 授权 
    • -- replication slave 表示授权复制 -- *.* 表示所有的库和表
    • grant replication slave on *.* to 'repl'@'192.168.43.3';
  • 刷新权限信息
    • flush privileges; 
【slave节点配置】:
首先在master节点,通过【show master status】了解master节点的状态,把file的内容填写到master-log-file中,把position填写到master-log-pos中

【 连接master 】change master to master_host='192.168.43.4',master_user='repl',master_password='123456',master_log_file='binlog.000002',master_log_pos=855;

 查询同步状态:【show slave status\G】 当看见下面这两个属性都是yes就证明我们已经配置完

 【可能遇见的问题】:因为我是直接把虚拟机拷贝了一份,但是mysql的serverid是一致的,所以可能会出现上面的slave-io-running:no的情况,我们只需要把他的auto-confi中的id修改不一致即可。

测试】:当我们在master上新建一个数据库的时候,slave上就会自动创建同样的数据库。同样的,在新建表的时候,slave也会自动创建。至此主从配置配置和搭建完毕。

【binlog是什么】:查询所有的binlog【show BINARY LOGS;】 查询当前使用的binlog【SHOW BINLOG EVENTS in '这里就是使用show master status查询出来的file'】。他其实就是把每一次的sql放在info中,slave拿过去进行解析后执行,就实现了和master一样的效果了

 【主从同步延迟怎么办?】:主节点和从节点进行通信那网络延迟是无法被规避的,一般可能是

大事务或者节点过多而造成的数据同步延迟,这种情况下,我们可以通过【show slave status\G】中的这几个字段判断和主库的延迟量,如果延迟过大我们直接强制走主库。

 或者通过控制slave节点的数量,或者去做级联,具体还是需要通过实际情况而定。

ShardingJDBC实现读写分离 

上面已经搭建了主从的这样的mysql服务器,但是,当一个sql来访问我们的服务端的时候,怎么判断他走哪个节点呢?这个时候shardingSphere就为我们了干了这个事情。

【配置】

spring.shardingsphere.props.sql-show=true
spring.shardingsphere.datasource.names="write-ds,read-ds"
spring.shardingsphere.datasource.common.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.common.driver-class-name=com.mysql.jdbc.Driver

#写库配置
spring.shardingsphere.datasource.write-ds.jdbc-url=jdbc:mysql://192.168.43.4/readwritedb?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8
spring.shardingsphere.datasource.write-ds.username=root
spring.shardingsphere.datasource.write-ds.password=123456

#读库配置
spring.shardingsphere.datasource.read-ds.jdbc-url=jdbc:mysql://192.168.43.3/readwritedb?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8
spring.shardingsphere.datasource.read-ds.username=root
spring.shardingsphere.datasource.read-ds.password=123456

# db0这个名字随意定义,就是一个逻辑库的名字
spring.shardingsphere.rules.replica-query.data-sources.db0.primary-data-source-name=write-ds
spring.shardingsphere.rules.replica-query.data-sources.db0.replica-data-source-names=read-ds
spring.shardingsphere.rules.replica-query.load-balancers.db0.type=ROUND_ROBIN
#无任何意义
spring.shardingsphere.rules.replica-query.load-balancers.db0.props.test=test
View Code

测试插入一条数据

发现它走的是写库

 查询一条数据

 发现它走的是读库

ShardingJDBC分布式事务

分库分表解决了数据库的压力,可是,这就带来了一个新的问题,分布式事务。当用户点击一个按钮的时候,有可能是涉及到多个表的操作,但是,经过分库分表后,这些表没有在同一个数据库中,那如何保证事务的一致性呢?这个时候就引出了全局事务。流程如下:

  • 在一个应用中,同时操作两个数据库的数据【当mysql收到需要操作某个数据库的数据的时候他不能直接操作,因为这就变成了单个事务了】
  • 这个时候引入一个第三方【全局事务】,这个全局事务决定这两个操作是同时成功还是失败。
  • 当这两个数据库的操作都完成后【他们不会直接提交事务】,他们就会分别给全局事务一个消息,告诉全局事务他们是否操作成功还是失败。
  • 一旦有一个节点的执行错误,这个全局事务就让他们都回滚事务,否则才告知他们提交事务,这样就能保证了事务的一致性

【问题】:我们这里肯定不能使用传统的事务来做,因为使用全局事务他就会自动提交了,那这里就有一个XA协议

【XA协议】:是 X/Open 这个组织定义的一套分布式事务的标准中的一个协议,实际上这个组织提供三个角色和两个协议,使用这些才能构建一个分布式事务的解决方案。

三个角色分别如下

AP(Application Program):表示应用程序,也可以理解成使用DTP模型的程序
RM(Resource Manager):资源管理器,这个资源可以是数据库应用程序通过资源管理器对资源进行控制,资源管理器必须实现XA定义的接口
TM(Transaction Manager):表示事务管理器,负责协调和管理全局事务事务管理器控制整个全局事务,管理事务的生命周期,并且协调资源。 
两个协议分别是:
【XA协议】:
TM用它来通知和协调相关RM事务的开始、结束、提交或回滚。目前Oracle、Mysql、DB2都提供了对XA的支持;
【TX协议】: 全局事务管理器与资源管理器之间通信的接口
工作流程:

 具体做法:

配置

spring.shardingsphere.props.sql-show=true
spring.shardingsphere.datasource.names="ds-0,ds-1"
spring.shardingsphere.datasource.common.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.common.driver-class-name=com.mysql.jdbc.Driver

#配置两个数据源
spring.shardingsphere.datasource.ds-0.jdbc-url=jdbc:mysql://192.168.43.3/shard01?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8
spring.shardingsphere.datasource.ds-0.username=root
spring.shardingsphere.datasource.ds-0.password=123456

spring.shardingsphere.datasource.ds-1.jdbc-url=jdbc:mysql://192.168.43.4/shard02?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=UTF-8
spring.shardingsphere.datasource.ds-1.username=root
spring.shardingsphere.datasource.ds-1.password=123456

#使用user_id作为分片键
spring.shardingsphere.rules.sharding.default-database-strategy.standard.sharding-column=user_id
spring.shardingsphere.rules.sharding.default-database-strategy.standard.sharding-algorithm-name=database-inline
spring.shardingsphere.rules.sharding.sharding-algorithms.database-inline.type=INLINE
spring.shardingsphere.rules.sharding.sharding-algorithms.database-inline.props.algorithm-expression=ds-$->{user_id % 2}

#主键生成规则
spring.shardingsphere.rules.sharding.tables.t_order.key-generate-strategy.column=order_id
spring.shardingsphere.rules.sharding.tables.t_order.key-generate-strategy.key-generator-name=snowflake


spring.shardingsphere.rules.sharding.key-generators.snowflake.type=SNOWFLAKE
spring.shardingsphere.rules.sharding.key-generators.snowflake.props.worker-id=123
View Code

在代码中增加一个注解即可,我们测试在代码中让它报错,看看事务会不会回滚

 @Override
    @Transactional(rollbackFor = Exception.class)
    @ShardingTransactionType(TransactionType.XA)
    public TOrder addOrder() {
        for (int i = 0; i <10 ; i++) {
            TOrder tOrder=new TOrder();
            tOrder.setAddressId(1);
            tOrder.setStatus("GLOBAL_TRANSACTION");
            tOrder.setUserId(random.nextInt(1000));
            //这里让i等于4的时候报错,看看会不会回滚
            
            if(i==4){
                int ex=1/0;
            }
            orderMapper.insert(tOrder);
        }
        return new TOrder();
    }
View Code

我们看见已经报错了,并且数据没有进入到数据库

ShardingSphere 默认的 XA 事务管理器为 Atomikos,那他是如何实现多数据源头的事务管理的呢?

实际上主要还是基于2pc的提交(一种一致性协议):

  • 表示事务管理器向每一个数据库都发送执行命令的操作,每个数据库就开始执行他们的操作,并且把执行的结果发送给事务管理器
  • 如果资源管理器【数据库】有一个的发送结果是错误的,那资源管理器事务管理器就向数据库发送事务回滚的要求,否则是发送让他们都执行的要求。

分布式事务涉及问题

【XA事务存在的问题】
因为xa是属于强一致性事务,因为在全局事务中,只要有任何一个RM出现异常,都会导致全局事务回滚,同时在第一阶段的时候给数据库发送请求的时候,会锁定数据库资源,但是一旦网络延迟了,那就要等待很久。
 
CAP理论】:这个理论告诉我们在分布式系统中,这三个只能满足两个。对于一个业务系统来说,【可用性】和【分区容错性】是必须要满足的两个条件,并且这两者是相辅相成的。我们必须保证系统可用,同时一个节点宕机,整个系统不能瘫痪。
  • C:Consistency 一致性 同一数据的多个副本是否实时相同。
  • A:Availability 可用性 可用性:一定时间内 & 系统返回一个明确的结果 则称为该系统可用。
  • P:Partition tolerance 分区容错性 将同一服务分布在多个系统中,从而保证某一个系统宕机,仍然有其他系统提供相同的服务
 【BASE理论】:
  • Basically Available(基本可用):允许在一定时间段,我们的数据不同步,比如说我master中的数据是5而slave节点的数据是2
  • Soft State(柔性状态):允许系统在多个不同节点的数据副本存在数据延时。
  • Eventually Consistent(最终一致性):在最终的某个点的时候达到数据的最终一致性
对于base理论我们要做的就是【最终一致性】,这可以通过【重试机制】达到效果:比如说我们在注册的时候,有一个赠送积分的功能,但是由于网络或者其他原因失败了,那我们可以把这个失败的数据放在一个本地的消息表中,不断的去跑这个消息然后进行重新赠送积分。

posted @ 2021-11-10 18:10  UpGx  阅读(916)  评论(0编辑  收藏  举报