【分库分表】分库分表 Sharding-JDBC

前言

Sharding-JDBC 定位为轻量级 Java 框架,在 Java 的 JDBC 层提供额外服务。它使用客户端直连数据库,以 jar 包的形式提供服务,无需额外部署和依赖,可理解为增强版的 JDBC 驱动。

  • 适用于任何基于JDBC的ORM框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。
  • 支持任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP等。
  • 支持任意实现JDBC规范的数据库。目前支持MySQL,Oracle,SQLServer,PostgreSQL以及任何遵循SQL92标准的数据库。

一、基本概念

1.1、表

表是透明化数据分片的关键概念。Apache ShardingSphere 通过提供多样化的表类型,适配不同场景下的数据分片需求。

1.2、逻辑表

相同结构的水平拆分数据库(表)的逻辑名称,是 SQL 中表的逻辑标识。例:订单数据根据主键尾数拆分为 10 张表,分别是 t_order_0 到 t_order_9,它们的逻辑表名为 t_order。

1.3、真实表

在水平拆分的数据库中真实存在的物理表。即上个示例中的 t_order_0 到 t_order_9。

1.4、绑定表

指分片规则一致的一组分片表。使用绑定表进行多表关联查询时,必须使用分片键进行关联,否则会出现笛卡尔积关联或者跨库关联,从而影响查询效率。例如:t_order 表和 t_order_item 表,均按照 order_id 分片,并且使用 order_id 进行关联,则此两张表互为绑定表关系。绑定表之间的多表关联查询不会出现笛卡尔积关联,关联查询效率将大大提升。举例说明,如果 SQL 为:

select i.* from t_order o join t_order_item i on o.order_id = i.order_id where o.order_id in (10, 11);

在不配置绑定表关系时,假设分片键 order_id 将数值 10 路由至第 0 片,将数值 11 路由至第 1 片,那么路由后的 SQL 应该为 4 条,它们呈现为笛卡尔积:

t_order表 t_order_item表
t_order_0 t_order_item_0
t_order_0 t_order_item_1
t_order_1 t_order_item_0
t_order_1 t_order_item_1

在配置绑定表关系后,并且使用 order_id 进行关联后,路由的SQL应该为 2 条:

t_order表 t_order_item表
t_order_0 t_order_item_0
t_order_1 t_order_item_1

其中 t_order 表由于指定了分片条件,ShardingSphere 将会以它作为整个绑定表的主表。所有的分片计算将会只使用主表的策略,那么 t_order_item 表的分片计算将会使用 t_order 表的分片条件。

spring:
    shardingsphere:
        sharding:
            tables:
                t_order:
                    actualDataNodes: ds${
    
    0..2}.t_order
                    logicTable: t_order
                    databaseStrategy:
                        inline:
                            sharding-column: order_id
                            algorithm-expression: ds$->{
    
    order_id % 3}
                    keyGenerator:
                        type: SNOWFLAKE
                        column: order_id
                t_order_item:
                    actual-data-nodes: ds${
    
    0..2}.t_order_item
                    logicTable: t_order_item
                    database-strategy:
                        inline:
                            sharding-column: order_id
                            algorithm-expression: ds${
    
    order_id % 3}
                    keyGenerator:
                        type: SNOWFLAKE
                        column: item_id
            binding-tables:
                - t_order, t_order_item

1.5、广播表

指所有的分片数据源中都存在的表,表结构以及表中的数据在每个数据库中均完全一致。适用于数据量不大且需要与海量数据的表进行关联查询的场景,例如:字典表。

spring:
    shardingsphere:
        sharding:
            tables:
                t_config:
                    actual-data-nodes: ds${
    
    0..2}.t_config
            broadcast-tables: 
            		- t_config

1.6、单表

指所有的分片数据源中仅唯一存在的表。适用于数据量不大且无需分片的表。

二、数据节点

数据分片的最小单元,由数据源名称和真实表组成。例:ds_0.t_order_0。逻辑表与真实表的映射关系,可以分为均匀分布和自定义分布两种形式。

2.1、均匀分布

指真实表在每个数据源内呈现均匀分布的态势,例如:

db0
  ├── t_order0
  └── t_order1
