sharding-vsphere 测试

本文对Sharding-JDBC官方demo进行测试。先介绍准备工作,包括下载源码、查看版本和配置sql环境。接着进行精确分库测试,分析代码逻辑流程;开展读写分离测试,观察数据读写情况;还进行了分布式主键测试,解释默认策略不连续及尾数多偶数的原因,3.1.0版本已解决该问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

sharding-jdbc官方demo测试

官方demo github:
https://github.com/apache/incubator-shardingsphere-example/tree/master

一、准备工作

github下载源码

第一步:
查看pom.xml信息,判断它这个example是针对哪个版本的sharding-jdbc。
如:
<sharding-sphere.version>3.1.0</sharding-sphere.version>

第二步:
查看 readme.md, 得知 给的 数据库demo sql的位置。
在项目的根目录下的src/resources下

The manual schema initial script is in `https://github.com/sharding-sphere/sharding-sphere-example/blob/dev/src/resources/manual_schema.sql`, 
please execute it before you first run the example.

第三步:
执行sql,配置好相关的sql环境,方便后面测试。

查看demo’源码
sharding-jdbc-example/spring-namespace-nodep-example/spring-namespace-nodep-mybatis-example

它把分片分成2大类,一个精确分片,一个是范围分片。

precise 英 [prɪˈsaɪs]
美 [prɪˈsaɪs]
adj. 准确的; 确切的; 精确的; 明确的; (强调时间或方式等) 就,恰好; 细致的; 精细的; 认真的; 一丝不苟的;

二、精确分库测试(spring+mybatis环境)

两个数据库demo_ds_0, demo_ds_1

第一步:创建数据库

使用如下命令,创建这两个数据库

CREATE SCHEMA IF NOT EXISTS demo_ds_0;
CREATE SCHEMA IF NOT EXISTS demo_ds_1;

第二步:application-sharding-databases-precise.xml 配置文件修改

修改为你数据库账号密码以及端口号

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context" 
    xmlns:tx="http://www.springframework.org/schema/tx" 
    xmlns:sharding="http://shardingsphere.io/schema/shardingsphere/sharding"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans.xsd 
                        http://www.springframework.org/schema/tx 
                        http://www.springframework.org/schema/tx/spring-tx.xsd
                        http://www.springframework.org/schema/context 
                        http://www.springframework.org/schema/context/spring-context.xsd
                        http://shardingsphere.io/schema/shardingsphere/sharding 
                        http://shardingsphere.io/schema/shardingsphere/sharding/sharding.xsd">
    <import resource="classpath:META-INF/shardingTransaction.xml"/>
    <context:component-scan base-package="io.shardingsphere.example.repository.mybatis" />
    
    <bean id="demo_ds_0" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:13306/demo_ds_0"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
    
    <bean id="demo_ds_1" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:13306/demo_ds_1"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
    </bean>
    
    <sharding:inline-strategy id="databaseStrategy" sharding-column="user_id" algorithm-expression="demo_ds_${user_id % 2}" />
    
    <sharding:data-source id="shardingDataSource">
        <sharding:sharding-rule data-source-names="demo_ds_0, demo_ds_1">
            <sharding:table-rules>
                <sharding:table-rule logic-table="t_order" database-strategy-ref="databaseStrategy" generate-key-column-name="order_id" />
                <sharding:table-rule logic-table="t_order_item" database-strategy-ref="databaseStrategy" generate-key-column-name="order_item_id" />
            </sharding:table-rules>
        </sharding:sharding-rule>
    </sharding:data-source>
    
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="shardingDataSource" />
    </bean>
    <tx:annotation-driven />
    
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="shardingDataSource"/>
        <property name="mapperLocations" value="classpath*:META-INF/mappers/*.xml"/>
    </bean>
    
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="io.shardingsphere.example.repository.mybatis.repository"/>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
    </bean>
</beans>

第三步: 执行SpringNamespaceExample

执行SpringNamespaceExample 根据打印跟踪。

精确分库测试demo代码逻辑流程分析总结

  1. 初始化spring容器
    根据你选择分片类型 精确数据库,加载特定的xml到 spring上下文 。

此步骤会把xml的一些配置,加载到spring 的bean中,让spring管理。

  1. 通过 spring 上下文获取业务bean
    SpringPojoServiceImpl 类

1)使用@Resource注入
注入Mapper接口 mybatis-repository,这里它的Mapper接口都是以Repository结尾。

一般Mapper接口都是以Mapper做结尾,方便辨识,这里为什么用Repository结尾呢?
分析:方便抽象,因为这里会测试三种数据库访问方式 JDBC、JPA、MyBatis,比如在业务类的父抽象类public abstract class CommonServiceImpl中,我们可以getOrderRepository()、 getOrderItemRepository()获取子类对的数据库访问实例,不一定是Mybatis的Mapper实例。
在这里插入图片描述

