sharding-jdbc实现分库分表

    技术2022-07-10  109

    介绍

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

    sharding-jdbc是一个分布式的关系型数据库中间件轻量级的Java框架,以jar的方式提供服务适用于任何基于 JDBC 的 ORM 框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template 或直接使用 JDBC。支持任何第三方的数据库连接池,,如:DBCP, C3P0, BoneCP, Druid, HikariCP 等。支持任意实现 JDBC 规范的数据库,目前支持 MySQL,Oracle,SQLServer,PostgreSQL 以及任何遵循 SQL92 标准的数据库。

    服务配置

    Java API yaml properties spring命名空间

    mycat 区别

    MyCat是服务端的代理,Sharding-Jdbc是客户端代理 实际开发中如果企业有DBA建议使用MyCat,都是开发人员建议使用sharding-jdbc MyCat不支持在一个库内进行水平分表,而sharding-jdbc支持在同一个数据库中进行水平分表

    表概念

    数据节点:存储数据的MySQL节点 绑定表:相当于MyCat中的子表 广播表:相当于MyCat中的全局表

    环境

    两个数据库

    分库分表实现

    创建数据库

    两个数据库分别建两个表

    CREATE TABLE `order_info_1` ( `id` int(11) NOT NULL, `order_amount` decimal(10,2) DEFAULT NULL, `order_status` int(255) DEFAULT NULL, `user_id` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `order_info_2` ( `id` int(11) NOT NULL, `order_amount` decimal(10,2) DEFAULT NULL, `order_status` int(255) DEFAULT NULL, `user_id` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    依赖

    <dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>sharding-jdbc-spring-boot-starter</artifactId> <version>4.0.0-RC2</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.2.7.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.16</version> </dependency> <dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>2.6.1</version> <exclusions> <exclusion> <artifactId>slf4j-api</artifactId> <groupId>org.slf4j</groupId> </exclusion> </exclusions> </dependency>

    配置

    # 配置数据源 spring.shardingsphere.datasource.names=ds0,ds1 spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver spring.shardingsphere.datasource.ds0.jdbcUrl=jdbc:mysql://192.168.56.132:3306/shard_order spring.shardingsphere.datasource.ds0.username=guanzc spring.shardingsphere.datasource.ds0.password=123456 # 数据源 spring.shardingsphere.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver spring.shardingsphere.datasource.ds1.jdbcUrl=jdbc:mysql://192.168.56.134:3306/shard_order spring.shardingsphere.datasource.ds1.username=guanzc spring.shardingsphere.datasource.ds1.password=123456 # 具体的分片规则,基于数据节点 spring.shardingsphere.sharding.tables.order_info.actual-data-nodes=ds$->{0..1}.order_info_$->{1..2} # 分库的规则 spring.shardingsphere.sharding.tables.order_info.database-strategy.inline.sharding-column=id spring.shardingsphere.sharding.tables.order_info.database-strategy.inline.algorithm-expression=ds$->{id % 2} # 分表的规则 spring.shardingsphere.sharding.tables.order_info.table-strategy.inline.sharding-column=user_id spring.shardingsphere.sharding.tables.order_info.table-strategy.inline.algorithm-expression=order_info_$->{user_id % 2 + 1} 按照id奇偶数分库,user_id 奇偶分表

    插入、查询数据

    @Autowired private JdbcTemplate jdbcTemplate; @Test void contextLoads() { String sql = "insert into order_info(id,order_amount,order_status,user_id) values(1,23.12,1,2)"; int i = jdbcTemplate.update(sql); System.out.println("新增数据:"+i); sql ="select * from order_info"; List<Map<String, Object>> result = jdbcTemplate.queryForList(sql); System.out.println("条数:"+result.size()); }

    按照分库分表规则, 执行插入sql 数据会保存到,ds1的 order_info_1表中。

    广播表

    字典表,在每个数据库中都需要配置

    sql语句

    CREATE TABLE `province_info` ( `id` int(11) NOT NULL, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    配置广播表

    spring.shardingsphere.sharding.broadcast-tables=province_info

    测试

    @Test public void testBroadTables(){ String sql = "insert into province_info(id, name) values(1, '北京')"; int i = jdbcTemplate.update(sql); System.out.println("插入条数:"+i); //查询 sql ="select * from province_info"; List<Map<String, Object>> result = jdbcTemplate.queryForList(sql); for (Map<String, Object> map : result){ System.out.println(map.get("id")+"-------------- "+map.get("name")); } } 插入时,会分别想两个数据插入一样的数据。实际是两条数据查询时,只会查询出一条数据

    绑定表

    父子表

    sql 语句

    分别创建 order_info_1、2 的绑定表 order_item_1、2

    CREATE TABLE `order_item_1` ( `id` int(11) DEFAULT NULL, `product_name` varchar(255) DEFAULT NULL, `order_id` int(11) DEFAULT NULL, `user_id` int(11) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `order_item_2` ( `id` int(11) DEFAULT NULL, `product_name` varchar(255) DEFAULT NULL, `order_id` int(11) DEFAULT NULL, `user_id` int(11) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    配置分库分表

    #绑定表-分片规则 spring.shardingsphere.sharding.tables.order_item.actual-data-nodes=ds$->{0..1}.order_item_${1..2} spring.shardingsphere.sharding.tables.order_item.database-strategy.inline.sharding-column=order_id spring.shardingsphere.sharding.tables.order_item.database-strategy.inline.algorithm-expression=ds$->{order_id % 2} #绑定表-分表规则 spring.shardingsphere.sharding.tables.order_item.table-strategy.inline.sharding-column=id spring.shardingsphere.sharding.tables.order_item.table-strategy.inline.algorithm-expression=order_item_$->{id % 2 + 1} # 配置绑定表 spring.shardingsphere.sharding.binding-tables=order_info,order_item

    测试

    @Test void bangding(){ String sql = "insert into order_item(id, product_name, order_id, user_id) values(1, 'HUAWEI',2,2),(2, 'HUAWEI',3,3)"; int i = jdbcTemplate.update(sql); System.out.println("插入条数:"+i); } 根据分片、分表规则,id=1 数据存储在 ds0的 order_item_2表中id=2 数据存储在 ds1的 order_item_1表中

    读写分离

    配置,至少需要配置三个服务器,两主一从。配置按照两主两从。

    配置数据库

    # 指定主从的配置节点 spring.shardingsphere.datasource.names=master0,master0slave0,master1,master1slave0 数据源配置... # 读写分离主从关系绑定 spring.shardingsphere.sharding.master-slave-rules.ds0.master-data-source-name=master0 spring.shardingsphere.sharding.master-slave-rules.ds0.slave-data-source-names=master0slave0 spring.shardingsphere.sharding.master-slave-rules.ds0.load-balance-algorithm-type=round_robin spring.shardingsphere.sharding.master-slave-rules.ds1.master-data-source-name=master1 spring.shardingsphere.sharding.master-slave-rules.ds1.slave-data-source-names=master1slave0 spring.shardingsphere.sharding.master-slave-rules.ds1.load-balance-algorithm-type=random

    测试

    @Test void contextLoads() { String sql = "insert into order_info(id,order_amount,order_status,user_id) values(1,243.18,1,2)"; int i = jdbcTemplate.update(sql); System.out.println("新增数据:"+i); } 干掉一个数据库后,插入数据,测试
    Processed: 0.020, SQL: 9