db1
  ├── t_order0
  └── t_order1

数据节点的配置如下:

db0.t_order0, db0.t_order1, db1.t_order0, db1.t_order1

2.2、自定义分布

指真实表呈现特定规则的分布,例如:

db0
  ├── t_order0
  └── t_order1
db1
  ├── t_order2
  ├── t_order3
  └── t_order4

数据节点的配置如下:

db0.t_order0, db0.t_order1, db1.t_order2, db1.t_order3, db1.t_order4

三、分片

3.1、分片键

用于将数据库(表)水平拆分的数据库字段。例如:按照订单表中的订单 id 取模分片,则订单 id 就是分片键。SQL 中如果没有用于分片的字段,将执行全路由,性能较差。Apache ShardingSphere 支持根据单字段、多字段进行分片。

3.2、分片算法

用于将数据分片的算法。分片算法可以使用 Apache ShardingSphere 内置的分片算法语法糖,也可以由开发者自定义分片算法。

  • 自动化分片算法:分片算法语法糖,用于便捷的托管所有数据节点,使用者无需关注真实表的物理分布。包括取模、哈希、范围、时间等常用分片算法的实现。
  • 自定义分片算法:提供接口让开发者自己实现与业务紧密相关的分片算法,并允许开发者自行管理真实表的物理分布。
    • 标准分片算法:用于处理单字段作为分片键的 =,>,<,>=,<=,in, between and 进行分片的场景。
    • 复合分片算法:用于处理多字段作为分片键进行分片的场景。
    • Hint分片算法:用于处理使用 Hint 行分片的场景。

3.3、分片策略

包括分片键和分片算法,由于分片算法的独立性,将其独立抽离。真正可用于分片操作的是分片键+分片算法,也就是分片策略。

3.4、强制分片路由

对于分片字段并非是 SQL 字段而是其它外置条件决定的场景,可以使用 SQL Hint 注入分片值。比如,按照员工主键分库,但是数据库中并没有该字段。SQL Hint 支持通过 Java API 和 SQL 注释两种方式。

四、行表达式

在繁琐的数据分片规则配置中,随着数据节点的增多,大量的重复配置使得配置本身不易被维护。通过行表达式可以有效地简化数据节点配置工作量。

对于常见的分片算法,使用 Java 代码实现并不有助于配置的统一管理。通过行表达式书写分片算法,可以有效地将规则配置一同存放,更加易于浏览与存储。

只需要在配置中使用 ${expression} 或者 $->{expression} 标识行表达式即可。目前支持数据节点和分片算法这两个部分的配置。行表达式的内容使用的是 Groovy 的语法,Groovy 能够支持的所有操作,行表达式均能够支持。例如:${begin..end} :表示范围区间;${[unit1, unit2, unit3]} :表示枚举值;如果连续出现多个行表达式,整个表达式最终的结果将会根据每个子表达式的结果进行笛卡尔组合

e.g. ${
    
    ['online', 'offline']}_table${
    
    1..2} ,则最终解析为 online_table1,online_table2,offline_table1,offline_table2

五、分布式主键

Apache ShardingSphere 不仅提供了内置的分布式主键生成器,例如 UUID、SNOWFLAKE,还抽离出分布式主键生成器的接口,方便用户自行实现自定义的自增主键生成器。

5.1、UUID

使用 UUID.randomUUID() 的方式。

5.2、SNOWFLAKE

使用 Twitter 的雪花算法(snowflake)生成 64 bit 的长整型数据,包括 1 bit 符号位、41bit 时间戳位、10 bit 工作进程位、12 bit 序列号位。

符号位:预留,恒为零。

时间戳位:约等于 69.73 年。ShardingSphere 的雪花算法的时间纪元从 2016 年11月1日零点开始,可以使用到 2086 年。

工作进程位:对应每个工作进程 id。

序列号位:在一毫秒内生成的 id。如果一毫秒内生成的数量超过了 4096,则生成器会等待到下个毫秒继续生成。

5.3、时钟回拨