@Mapper
public interface MybatisOrderItemRepository extends OrderItemRepository {
}
@Mapper
public interface MybatisOrderRepository extends OrderRepository {
}

2)继承抽象类 CommonServiceImpl
继承抽象类 CommonServiceImpl 实现了方法
initEnvironment() 初始化环境: 创建表

@Service
@Transactional
public class SpringPojoServiceImpl extends CommonServiceImpl implements SpringPojoService {
    
    @Resource
    private OrderRepository orderRepository;
    
    @Resource
    private OrderItemRepository orderItemRepository;
    
  1. 调业务类初始化方法 commonService.initEnvironment();
    该方法内容如下:
    getOrderRepository获取实现子类注入的Mapper实例,进行数据库操作,主要做创建表和清空表。
public abstract class CommonServiceImpl implements CommonService {
    
    @Override
    public void initEnvironment() {
        getOrderRepository().createTableIfNotExists();
        getOrderItemRepository().createTableIfNotExists();
        getOrderRepository().truncateTable();
        getOrderItemRepository().truncateTable();
    }
  1. 调processSuccess 测试成功插入和成功删除记录过程。
    至此,真正开始测试精确分库,insertData方法插入数据库,返回 订单主键。
    后面又根据 订单主键传入deleteData方法进行删除。
    @Transactional
    @Override
    public void processSuccess(final boolean isRangeSharding) {
        System.out.println("-------------- Process Success Begin ---------------");
        List<Long> orderIds = insertData();
        printData(isRangeSharding);
        deleteData(orderIds);
        printData(isRangeSharding);
        System.out.println("-------------- Process Success Finish --------------");
    }

插入以及删除,都要利用分片规则去特定的库中插入和删除。
因为插入和删除在一个事务中,你在insert完还没有deleteData时,事务还没有结束,你在插入完数据后加断点,你用navicat也看不到插入的数据,无法判断它是否正确按照分片规则,分库存储。

因此,注调
deleteData(orderIds);
printData(isRangeSharding);
断点加在processSuccess方法执行完,观察入库情况,进行判断入库是否正确。

spring读取xml中的分片规则如下:
分片策略:内部策略,进行分片的列 user_id ,算法表达式: demo_ds_${user_id % 2} 即用户id与2取模,决定它存储到那个库。
指定访问这些表的逻辑表名为:logic-table=“t_order”、logic-table=“t_order_item”

    <sharding:inline-strategy id="databaseStrategy" sharding-column="user_id" algorithm-expression="demo_ds_${user_id % 2}" />
    
    <sharding:data-source id="shardingDataSource">
        <sharding:sharding-rule data-source-names="demo_ds_0, demo_ds_1">
            <sharding:table-rules>
                <sharding:table-rule logic-table="t_order" database-strategy-ref="databaseStrategy" generate-key-column-name="order_id" />
                <sharding:table-rule logic-table="t_order_item" database-strategy-ref="databaseStrategy" generate-key-column-name="order_item_id" />
            </sharding:table-rules>
        </sharding:sharding-rule>
    </sharding:data-source>

观察完入库后,我们再观察删记录,查看删记录代码,

    private void deleteData(final List<Long> orderIds) {
        System.out.println("---------------------------- Delete Data ----------------------------");
        for (Long each : orderIds) {
            getOrderRepository().delete(each);
            getOrderItemRepository().delete(each);
        }
    }

跟Mapper实例的delete sql,可以发现它 不用写库名,直接使用逻辑表名,传入order_id,它就会根据分库规则自动到对应的数据库删除相应记录。

    <delete id="delete">
        DELETE FROM t_order WHERE order_id = #{orderId,jdbcType=INTEGER};
    </delete>

至此,官方demo,分库测试代码,分析完毕。

三、读写分离测试(只读写分离、不分片场景)

【死磕Sharding-jdbc】—–读写分离
参考URL: http://cmsblogs.com/?p=2550#

根据代码分析,它分成分片的读写分离和不分片的读写分离,本节测试ShardingType.MASTER_SLAVE;

      private static ShardingType type = ShardingType.MASTER_SLAVE;
//    private static ShardingType type = ShardingType.SHARDING_MASTER_SLAVE;

读写分离支持项

