Sharding-JDBC快速入门,环境搭建,实现,集成springboot

1. Sharding-JDBC快速入门

1.1 需求说明

人工创建两张表,t_order_1和t_order_2,这两张表是订单表拆分后的表,通过Sharding-Jdbc向订单表插入数据, 按照一定的分片规则,主键为偶数的进入t_order_1,另一部分数据进入t_order_2,通过Sharding-Jdbc 查询数

据,根据 SQL语句的内容从t_order_1或t_order_2查询数据。

1.2. 环境搭建

1.2.1 环境说明

操作系统:Win10

数据库:MySQL-5.7.25 JDK:64位 jdk1.8.0_201

应用框架:spring-boot-2.1.3.RELEASE,Mybatis3.5.0 Sharding-JDBC:sharding-jdbc-spring-boot-starter-4.0.0-RC1

1.2.2 创建数据库

创建订单库order_db

CREATE DATABASE `order_db` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';

在order_db中创建t_order_1、t_order_2表

DROP TABLE IF EXISTS `t_order_1`; 
CREATE TABLE `t_order_1` (
`order_id` bigint(20) NOT NULL COMMENT '订单id',
`price` decimal(10, 2) NOT NULL COMMENT '订单价格',
`user_id` bigint(20) NOT NULL COMMENT '下单用户id',
`status` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '订单状态',
PRIMARY KEY (`order_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

DROP TABLE IF EXISTS `t_order_2`; 
CREATE TABLE `t_order_2` (
`order_id` bigint(20) NOT NULL COMMENT '订单id',
`price` decimal(10, 2) NOT NULL COMMENT '订单价格',
`user_id` bigint(20) NOT NULL COMMENT '下单用户id',
`status` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '订单状态',
PRIMARY KEY (`order_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

1.2.3.引入maven依赖

引入 sharding-jdbc和SpringBoot整合的Jar包:

<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>sharding‐jdbc‐spring‐boot‐starter</artifactId>
    <version>4.0.0‐RC1</version>
</dependency>

具体spring boot相关依赖及配置请参考资料中sharding-jdbc-simple工程,本指引只说明与Sharding- JDBC相关的内容。

1.3 编写程序

1.3.1 分片规则配置

分片规则配置是sharding-jdbc进行对分库分表操作的重要依据,配置内容包括:数据源、主键生成策略、分片策 略等。

在application.properties中配置

server.port=56081

spring.application.name = sharding‐jdbc‐simple‐demo
server.servlet.context‐path = /sharding‐jdbc‐simple‐demo spring.http.encoding.enabled = true spring.http.encoding.charset = UTF‐8 spring.http.encoding.force = true

spring.main.allow‐bean‐definition‐overriding = true

mybatis.configuration.map‐underscore‐to‐camel‐case = true # 以下是分片规则配置
# 定义数据源
spring.shardingsphere.datasource.names = m1

spring.shardingsphere.datasource.m1.type = com.alibaba.druid.pool.DruidDataSource spring.shardingsphere.datasource.m1.driver‐class‐name = com.mysql.jdbc.Driver spring.shardingsphere.datasource.m1.url = jdbc:mysql://localhost:3306/order_db?useUnicode=true spring.shardingsphere.datasource.m1.username = root spring.shardingsphere.datasource.m1.password = root

# 指定t_order表的数据分布情况,配置数据节点
spring.shardingsphere.sharding.tables.t_order.actual‐data‐nodes = m1.t_order_$‐>{1..2}

# 指定t_order表的主键生成策略为SNOWFLAKE spring.shardingsphere.sharding.tables.t_order.key‐generator.column=order_id spring.shardingsphere.sharding.tables.t_order.key‐generator.type=SNOWFLAKE




# 指定t_order表的分片策略,分片策略包括分片键和分片算法spring.shardingsphere.sharding.tables.t_order.table‐strategy.inline.sharding‐column = order_id 
spring.shardingsphere.sharding.tables.t_order.table‐strategy.inline.algorithm‐expression = t_order_$‐>{order_id % 2 + 1}

# 打开sql输出日志
spring.shardingsphere.props.sql.show = true swagger.enable = true
logging.level.root = info logging.level.org.springframework.web = info logging.level.com.bigdata.dbsharding = debug 
logging.level.druid.sql = debug
  1. 首先定义数据源m1,并对m1进行实际的参数配置。

  2. 指定t_order表的数据分布情况,他分布在m1.t_order_1,m1.t_order_2

  3. 指定t_order表的主键生成策略为SNOWFLAKE,SNOWFLAKE是一种分布式自增算法,保证id全局唯一

  4. 定义t_order分片策略,order_id为偶数的数据落在t_order_1,为奇数的落在t_order_2,分表策略的表达式为t_order_$->{order_id % 2 + 1}

1.3.2. 数据操作

@Mapper 
@Component
public interface OrderDao {

    /**
    *新增订单
    *@param price 订单价格
    *@param userId 用户id
    *@param status 订单状态
    *@return
    */
    @Insert("insert into t_order(price,user_id,status) value(#{price},#{userId},#{status})") int insertOrder(@Param("price") BigDecimal price, @Param("userId")Long userId,
    @Param("status")String status);

    /**
    *根据id列表查询多个订单
    *@param orderIds 订单id列表
    *@return
    */ @Select({"<script>" +
    "select " + " * " +
    " from t_order t" +
    " where t.order_id in " +
    "<foreach collection='orderIds' item='id' open='(' separator=',' close=')'>" + " #{id} " +
    "</foreach>"+ "</script>"})
    List<Map> selectOrderbyIds(@Param("orderIds")List<Long> orderIds);

}

1.3.3. 测试

编写单元测试:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {ShardingJdbcSimpleDemoBootstrap.class}) 
public class OrderDaoTest {
    @Autowired
    private OrderDao orderDao; 
    @Test
    public void testInsertOrder(){ for (int i = 0 ; i<10; i++){
    	orderDao.insertOrder(new BigDecimal((i+1)*5),1L,"WAIT_PAY");
    }
    @Test
    public void testSelectOrderbyIds(){ 
        List<Long> ids = new ArrayList<>(); ids.add(373771636085620736L); 			             ids.add(373771635804602369L);
        List<Map> maps = orderDao.selectOrderbyIds(ids); System.out.println(maps);
    }
}

执行testInsertOrder:
在这里插入图片描述

通过日志可以发现order_id为奇数的被插入到t_order_2表,为偶数的被插入到t_order_1表,达到预期目标。执行testSelectOrderbyIds:

在这里插入图片描述

通过日志可以发现,根据传入order_id的奇偶不同,sharding-jdbc分别去不同的表检索数据,达到预期目标。

1.4. 流程分析

通过日志分析,Sharding-JDBC在拿到用户要执行的sql之后干了哪些事儿:

(1) 解析sql,获取片键值,在本例中是order_id

(2) Sharding-JDBC通过规则配置 t_order_$->{order_id % 2 + 1},知道了当order_id为偶数时,应该往t_order_1表插数据,为奇数时,往t_order_2插数据。

(3) 于是Sharding-JDBC根据order_id的值改写sql语句,改写后的SQL语句是真实所要执行的SQL语句。

(4) 执行改写后的真实sql语句

(5) 将所有真正执行sql的结果进行汇总合并,返回。

1.5. 其他集成方式

Sharding-JDBC不仅可以与spring boot良好集成,它还支持其他配置方式,共支持以下四种集成方式。

Spring Boot Yaml 配置

定义application.yml,内容如下:

server:
  port: 56081
  servlet:
    context-path: /sharding-jdbc-simple-demo
spring:
  application:
    name: sharding-jdbc-simple-demo
  http:
    encoding:
      enabled: true
      charset: utf-8
      force: true
  main:
    allow-bean-definition-overriding: true
  shardingsphere:
    datasource:
      names: m1
      m1:
        type: com.alibaba.druid.pool.DruidDataSource
        driverClassName: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/order_db?useUnicode=true
        username: root
        password: mysql
    sharding:
      tables:
        t_order:
          actualDataNodes: m1.t_order_$->{1..2}
          tableStrategy:
            inline:
              shardingColumn: order_id
              algorithmExpression: t_order_$->{order_id % 2 + 1}
          keyGenerator:
            type: SNOWFLAKE
            column: order_id
    props:
      sql:
        show: true
mybatis:
  configuration:
    map-underscore-to-camel-case: true
swagger:
  enable: true
logging:
  level:
    root: info
    org.springframework.web: info
    com.bigdata.dbsharding: debug
    druid.sql: debug

如果使用application.yml则需要屏蔽原来的application.properties文件。

Java 配置

添加配置类:

@Configuration
public class ShardingJdbcConfig {

   // 定义数据源
   Map<String, DataSource> createDataSourceMap() { 
       DruidDataSource dataSource1 = new DruidDataSource(); 		           dataSource1.setDriverClassName("com.mysql.jdbc.Driver");
       dataSource1.setUrl("jdbc:mysql://localhost:3306/order_db?useUnicode=true");            dataSource1.setUsername("root");
       dataSource1.setPassword("root");
       Map<String, DataSource> result = new HashMap<>(); result.put("m1", dataSource1);
       return result;
   }
// 定义主键生成策略
   private static KeyGeneratorConfiguration getKeyGeneratorConfiguration() { KeyGeneratorConfiguration result = new
   KeyGeneratorConfiguration("SNOWFLAKE","order_id"); return result;
   }

   // 定义t_order表的分片策略
   TableRuleConfiguration getOrderTableRuleConfiguration() {
       TableRuleConfiguration result = new TableRuleConfiguration("t_order","m1.t_order_$‐>
       {1..2}");
       result.setTableShardingStrategyConfig(new InlineShardingStrategyConfiguration("order_id", "t_order_$‐>{order_id % 2 + 1}"));
       result.setKeyGeneratorConfig(getKeyGeneratorConfiguration());

       return result;
   }
   // 定义sharding‐Jdbc数据源
   @Bean
   DataSource getShardingDataSource() throws SQLException {
       ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration(); shardingRuleConfig.getTableRuleConfigs().add(getOrderTableRuleConfiguration());
       //spring.shardingsphere.props.sql.show = true 
       Properties properties = new Properties(); 
       properties.put("sql.show","true");
       return ShardingDataSourceFactory.createDataSource(createDataSourceMap(),
       shardingRuleConfig,properties);
   }
}

由于采用了配置类所以需要屏蔽原来application.properties文件中spring.shardingsphere开头的配置信息。还需要在SpringBoot启动类中屏蔽使用spring.shardingsphere配置项的类:

@SpringBootApplication(exclude = {SpringBootConfiguration.class}) public class ShardingJdbcSimpleDemoBootstrap {	}

Spring Boot properties配置

此方式同快速入门程序。

# 定义数据源
spring.shardingsphere.datasource.names = m1

spring.shardingsphere.datasource.m1.type = com.alibaba.druid.pool.DruidDataSource spring.shardingsphere.datasource.m1.driver‐class‐name = com.mysql.jdbc.Driver spring.shardingsphere.datasource.m1.url = jdbc:mysql://localhost:3306/order_db?useUnicode=true spring.shardingsphere.datasource.m1.username = root spring.shardingsphere.datasource.m1.password = root


# 指定t_order表的主键生成策略为SNOWFLAKE 
spring.shardingsphere.sharding.tables.t_order.key‐generator.column=order_id spring.shardingsphere.sharding.tables.t_order.key‐generator.type=SNOWFLAKE

# 指定t_order表的数据分布情况
spring.shardingsphere.sharding.tables.t_order.actual‐data‐nodes = m1.t_order_$‐>{1..2}

# 指定t_order表的分表策略
spring.shardingsphere.sharding.tables.t_order.table‐strategy.inline.sharding‐column = order_id 
spring.shardingsphere.sharding.tables.t_order.table‐strategy.inline.algorithm‐expression = t_order_$‐>{order_id % 2 + 1}
Spring命名空间配置

此方式使用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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:sharding="http://shardingsphere.apache.org/schema/shardingsphere/sharding"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring‐beans.xsd http://shardingsphere.apache.org/schema/shardingsphere/sharding
http://shardingsphere.apache.org/schema/shardingsphere/sharding/sharding.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring‐context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring‐tx.xsd">
    <context:annotation‐config />

    <!‐‐定义多个数据源‐‐>
    <bean id="m1" class="com.alibaba.druid.pool.DruidDataSource" destroy‐method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/order_db_1?useUnicode=true" />
        <property name="username" value="root" />
        <property name="password" value="root" />
    </bean>

    <!‐‐定义分库策略‐‐>
    <sharding:inline‐strategy id="tableShardingStrategy" sharding‐column="order_id" algorithm‐ expression="t_order_$‐>{order_id % 2 + 1}" />

    <!‐‐定义主键生成策略‐‐>
    <sharding:key‐generator id="orderKeyGenerator" type="SNOWFLAKE" column="order_id" />

    <!‐‐定义sharding‐Jdbc数据源‐‐>
    <sharding:data‐source id="shardingDataSource">
        <sharding:sharding‐rule data‐source‐names="m1">
        <sharding:table‐rules>
        <sharding:table‐rule logic‐table="t_order" table‐strategy‐ ref="tableShardingStrategy" key‐generator‐ref="orderKeyGenerator" />
        </sharding:table‐rules>
        </sharding:sharding‐rule>
    </sharding:data‐source>
</beans>

2. 水平分表

前面已经介绍过,水平分表是在同一个数据库内,把同一个表的数据按一定规则拆到多个表中。在快速入门里,我 们已经对水平分库进行实现,这里不再重复介绍。

3. 水平分库

前面已经介绍过,水平分库是把同一个表的数据按一定规则拆到不同的数据库中,每个库可以放在不同的服务器 上。接下来看一下如何使用Sharding-JDBC实现水平分库,咱们继续对快速入门中的例子进行完善。

(1) 将原有order_db库拆分为order_db_1、order_db_2

在这里插入图片描述

(2) 分片规则修改

由于数据库拆分了两个,这里需要配置两个数据源。

分库需要配置分库的策略,和分表策略的意义类似,通过分库策略实现数据操作针对分库的数据库进行操作。

# 定义多个数据源
spring.shardingsphere.datasource.names = m1,m2

spring.shardingsphere.datasource.m1.type = com.alibaba.druid.pool.DruidDataSource spring.shardingsphere.datasource.m1.driver‐class‐name = com.mysql.jdbc.Driver spring.shardingsphere.datasource.m1.url = jdbc:mysql://localhost:3306/order_db_1?useUnicode=true spring.shardingsphere.datasource.m1.username = root spring.shardingsphere.datasource.m1.password = root

spring.shardingsphere.datasource.m2.type = com.alibaba.druid.pool.DruidDataSource spring.shardingsphere.datasource.m2.driver‐class‐name = com.mysql.jdbc.Driver spring.shardingsphere.datasource.m2.url = jdbc:mysql://localhost:3306/order_db_2?useUnicode=true spring.shardingsphere.datasource.m2.username = root spring.shardingsphere.datasource.m2.password = root
...
# 分库策略,以user_id为分片键,分片策略为user_id % 2 + 1,user_id为偶数操作m1数据源,否则操作m2。spring.shardingsphere.sharding.tables.t_order.database‐strategy.inline.sharding‐column = user_id 
spring.shardingsphere.sharding.tables.t_order.database‐strategy.inline.algorithm‐expression = m$‐>{user_id % 2 + 1}

分库策略定义方式如下:

#分库策略,如何将一个逻辑表映射到多个数据源
spring.shardingsphere.sharding.tables.<逻辑表名称>.database‐strategy.<分片策略>.<分片策略属性名>=   #
分片策略属性值

#分表策略,如何将一个逻辑表映射为多个实际表
spring.shardingsphere.sharding.tables.<逻辑表名称>.table‐strategy.<分片策略>.<分片策略属性名>= #分片策略属性值

Sharding-JDBC支持以下几种分片策略:

不管理分库还是分表,策略基本一样。

  • standard:标准分片策略,对应StandardShardingStrategy。提供对SQL语句中的=, IN和BETWEEN AND的分片操作支持。StandardShardingStrategy只支持单分片键,提供PreciseShardingAlgorithm和RangeShardingAlgorithm两个分片算法。PreciseShardingAlgorithm是必选的,用于处理=和IN的分片。RangeShardingAlgorithm是可选的,用于处理BETWEEN AND分片,如果不配置RangeShardingAlgorithm,SQL中的BETWEEN AND将按照全库路由处理。
  • complex:符合分片策略,对应ComplexShardingStrategy。复合分片策略。提供对SQL语句中的=, IN和BETWEEN AND的分片操作支持。ComplexShardingStrategy支持多分片键,由于多分片键之间的关系复杂,因此并未进行过多的封装,而是直接将分片键值组合以及分片操作符透传至分片算法,完全由应用开发 者实现,提供最大的灵活度。
  • inline:行表达式分片策略,对应InlineShardingStrategy。使用Groovy的表达式,提供对SQL语句中的=和 IN的分片操作支持,只支持单分片键。对于简单的分片算法,可以通过简单的配置使用,从而避免繁琐的Java

​ 代码开发,如:t_user_$->{u_id % 8}表示t_user表根据u_id模8,而分成8张表,表名称为t_user_0到t_user_7。

  • hint:Hint分片策略,对应HintShardingStrategy。通过Hint而非SQL解析的方式分片的策略。对于分片字段 非SQL决定,而由其他外置条件决定的场景,可使用SQL Hint灵活的注入分片字段。例:内部系统,按照员工登录主键分库,而数据库中并无此字段。SQL Hint支持通过Java API和SQL注释(待实现)两种方式使用。
  • none:不分片策略,对应NoneShardingStrategy。不分片的策略。
    目前例子中都使用inline分片策略,若对其他分片策略细节若感兴趣,请查阅官方文档:
    https://shardingsphere.apache.org/

(3) 插入测试

修改testInsertOrder方法,插入数据中包含不同的user_id

@Test
public void testInsertOrder(){ 
    for (int i = 0 ; i<10; i++){
		orderDao.insertOrder(new BigDecimal((i+1)*5),1L,"WAIT_PAY");
	}
	for (int i = 0 ; i<10; i++){
		orderDao.insertOrder(new BigDecimal((i+1)*10),2L,"WAIT_PAY");
	}
}

执行testInsertOrder:

通过日志可以看出,根据user_id的奇偶不同,数据分别落在了不同数据源,达到目标。

(4) 查询测试

调用快速入门的查询接口进行测试:

List<Map> selectOrderbyIds(@Param("orderIds")List<Long> orderIds);

通过日志发现,sharding-jdbc将sql路由到m1和m2:

在这里插入图片描述

问题分析:

由于查询语句中并没有使用分片键user_id,所以sharding-jdbc将广播路由到每个数据结点。下边我们在sql中添加分片键进行查询。

在OrderDao中定义接口:

@Select({"<script>",
" select",
" * ",
" from t_order t ", "where t.order_id in",
"<foreach collection='orderIds' item='id' open='(' separator=',' close=')'>", "#{id}",
"</foreach>",
" and t.user_id = #{userId} ", "</script>"
})
List<Map> selectOrderbyUserAndIds(@Param("userId") Integer userId,@Param("orderIds")List<Long> orderIds);

编写测试方法:

@Test
public void testSelectOrderbyUserAndIds(){ 
    List<Long> orderIds = new ArrayList<>(); 
    orderIds.add(373422416644276224L); orderIds.add(373422415830581248L);
	//查询条件中包括分库的键user_id
	int user_id = 1;
	List<Map> orders = orderDao.selectOrderbyUserAndIds(user_id,orderIds); 
    JSONArray 	jsonOrders = new JSONArray(orders); 
    System.out.println(jsonOrders);
}

执行testSelectOrderbyUserAndIds:
在这里插入图片描述

查询条件user_id为1,根据分片策略m$->{user_id % 2 + 1}计算得出m2,此sharding-jdbc将sql路由到m2,见上图日志。

4. 垂直分库

前面已经介绍过,垂直分库是指按照业务将表进行分类,分布到不同的数据库上面,每个库可以放在不同的服务器 上,它的核心理念是专库专用。接下来看一下如何使用Sharding-JDBC实现垂直分库。

(1) 创建数据库

创建数据库user_db

CREATE DATABASE `user_db` CHARACTER SET 'utf8' COLLATE 'utf8_general_ci';

在user_db中创建t_user表

DROP TABLE IF EXISTS `t_user`; 
CREATE TABLE `t_user` (
`user_id` bigint(20) NOT NULL COMMENT '用户id',
`fullname` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户姓名',
`user_type` char(1) DEFAULT NULL COMMENT '用户类型',
PRIMARY KEY (`user_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

(2) 在Sharding-JDBC规则中修改

# 新增m0数据源,对应user_db 
spring.shardingsphere.datasource.names = m0,m1,m2
...
spring.shardingsphere.datasource.m0.type = com.alibaba.druid.pool.DruidDataSource

spring.shardingsphere.datasource.m0.driver‐class‐name = com.mysql.jdbc.Driver
spring.shardingsphere.datasource.m0.url = jdbc:mysql://localhost:3306/user_db?useUnicode=true 
spring.shardingsphere.datasource.m0.username = root spring.shardingsphere.datasource.m0.password = root

....
# t_user分表策略,固定分配至m0的t_user真实表spring.shardingsphere.sharding.tables.t_user.actual‐data‐nodes = m$‐>{0}.t_user spring.shardingsphere.sharding.tables.t_user.table‐strategy.inline.sharding‐column = user_id 
spring.shardingsphere.sharding.tables.t_user.table‐strategy.inline.algorithm‐expression = t_user

(3) 数据操作

新增UserDao:

@Mapper @Component
public interface UserDao {

/**
*新增用户
*@param userId 用户id
*@param fullname 用户姓名
*@return
*/
@Insert("insert into t_user(user_id, fullname) value(#{userId},#{fullname})") int insertUser(@Param("userId")Long userId,@Param("fullname")String fullname);

/**
*根据id列表查询多个用户
*@param userIds 用户id列表
*@return
*/ 
@Select({"<script>",
" select",
" * ",
" from t_user t ",
" where t.user_id in",
"<foreach collection='userIds' item='id' open='(' separator=',' close=')'>", "#{id}",
"</foreach>", "</script>"
})
List<Map> selectUserbyIds(@Param("userIds")List<Long> userIds);

}

(4) 测试

新增单元测试方法:

@Test
public void testInsertUser(){ 
    for (int i = 0 ; i<10; i++){
		Long id = i + 1L; 
        userDao.insertUser(id,"姓名"+ id );
	}

}
@Test
public void testSelectUserbyIds(){ 
    List<Long> userIds = new ArrayList<>(); 
    userIds.add(1L);
	userIds.add(2L);
	List<Map> users = userDao.selectUserbyIds(userIds); 
    System.out.println(users);
}

执行testInsertUser:
在这里插入图片描述

通过日志可以看出t_user表的数据被落在了m0数据源,达到目标。执行testSelectUserbyIds:

在这里插入图片描述

通过日志可以看出t_user表的查询操作被落在了m0数据源,达到目标。

5. 公共表

公共表属于系统中数据量较小,变动少,而且属于高频联合查询的依赖表。参数表、数据字典表等属于此类型。可 以将这类表在每个数据库都保存一份,所有更新操作都同时发送到所有分库执行。接下来看一下如何使用Sharding-JDBC实现公共表。

(1) 创建数据库

分别在user_db、order_db_1、order_db_2中创建t_dict表:

CREATE TABLE `t_dict` (
`dict_id` bigint(20) NOT NULL COMMENT '字典id',
`type` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '字典类型',
`code` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '字典编码',
`value` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '字典值',
PRIMARY KEY (`dict_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

(2) 在Sharding-JDBC规则中修改

# 指定t_dict为公共表
spring.shardingsphere.sharding.broadcast‐tables=t_dict

(3) 数据操作

新增DictDao:

@Mapper 
@Component
public interface DictDao {

    /**
    *新增字典
    *@param type 字典类型
    *@param code 字典编码
    *@param value 字典值
    *@return
    */
    @Insert("insert into t_dict(dict_id,type,code,value) value(#{dictId},#{type},#{code},#
    {value})")
    int insertDict(@Param("dictId") Long dictId,@Param("type") String type, @Param("code")String code, @Param("value")String value);

    /**
    *删除字典
    *@param dictId 字典id
    *@return
    */
	@Delete("delete from t_dict where dict_id = #{dictId}") 
    int deleteDict(@Param("dictId") Long dictId);

}

(4) 字典操作测试

新增单元测试方法:

@Test
public void testInsertDict(){ 
    dictDao.insertDict(1L,"user_type","0","管理员"); 			dictDao.insertDict(2L,"user_type","1","操作员");
}
@Test
public void testDeleteDict(){ 
    dictDao.deleteDict(1L); 
    dictDao.deleteDict(2L);
}

执行testInsertDict:
在这里插入图片描述

通过日志可以看出,对t_dict的表的操作被广播至所有数据源。 测试删除字典,观察是否把所有数据源中该 公共表的记录删除。

(5) 字典关联查询测试

字典表已在各各分库存在,各业务表即可和字典表关联查询。

定义用户关联查询dao:

在UserDao中定义:

/**
*根据id列表查询多个用户,关联查询字典表
*@param userIds 用户id列表
*@return
*/ 
@Select({"<script>",
" select",
" * ",
" from t_user t ,t_dict b",
" where t.user_type = b.code and t.user_id in",
"<foreach collection='userIds' item='id' open='(' separator=',' close=')'>", "#{id}",
"</foreach>", "</script>"
})
List<Map> selectUserInfobyIds(@Param("userIds")List<Long> userIds);

定义测试方法:

@Test
public void testSelectUserInfobyIds(){ 
    List<Long> userIds = new ArrayList<>(); userIds.add(1L);
	userIds.add(2L);
	List<Map> users = userDao.selectUserInfobyIds(userIds); 
    JSONArray jsonUsers = new JSONArray(users); 
    System.out.println(jsonUsers);

}

执行测试方法,查看日志,成功关联查询字典表:
在这里插入图片描述

6. 读写分离

6.1 理解读写分离

面对日益增加的系统访问量,数据库的吞吐量面临着巨大瓶颈。 对于同一时刻有大量并发读操作和较少写操作类型的应用系统来说,将数据库拆分为主库和从库,主库负责处理事务性的增删改操作,从库负责处理查询操作,能 够有效的避免由数据更新导致的行锁,使得整个系统的查询性能得到极大的改善。

在这里插入图片描述

通过一主多从的配置方式,可以将查询请求均匀的分散到多个数据副本,能够进一步的提升系统的处理能力。 使用多主多从的方式,不但能够提升系统的吞吐量,还能够提升系统的可用性,可以达到在任何一个数据库宕机,甚至 磁盘物理损坏的情况下仍然不影响系统的正常运行。

在这里插入图片描述

读写分离的数据节点中的数据内容是一致的,而水平分片的每个数据节点的数据内容却并不相同。将水平分片和读 写分离联合使用,能够更加有效的提升系统的性能。

Sharding-JDBC读写分离则是根据SQL语义的分析,将读操作和写操作分别路由至主库与从库。它提供透明化读写分离,让使用方尽量像使用一个数据库一样使用主从数据库集群。

在这里插入图片描述

Sharding-JDBC提供一主多从的读写分离配置,可独立使用,也可配合分库分表使用,同一线程且同一数据库连接 内,如有写入操作,以后的读操作均从主库读取,用于保证数据一致性。Sharding-JDBC不提供主从数据库的数据 同步功能,需要采用其他机制支持。

在这里插入图片描述

接下来,咱们对上面例子中user_db进行读写分离实现。为了实现Sharding-JDBC的读写分离,首先,要进行

mysql的主从同步配置。

6.2 mysql主从同步(windows)

一,新增mysql实例

复制原有mysql如:D:\mysql-5.7.25(作为主库) -> D:\mysql-5.7.25-s1(作为从库),并修改以下从库的my.ini:

[mysqld]
#设置3307端口port = 3307
# 设置mysql的安装目录
basedir=D:\mysql‐5.7.25‐s1
# 设置mysql数据库的数据的存放目录
datadir=D:\mysql‐5.7.25‐s1\data

然后将从库安装为windows服务,注意配置文件位置:

D:\mysql‐5.7.25‐s1\bin>mysqld install mysqls1 ‐‐defaults‐file="D:\mysql‐5.7.25‐s1\my.ini"

由于从库是从主库复制过来的,因此里面的数据完全一致,可使用原来的账号、密码登录。

二,修改主、从库的配置文件(my.ini),新增内容如下:

主库:

[mysqld] #开启日志
log‐bin = mysql‐bin
#设置服务id,主从不能一致server‐id = 1
#设置需要同步的数据库
binlog‐do‐db=user_db #屏蔽系统库同步
binlog‐ignore‐db=mysql binlog‐ignore‐db=information_schema binlog‐ignore‐db=performance_schema

从库:

[mysqld] #开启日志
log‐bin = mysql‐bin
#设置服务id,主从不能一致server‐id = 2
#设置需要同步的数据库
replicate_wild_do_table=user_db.% #屏蔽系统库同步
replicate_wild_ignore_table=mysql.% replicate_wild_ignore_table=information_schema.% replicate_wild_ignore_table=performance_schema.%

重启主库和从库:

net start [主库服务名]
net start [从库服务名mysqls1]

请注意,主从MySQL下的数据(data)目录下有个文件auto.cnf,文件中定义了uuid,要保证主从数据库实例的

uuid不一样,建议直接删除掉,重启服务后将会重新生成。

三,授权主从复制专用账号

#切换至主库bin目录,登录主库mysql ‐h localhost ‐uroot ‐p #授权主备复制专用账号
GRANT REPLICATION SLAVE ON *.* TO 'db_sync'@'%' IDENTIFIED BY 'db_sync';
#刷新权限
FLUSH PRIVILEGES;
#确认位点 记录下文件名以及位点
show master status;

在这里插入图片描述

四,设置从库向主库同步数据、并检查链路

#切换至从库bin目录,登录从库
mysql ‐h localhost ‐P3307 ‐uroot ‐p 
#先停止同步
STOP SLAVE;
#修改从库指向到主库,使用上一步记录的文件名以及位点
CHANGE MASTER TO
master_host = 'localhost', 
master_user = 'db_sync', 
master_password = 'db_sync', 
master_log_file = 'mysql‐bin.000002', 
master_log_pos = 154;
#启动同步
START SLAVE;
#查看从库状态Slave_IO_Runing和Slave_SQL_Runing都为Yes说明同步成功,如果不为Yes,请检查error_log,然后 排查相关异常。
show slave status\G

#注意 如果之前此备库已有主库指向 需要先执行以下命令清空
STOP SLAVE IO_THREAD FOR CHANNEL '';
reset slave all;

最后测试在主库修改数据库,看从库是否能够同步成功。

6.3 实现sharding-jdbc读写分离

(1) 在Sharding-JDBC规则中修改

# 增加数据源s0,使用上面主从同步配置的从库。
spring.shardingsphere.datasource.names = m0,m1,m2,s0
...
spring.shardingsphere.datasource.s0.type = com.alibaba.druid.pool.DruidDataSource spring.shardingsphere.datasource.s0.driver‐class‐name = com.mysql.jdbc.Driver spring.shardingsphere.datasource.s0.url = jdbc:mysql://localhost:3307/user_db?useUnicode=true 
spring.shardingsphere.datasource.s0.username = root spring.shardingsphere.datasource.s0.password = root

....

# 主库从库逻辑数据源定义 ds0为user_db 
spring.shardingsphere.sharding.master‐slave‐rules.ds0.master‐data‐source‐name=m0 spring.shardingsphere.sharding.master‐slave‐rules.ds0.slave‐data‐source‐names=s0

# t_user分表策略,固定分配至ds0的t_user真实表spring.shardingsphere.sharding.tables.t_user.actual‐data‐nodes = ds0.t_user
....

(2) 测试

执行testInsertUser单元测试:

在这里插入图片描述

通过日志可以看出,所有写操作落入m0数据源。执行testSelectUserbyIds单元测试:
在这里插入图片描述
通过日志可以看出,所有写操作落入s0数据源,达到目标。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值