服务器时钟回拨会导致产生重复序列,因此默认分布式主键生成器提供了一个最大容忍的时钟回拨毫秒数。 如果时钟回拨的时间超过最大容忍的毫秒数阈值,则程序报错;如果在可容忍的范围内,默认分布式主键生成器会等待时钟同步到最后一次主键生成的时间后再继续工作。 最大容忍的时钟回拨毫秒数的默认值为 0,可通过属性设置。

六、分片策略

每个表最多可以指定一个分片策略。

6.1、行表达式分片策略

对应 InlineShardingStrategy。使用 Groovy 的表达式,提供对 SQL 语句中的 = 和 in 的分片操作的支持,只支持单分片键。

${begin..end} :表示范围区间

${[unit1, unit2, unit3]} :表示枚举值

行表达式如果连续出现多个行表达式,整个表达式最终的结果将会根据每个子表达式的结果进行笛卡尔组合。比如 ${['online', 'offline']}_table${1..2} ,则最终解析为 online_table1,online_table2,offline_table1,offline_table2

java api 方式 - 数据分片

/**
 * 数据分片
 */
public class ShardingJdbcDemo {
    
    

    public static void main(String[] args) {
    
    
        Map<String, DataSource> dataSourceMap = new HashMap<>();
        // 配置数据源0
        DruidDataSource dataSource0 = new DruidDataSource();
        dataSource0.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource0.setUrl("jdbc:mysql://10.211.55.6:3306/ds0");
        dataSource0.setUsername("root");
        dataSource0.setPassword("root");
        dataSourceMap.put("ds0", dataSource0);
        // 配置数据源1
        DruidDataSource dataSource1 = new DruidDataSource();
        dataSource1.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource1.setUrl("jdbc:mysql://10.211.55.14:3306/ds1");
        dataSource1.setUsername("root");
        dataSource1.setPassword("root");
        dataSourceMap.put("ds1", dataSource1);
        // 配置数据源2
        DruidDataSource dataSource2 = new DruidDataSource();
        dataSource2.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource2.setUrl("jdbc:mysql://10.211.55.15:3306/ds2");
        dataSource2.setUsername("root");
        dataSource2.setPassword("root");
        dataSourceMap.put("ds2", dataSource2);
        // 配置表规则
        TableRuleConfiguration orderRuleConfiguration = new TableRuleConfiguration("t_order", "ds${0..2}.t_order");
        // 配置分库策略
        InlineShardingStrategyConfiguration databaseShardingStrategyConfiguration = new InlineShardingStrategyConfiguration("order_id", "ds${order_id % 3}");
        orderRuleConfiguration.setDatabaseShardingStrategyConfig(databaseShardingStrategyConfiguration);
        // 配置分表策略
        InlineShardingStrategyConfiguration tableShardingStrategyConfiguration = new InlineShardingStrategyConfiguration("order_id", "t_order");
        orderRuleConfiguration.setTableShardingStrategyConfig(tableShardingStrategyConfiguration);
        // 配置分片规则
        ShardingRuleConfiguration shardingRuleConfiguration = new ShardingRuleConfiguration();
        // 添加表规则
        shardingRuleConfiguration.getTableRuleConfigs().add(orderRuleConfiguration);
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        try {
    
    
            DataSource dataSource = ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfiguration, new Properties());
            final String sql = "select * from t_order where order_id = ?";
            connection = dataSource.getConnection();
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setLong(1, 1);
            ResultSet resultSet = preparedStatement.executeQuery();
            while (resultSet.next()) {
    
    
                System.out.println("order_id >>> " + resultSet.getLong(1));
                System.out.println("user_id >>> " + resultSet.getLong(2));
            }
        } catch (SQLException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            try {
    
    
                if (preparedStatement != null) {
    
    
                    preparedStatement.close();
                }
            } catch (SQLException e) {
    
    
                e.printStackTrace();
            }
            try {
    
    
                if (connection != null) {
    
    
                    connection.close();
                }
            } catch (SQLException e) {
    
    
                e.printStackTrace();
            }
        }


    }
}

Spring Boot yml 方式 - 数据分片