  • 提供了一主多从的读写分离配置,可独立使用,也可配合分库分表使用。
  • 同一线程且同一数据库连接内,如有写入操作,以后的读操作均从主库读取,用于保证数据一致性。
  • Spring命名空间。
  • 基于Hint的强制主库路由。

第一步:执行sql配置测试数据库环境

CREATE SCHEMA IF NOT EXISTS demo_ds_master;
CREATE SCHEMA IF NOT EXISTS demo_ds_slave_0;
CREATE SCHEMA IF NOT EXISTS demo_ds_slave_1;

第二步:调试demo源码测试

如下,注释掉删除函数 deleteData, 加入如下2个断点,我们可以观察到插入和查询printData都正常。
说明:同一线程且同一数据库连接内,如有写入操作,以后的读操作均从主库读取,用于保证数据一致性。
在这里插入图片描述
继续在方法外,加断点,执行printData(isRangeSharding); 发现报错如下,Table ‘demo_ds_slave_1.t_order’ doesn’t exist 说明它从从表读数据。
在这里插入图片描述

关于spring xml读写分离配置核心如下:
Spring事务管理Transaction Manager配置 ref为masterSlaveDataSource

在id=“masterSlaveDataSource” 中 定义了 主数据源名字、从数据源名字,以及策略引用strategy-ref=“randomStrategy”

在id=“randomStrategy” 中,我们定义了具体策略算法实现类,这里选择的算法是随机负载均衡算法。

    <bean id="randomStrategy" class="io.shardingsphere.api.algorithm.masterslave.RandomMasterSlaveLoadBalanceAlgorithm" />
    
    <master-slave:data-source id="masterSlaveDataSource" master-data-source-name="demo_ds_master" slave-data-source-names="demo_ds_slave_0, demo_ds_slave_1" strategy-ref="randomStrategy" />
    
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="masterSlaveDataSource" />
    </bean>
    <tx:annotation-driven />

四、其它测试

分布式主键

[推荐]数据库分库分表中间件 Sharding-JDBC 源码分析 —— 分布式主键
参考URL: https://www.cnblogs.com/yunai/p/7588528.html
跟我学shardingjdbc之分布式主键及其自定义
参考URL: http://wuwenliang.net/2019/03/25/跟我学shardingjdbc之分布式主键及其自定义/

雪花算法(SnoWflake)是Twitter公布的分布式主键生成算法,也是ShardingSphere默认提供的配置分布式主键生成策略方式。在ShardingSphere的类路径为:io.shardingsphere.core.keygen.DefaultKeyGenerator

官方code 提供了相应的单元测试,查看运行单元测试即可
如下单元测试代码: 多个线程同时调generateKey方法,执行taskNumber次,返回的id放到set中,判断set中的元素个数是否是taskNumber个,从而判断多线程执行时,是否有重复id。

    public void assertGenerateKeyWithMultipleThreads() {
        int threadNumber = Runtime.getRuntime().availableProcessors() << 1;
        System.out.println("threadNumber: " + threadNumber);

        ExecutorService executor = Executors.newFixedThreadPool(threadNumber);
        int taskNumber = threadNumber << 2;

        System.out.println("taskNumber: " + taskNumber);
        final DefaultKeyGenerator keyGenerator = new DefaultKeyGenerator();
        Set<Number> actual = new HashSet<>();
        for (int i = 0; i < taskNumber; i++) {
            actual.add(executor.submit(new Callable<Number>() {
                
                @Override
                public Number call() {
                    return keyGenerator.generateKey();
                }
            }).get());
        }
        assertThat(actual.size(), is(taskNumber));
    }

DefaultKeyGeneratorTest
同时针对id生成器,官方代码提供了一个工厂类,根据类名string反射获取KeyGenerator 实例。

public final class KeyGeneratorFactory {
    
    /**
     * Create key generator.
     * 
     * @param keyGeneratorClassName key generator class name
     * @return key generator instance
     */
    public static KeyGenerator newInstance(final String keyGeneratorClassName) {
        try {
            return (KeyGenerator) Class.forName(keyGeneratorClassName).newInstance();
        } catch (final ReflectiveOperationException ex) {
            throw new IllegalArgumentException(String.format("Class %s should have public privilege and no argument constructor", keyGeneratorClassName));
        }
    }
}
官网:ShardingSphere提供的默认分布式自增主键策略为什么是不连续的,且尾数大多为偶数?

ShardingSphere采用snowflake算法作为默认的分布式分布式自增主键策略,用于保证分布式的情况下可以无中心化的生成不重复的自增序列。因此自增主键可以保证递增,但无法保证连续。

而snowflake算法的最后4位是在同一毫秒内的访问递增值。因此,如果毫秒内并发度不高,最后4位为零的几率则很大。因此并发度不高的应用生成偶数主键的几率会更高。

在3.1.0版本中,尾数大多为偶数的问题已彻底解决,参见:https://github.com/sharding-sphere/sharding-sphere/issues/1617

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

西京刀客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值