spring:
    application:
        name: shardingsphere-demo
    shardingsphere:
        datasource:
            names: ds0,ds1,ds2
            ds0:
                type: com.alibaba.druid.pool.DruidDataSource
                driverClassName: com.mysql.cj.jdbc.Driver
                url: jdbc:mysql://10.211.55.6:3306/ds0
                username: root
                password: root
            ds1:
                type: com.alibaba.druid.pool.DruidDataSource
                driverClassName: com.mysql.cj.jdbc.Driver
                url: jdbc:mysql://10.211.55.14:3306/ds1
                username: root
                password: root
            ds2:
                type: com.alibaba.druid.pool.DruidDataSource
                driverClassName: com.mysql.cj.jdbc.Driver
                url: jdbc:mysql://10.211.55.15:3306/ds2
                username: root
                password: root
        sharding:
            tables:
                t_order:
                    actualDataNodes: ds${
    
    0..2}.t_order
                    logicTable: t_order
                    databaseStrategy:
                        inline:
                            sharding-column: order_id
                            algorithm-expression: ds${
    
    order_id % 3}

6.2、标准分片策略

对应 StandardShardingStrategy。提供对 SQL 语句中的 =、>、 <、 >=、<=、in、between and 的分片操作支持。只支持单分片键。标准分片策略提供了 PreciseShardingAlgorithm、RangeShardingAlgorithm 两种分片算法。PreciseShardingAlgorithm 是必选的,RangeShardingAlgorithm 是可选的。

spring:
	shardingsphere:
		sharding:
			tables:
				t_order: 
					actualDataNodes: ds$->{
    
    0..2}.t_order
          logicTable: t_order
          databaseStrategy:
          	standard:
            	shardingColumn: order_id
            	preciseAlgorithmClassName: com.shardingsphere.demo.algorithm.MyPreciseShardingAlgorithm
              rangeAlgorithmClassName: com.shardingsphere.demo.algorithm.MyRangeShardingAlgorithm

6.3、复合分片策略

对应 ComplexShardingStrategy。提供对 SQL语句中的 =、>、<、>=、<=、in、between and 的分片操作支持。支持多分片键。

spring:
	shardingsphere:
		sharding:
			tables:
				t_order: 
					actualDataNodes: ds$->{
    
    0..2}.t_order
          logicTable: t_order
          databaseStrategy:
          	complex:
            	shardingColumns: order_id, user_id
            	algorithmClassName: com.shardingsphere.demo.algorithm.MyComplexKeysShardingAlgorithm

6.4、Hint分片策略

对应 HintShardingStrategy。通过 Hint 指定分片值而非从 SQL 中提取分片值的方式进行分片的策略。

spring:
	shardingsphere:
		sharding:
			tables:
				t_order: 
					actualDataNodes: ds$->{
    
    0..2}.t_order
          logicTable: t_order
          databaseStrategy:
          	hint:
            	algorithmClassName: com.mzs.shardingsphere.demo.algorithm.MyHintShardingAlgorithm

6.5、不分片策略

对应NoneShardingStrategy。不分片的策略。

spring:
	shardingsphere:
		sharding:
			tables:
				t_order: 
					actualDataNodes: ds0.t_order
          logicTable: t_order
          databaseStrategy:
          	none:

七、分片算法

7.1、精确分片算法

对应 PreciseShardingAlgorithm。用于处理单分片键进行 = 、in 操作分片的场景。

public class MyPreciseShardingAlgorithm implements PreciseShardingAlgorithm<Long> {
    
    
  
  	private static final Logger LOGGER = LoggerFactory.getLogger(MyPreciseShardingAlgorithm.class);

    private static final String PREFIX = "ds";

    /**
     * Sharding.
     *
     * @param availableTargetNames available data sources or tables's names
     * @param shardingValue        sharding value
     * @return sharding result for data source or table's name
     */
    @Override
    public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingValue) {
    
    
        LOGGER.info("available target names >>> {}", availableTargetNames);
        String targetDatabase = PREFIX + shardingValue.getValue() % availableTargetNames.size();
        Optional<String> optional = availableTargetNames.stream().filter(targetDatabase::equals).findFirst();
        if (optional.isPresent()) {
    
    
            return targetDatabase;
        }
        throw new IllegalStateException(targetDatabase +" target name is not available.");
    }
}

7.4、范围分片算法

对应 RangeShardingAlgorithm。用于处理单分片键进行 between and、>、<、>=、<= 操作分片的场景。如果不配置 RangeShardingAlgorithm,SQL 中的 between and 将按照全库路由处理。

public class MyRangeShardingAlgorithm implements RangeShardingAlgorithm<Long> {
    
    

    private static final Logger LOGGER = LoggerFactory.getLogger(MyRangeShardingAlgorithm.class);

    private static final String PREFIX = "ds";

    private static final Long STEP = 100L;

    private static final Long INITIAL_END_POINT = 1L;

    /**
     * Sharding.
     *
     * @param availableTargetNames available data sources or tables's names
     * @param shardingValue        sharding value
     * @return sharding results for data sources or tables's names
     */
    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Long> shardingValue) {
    
    
        LOGGER.info("available target names >>> {}", availableTargetNames);
        Collection<String> dataSources = new LinkedList<>();
        Range<Long> valueRange = shardingValue.getValueRange();
        boolean hasLowerBound = valueRange.hasLowerBound();
        boolean hasUpperBound = valueRange.hasUpperBound();
        LOGGER.info("has upper bound - {}, has lower bound - {} ", hasUpperBound, hasLowerBound);
        Long lowerEndPoint = INITIAL_END_POINT;
        Long upperEndPoint = INITIAL_END_POINT;
        if (hasLowerBound && !hasUpperBound) {
    
    
            lowerEndPoint = valueRange.lowerEndpoint();
            upperEndPoint = Math.min(lowerEndPoint + STEP, Long.MAX_VALUE);
        }
        if (!hasLowerBound && hasUpperBound) {
    
    
            upperEndPoint = valueRange.upperEndpoint();
            lowerEndPoint = Math.max(upperEndPoint - STEP, INITIAL_END_POINT);
        }
        if (hasLowerBound && hasUpperBound) {
    
    
            lowerEndPoint = valueRange.lowerEndpoint();
            upperEndPoint = valueRange.upperEndpoint();
        }
        for (Long i = lowerEndPoint; i <= upperEndPoint; i++) {
    
    
            String dataSource = PREFIX + (i % availableTargetNames.size());
            dataSources.add(dataSource);
        }
        return dataSources;
    }
}

7.5、复合分片算法

对应 ComplexKeysShardingAlgorithm。用于处理使用多键作为分片键进行分片的场景。

public class MyComplexKeysShardingAlgorithm implements ComplexKeysShardingAlgorithm<Long> {
    
    

    private static final String SHARDING_KEY = "order_id";

    private static final String SHARDING_KEY2 = "user_id";

    private static final String PREFIX = "ds";

    /**
     * Sharding.
     *
     * @param availableTargetNames available data sources or tables's names
     * @param shardingValue        sharding value
     * @return sharding results for data sources or tables's names
     */
    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, ComplexKeysShardingValue<Long> shardingValue) {
    
    
        Collection<String> targetDataSources = new LinkedList<>();
        Map<String, Collection<Long>> columnNameAndShardingValuesMap = shardingValue.getColumnNameAndShardingValuesMap();
        if (columnNameAndShardingValuesMap.containsKey(SHARDING_KEY)) {
    
    
            List<Long> orderIdShardingValues = new LinkedList<>(columnNameAndShardingValuesMap.get(SHARDING_KEY));
            if (columnNameAndShardingValuesMap.containsKey(SHARDING_KEY2)) {
    
    
                List<Long> userIdShardingValues = new LinkedList<>(columnNameAndShardingValuesMap.get(SHARDING_KEY2));
                for (Long orderIdShardingValue : orderIdShardingValues) {
    
    
                    if (orderIdShardingValue % 2 == 1) {
    
    
                        targetDataSources.add(PREFIX + 2);
                        continue;
                    }
                    for (Long userIdShardingValue : userIdShardingValues) {
    
    
                        targetDataSources.add(PREFIX + (userIdShardingValue / 2 % 2));
                    }
                }
            }
            return availableTargetNames;
        }
        return targetDataSources;
    }
}

7.6、Hint分片算法

对应 HintShardingAlgorithm。用于处理使用 Hint 行分片的场景。

在一些场景中,分片条件不存在于 SQL 中,而是存在于外部业务逻辑。ShardingSphere 提供了一种通过外部指定分片结果的方式,叫做 Hint。

实现机制:ShardingSphere 使用 ThreadLocal 管理分片键值。可以通过编程的方式向 HintManager 中添加分片条件,该分片条件仅仅在当前线程中生效。

在数据库的 crud 操作的前面加上如下几行代码:

HintManager.clear();
try (HintManager hintManager = HintManager.getInstance()) {
    
    
  hintManager.setDatabaseShardingValue(0L); // 指定分片值
	// hintManager.addDatabaseShardingValue("t_order", 0L);	// 指定逻辑表名与分片值
	// hintManager.addTableShardingValue("t_order", 0L);		// 指定逻辑表名与分片值
  // hintManager.setMasterRouteOnly();										// 强制从MySQL的主节点读取数据
  // 执行数据库的 crud 操作
}
public class MyHintShardingAlgorithm implements HintShardingAlgorithm<Long> {
    
    

    private static final String PREFIX = "ds";

    /**
     * Sharding.
     *
     * <p>sharding value injected by hint, not in SQL.</p>
     *
     * @param availableTargetNames available data sources or tables's names
     * @param shardingValue        sharding value
     * @return sharding result for data sources or tables's names
     */
    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, HintShardingValue<Long> shardingValue) {
    
    
        Collection<String> dataSourceNames = new ArrayList<>();
      	// 遍历上述的 HintManager 指定的分片值
        for (Long value : shardingValue.getValues()) {
    
    	
            dataSourceNames.add(PREFIX + (value % 3));
        }
        return dataSourceNames;
    }
}

八、读写分离

java api 方式- 读写分离

/**
 * 读写分离
 */
public class ShardingJdbcDemo2 {
    
    

    public static void main(String[] args) {
    
    
        Map<String, DataSource> dataSourceMap = new HashMap<>();
        // 配置数据源1
        DruidDataSource dataSource1 = new DruidDataSource();
        dataSource1.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource1.setUrl("jdbc:mysql://10.211.55.7:3306/ds3");
        dataSource1.setUsername("root");
        dataSource1.setPassword("root");
        dataSourceMap.put("master0", dataSource1);
        // 配置数据源2
        DruidDataSource dataSource2 = new DruidDataSource();
        dataSource2.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource2.setUrl("jdbc:mysql://10.211.55.8:3306/ds3");
        dataSource2.setUsername("root");
        dataSource2.setPassword("root");
        dataSourceMap.put("slave0", dataSource2);

        MasterSlaveRuleConfiguration masterSlaveRuleConfiguration = new MasterSlaveRuleConfiguration(
                "master_slave_shard", "master0", Collections.singletonList("slave0"));

        Connection connection = null;
        PreparedStatement preparedStatement = null;
        try {
    
    
            DataSource dataSource = MasterSlaveDataSourceFactory.createDataSource(dataSourceMap, masterSlaveRuleConfiguration, new Properties());
            String sql = "select * from user_info where user_id = ?";
            connection = dataSource.getConnection();
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setLong(1, 1);
            ResultSet resultSet = preparedStatement.executeQuery();
            while (resultSet.next()) {
    
    
                System.out.println("user_id >>> " + resultSet.getLong(1));
                System.out.println("user_name >>> " + resultSet.getString(2));
            }
        } catch (SQLException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            try {
    
    
                if (preparedStatement != null) {
    
    
                    preparedStatement.close();
                }
            } catch (SQLException e) {
    
    
                e.printStackTrace();
            }
            try {
    
    
                if (connection != null) {
    
    
                    connection.close();
                }
            } catch (SQLException e) {
    
    
                e.printStackTrace();
            }
        }
    }
}

Spring Boot yml 方式 - 读写分离

spring:
    shardingsphere:
        datasource:
            names: master0,slave0
            master0:
                type: com.alibaba.druid.pool.DruidDataSource
                driver-class-name: com.mysql.cj.jdbc.Driver
                url: jdbc:mysql://10.211.55.7:3306/ds3
                username: root
                password: root
            slave0:
                type: com.alibaba.druid.pool.DruidDataSource
                driver-class-name: com.mysql.cj.jdbc.Driver
                url: jdbc:mysql://10.211.55.8:3306/ds3
                username: root
                password: root
        sharding:
            tables:
                user_info:
                    actualDataNodes: master0.user_info
                    databaseStrategy:
                        inline:
                            sharding-column: user_id
                            algorithm-expression: master0
                    keyGenerator:
                        type: SNOWFLAKE
                        column: user_id
            master-slave-rules:
                master0:
                    master-data-source-name: master0
                    slave-data-source-names: slave0

九、分页

完全支持 MySQL、PostgreSQL 和 Oracle 的分页查询,SQLServer 由于分页查询较为复杂,仅部分支持。

详情查看 ShardingSphere4.x版本分页

十、SQL 使用规范

详情查看 ShardingSphere4.x版本SQL规范

十一、分布式事务

首先需要导入如下依赖:

<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
    <version>${shardingsphere.version}</version>
</dependency>

<!-- 使用XA事务时,需要引入此模块 -->
<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>sharding-transaction-xa-core</artifactId>
    <version>${shardingsphere.version}</version>
</dependency>

<!-- 使用BASE事务时,需要引入此模块 -->
<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>sharding-transaction-base-seata-at</artifactId>
    <version>${sharding-sphere.version}</version>
</dependency>

然后配置 Spring Boot 的事务管理器。

@Configuration
@EnableTransactionManagement
public class TransactionConfiguration {
    
    
    
    @Bean
    public PlatformTransactionManager txManager(final DataSource dataSource) {
    
    
        return new DataSourceTransactionManager(dataSource);
    }
    
    @Bean
    public JdbcTemplate jdbcTemplate(final DataSource dataSource) {
    
    
        return new JdbcTemplate(dataSource);
    }
}

最后在业务代码中使用分布式事务。

@Transactional
@ShardingTransactionType(TransactionType.XA)  // 支持TransactionType.LOCAL, TransactionType.XA, TransactionType.BASE
public void insert() {
    
    
    jdbcTemplate.execute("INSERT INTO t_order (user_id, status) VALUES (?, ?)", (PreparedStatementCallback<Object>) preparedStatement -> {
    
    
        preparedStatement.setObject(1, i);
        preparedStatement.setObject(2, "init");
        preparedStatement.executeUpdate();
    });
}

十二、本地事务

对应 TransactionType.LOCAL。

  • 完全支持非跨库事务,例如:仅分表,或者分库但是路由的结果在单库中。

  • 完全支持因逻辑夜场导致的跨库事务。例如:同一事务中,跨两个库更新。更新完毕后,抛出空指针,则两个库的内容都能回滚。

  • 不支持因网络、硬件异常导致的跨库事务。例如:同一事务中,跨两个库更新,更新完毕后、未提交之前,第一个库宕机,则只有第二个库的数据提交。

十三、两阶段事务-XA

对应 TransactionType.XA。

ShardingSpere 默认的 XA 事务管理器是 Atomikos,在项目的 logs 目录中会生成 xa_tx.log 文件,用于崩溃恢复的。

也可以在项目的 classpath 中添加 jta.properties 来定制化 Atomikos 配置项。具体可以参考Atomikos 官方文档。

  • 支持数据分片后的跨库XA事务
  • 两阶段提交保证操作的原子性和数据的强一致性
  • 服务宕机重启后,提交/回滚中的事务可自动恢复
  • SPI机制整合主流的XA事务管理器,默认Atomikos,可以选择使用Narayana和Bitronix
  • 同时支持XA和非XA的连接池
  • 提供spring-boot和namespace的接入端
  • 不支持服务宕机后,在其它机器上恢复提交/回滚中的数据

十四、柔性事务

对应 TransactionType.BASE。

  • 完全支持跨库分布式事务

  • 支持RC隔离级别

  • 通过undo快照进行事务回滚

  • 支持服务宕机后的,自动恢复提交中的事务

  • 需要额外部署Seata-server服务进行分支事务的协调

猜你喜欢

转载自blog.csdn.net/u011397981/article/details/131425450