ShardingSphere,大家多少都有听过吧,Apache顶级项目,国内大佬的巨作,Java中用的最多的一个分库分表框架,如果你们的系统中需要分库分表,强烈建议使用,完全可以满足你的所有需求。 本文并不会介绍什么是分库分表,而是通过大量案例,让你了解ShardingSphere可以做什么?如何做?以及SpringBoot中如何使用它等等。 也可以转移到个人博客阅读:http:itsoku。comcourse25 废话不多说,开始干货分享。1、git地址 https:github。comapacheshardingsphere2、关于版本 shardingsphere目前最新版本5。X了,大版本之间变化比较大。本次以4。1。1为例来介绍。3、4。X版本文档 https:shardingsphere。apache。orgdocumentlegacy4。xdocumentcnoverview 如果对分库分表没有概念的朋友,建议大家先去看看,然后再继续向下看。4、纯javaapi代码案例案例git地址:https:gitee。comjavacode2018shardingspheredemo 4。1、准备工作 1)创建一个springboot应用 2)引入shardingsphere的maven配置dependencygroupIdorg。apache。shardingspheregroupIdshardingjdbccoreartifactIdversion4。1。1versiondependency 3)完整的maven配置如下?xmlversion1。0encodingUTF8?projectxmlnshttp:maven。apache。orgPOM4。0。0xmlns:xsihttp:www。w3。org2001XMLSchemainstancexsi:schemaLocationhttp:maven。apache。orgPOM4。0。0https:maven。apache。orgxsdmaven4。0。0。xsdmodelVersion4。0。0modelVersionparentgroupIdorg。springframework。bootgroupIdspringbootstarterparentartifactIdversion2。7。1versionrelativePath!lookupparentfromrepositoryparentgroupIdcom。itsokugroupIdsjdemo1artifactIdversion0。0。1SNAPSHOTversionnamesjdemo1namedescriptionDemoprojectforSpringBootdescriptionpropertiesjava。version1。8java。versionpropertiesdependenciesdependencygroupIdorg。mybatis。spring。bootgroupIdmybatisspringbootstarterartifactIdversion2。2。2versiondependencydependencygroupIdmysqlgroupIdmysqlconnectorjavaartifactIddependencydependencygroupIdorg。projectlombokgroupIdlombokartifactIdoptionaltrueoptionaldependencydependencygroupIdorg。springframework。bootgroupIdspringbootstartertestartifactIdscopetestscopedependencydependencygroupIdorg。apache。shardingspheregroupIdshardingjdbccoreartifactIdversion4。1。1versiondependencydependenciesbuildpluginsplugingroupIdorg。springframework。bootgroupIdspringbootmavenpluginartifactIdconfigurationexcludesexcludegroupIdorg。projectlombokgroupIdlombokartifactIdexcludeexcludesconfigurationpluginpluginsbuildproject4。2、案例1:单库多表 需求 一个库中有2个订单表,按照订单id取模,将数据路由到指定的表。 sql脚本dropdatabaseifexistssjds0;createdatabasesjds0;usesjds0;droptableifexiststorder0;createtabletorder0(orderidbigintnotnullprimarykey,useridbigintnotnull,pricebigintnotnull);droptableifexiststorder1;createtabletorder1(orderidbigintnotnullprimarykey,useridbigintnotnull,pricebigintnotnull);createtabletuser(idbigintnotnullprimarykeyautoincrement,namevarchar(128)notnull); java代码importcom。zaxxer。hikari。HikariDataSimportorg。apache。shardingsphere。api。config。sharding。ShardingRuleCimportorg。apache。shardingsphere。api。config。sharding。TableRuleCimportorg。apache。shardingsphere。api。config。sharding。strategy。InlineShardingStrategyCimportorg。apache。shardingsphere。shardingjdbc。api。ShardingDataSourceFimportorg。apache。shardingsphere。underlying。common。config。properties。ConfigurationPropertyKimportjavax。sql。DataSimportjava。sql。Cimportjava。sql。PreparedSimportjava。sql。SQLEimportjava。util。HashMimportjava。util。Mimportjava。util。PpublicclassDemo1{publicstaticvoidmain(String〔〕args)throwsSQLException{1、配置真实数据源MapString,DataSourcedataSourceMapnewHashMap();dataSourceMap。put(ds0,dataSource1());2。配置表的规则TableRuleConfigurationorderTableRuleConfignewTableRuleConfiguration(torder,ds0。torder{0。。1});指定表的分片策略(分片字段分片算法)orderTableRuleConfig。setTableShardingStrategyConfig(newInlineShardingStrategyConfiguration(orderid,torder{orderid2}));3、分片规则ShardingRuleConfigurationshardingRuleConfignewShardingRuleConfiguration();将表的分片规则加入到分片规则列表shardingRuleConfig。getTableRuleConfigs()。add(orderTableRuleConfig);4、配置一些属性PropertiespropsnewProperties();输出sqlprops。put(ConfigurationPropertyKey。SQLSHOW。getKey(),true);5、创建数据源DataSourcedataSourceShardingDataSourceFactory。createDataSource(dataSourceMap,shardingRuleConfig,props);6、获取连接,执行sqlConnectionconnectiondataSource。getConnection();connection。setAutoCommit(false);测试向torder表插入8条数据,8条数据会分散到2个表PreparedStatementpsconnection。prepareStatement(insertintotorder(orderid,userid,price)values(?,?,?));for(longi1;i8;i){intj1;ps。setLong(j,i);ps。setLong(j,i);ps。setLong(j,100i);System。out。println(ps。executeUpdate());}connection。commit();ps。close();connection。close();}privatestaticDataSourcedataSource1(){HikariDataSourcedataSource1newHikariDataSource();dataSource1。setDriverClassName(com。mysql。jdbc。Driver);dataSource1。setJdbcUrl(jdbc:mysql:localhost:3306sjds0?characterEncodingUTF8);dataSource1。setUsername(root);dataSource1。setPassword(root123);returndataSource1;}} 运行输出LogicSQL:insertintotorder(orderid,userid,price)values(?,?,?)ActualSQL:ds0:::insertintotorder1(orderid,userid,price)values(?,?,?):::〔1,1,100〕1LogicSQL:insertintotorder(orderid,userid,price)values(?,?,?)ActualSQL:ds0:::insertintotorder0(orderid,userid,price)values(?,?,?):::〔2,2,200〕1LogicSQL:insertintotorder(orderid,userid,price)values(?,?,?)ActualSQL:ds0:::insertintotorder1(orderid,userid,price)values(?,?,?):::〔3,3,300〕1LogicSQL:insertintotorder(orderid,userid,price)values(?,?,?)ActualSQL:ds0:::insertintotorder0(orderid,userid,price)values(?,?,?):::〔4,4,400〕1 4。3、案例2:多库多表 需求 2个库:sjds0、sjds1 2个库中都包含2个表:torder0,torder1 根据userid2路由库,根据orderid2路由表。 执行sqldropdatabaseifexistssjds0;createdatabasesjds0;usesjds0;droptableifexiststorder0;createtabletorder0(orderidbigintnotnullprimarykey,useridbigintnotnull,pricebigintnotnull);droptableifexiststorder1;createtabletorder1(orderidbigintnotnullprimarykey,useridbigintnotnull,pricebigintnotnull);dropdatabaseifexistssjds1;createdatabasesjds1;usesjds1;droptableifexiststorder0;createtabletorder0(orderidbigintnotnullprimarykey,useridbigintnotnull,pricebigintnotnull);droptableifexiststorder1;createtabletorder1(orderidbigintnotnullprimarykey,useridbigintnotnull,pricebigintnotnull); 代码importcom。zaxxer。hikari。HikariDataSimportorg。apache。shardingsphere。api。config。sharding。ShardingRuleCimportorg。apache。shardingsphere。api。config。sharding。TableRuleCimportorg。apache。shardingsphere。api。config。sharding。strategy。InlineShardingStrategyCimportorg。apache。shardingsphere。shardingjdbc。api。ShardingDataSourceFimportorg。apache。shardingsphere。underlying。common。config。properties。ConfigurationPropertyKimportjavax。sql。DataSimportjava。sql。Cimportjava。sql。PreparedSimportjava。sql。SQLEimportjava。util。HashMimportjava。util。Mimportjava。util。PpublicclassDemo2{publicstaticvoidmain(String〔〕args)throwsSQLException{配置真实数据源MapString,DataSourcedataSourceMapnewHashMap();dataSourceMap。put(ds0,dataSource1());dataSourceMap。put(ds1,dataSource2());2。配置表的规则TableRuleConfigurationorderTableRuleConfignewTableRuleConfiguration(torder,ds{0。。1}。torder{0。。1});指定db的分片策略(分片字段分片算法)orderTableRuleConfig。setDatabaseShardingStrategyConfig(newInlineShardingStrategyConfiguration(userid,ds{userid2}));指定表的分片策略(分片字段分片算法)orderTableRuleConfig。setTableShardingStrategyConfig(newInlineShardingStrategyConfiguration(orderid,torder{orderid2}));3、分片规则ShardingRuleConfigurationshardingRuleConfignewShardingRuleConfiguration();将表的分片规则加入到分片规则列表shardingRuleConfig。getTableRuleConfigs()。add(orderTableRuleConfig);4、配置一些属性PropertiespropsnewProperties();输出sqlprops。put(ConfigurationPropertyKey。SQLSHOW。getKey(),true);5、创建数据源DataSourcedataSourceShardingDataSourceFactory。createDataSource(dataSourceMap,shardingRuleConfig,props);6、获取连接,执行sqlConnectionconnectiondataSource。getConnection();connection。setAutoCommit(false);PreparedStatementpsconnection。prepareStatement(insertintotorder(orderid,userid,price)values(?,?,?));插入4条数据测试,每个表会落入1条数据for(longuserid1;userid2;userid){for(longorderid1;orderid2;orderid){intj1;ps。setLong(j,orderid);ps。setLong(j,userid);ps。setLong(j,100);System。out。println(ps。executeUpdate());}}connection。commit();ps。close();connection。close();}privatestaticDataSourcedataSource1(){HikariDataSourcedataSource1newHikariDataSource();dataSource1。setDriverClassName(com。mysql。jdbc。Driver);dataSource1。setJdbcUrl(jdbc:mysql:localhost:3306sjds0?characterEncodingUTF8);dataSource1。setUsername(root);dataSource1。setPassword(root123);returndataSource1;}privatestaticDataSourcedataSource2(){HikariDataSourcedataSource1newHikariDataSource();dataSource1。setDriverClassName(com。mysql。jdbc。Driver);dataSource1。setJdbcUrl(jdbc:mysql:localhost:3306sjds1?characterEncodingUTF8);dataSource1。setUsername(root);dataSource1。setPassword(root123);returndataSource1;}} 3行关键代码 运行输出LogicSQL:insertintotorder(orderid,userid,price)values(?,?,?)ActualSQL:ds1:::insertintotorder1(orderid,userid,price)values(?,?,?):::〔1,1,100〕1LogicSQL:insertintotorder(orderid,userid,price)values(?,?,?)ActualSQL:ds1:::insertintotorder0(orderid,userid,price)values(?,?,?):::〔2,1,100〕1LogicSQL:insertintotorder(orderid,userid,price)values(?,?,?)ActualSQL:ds0:::insertintotorder1(orderid,userid,price)values(?,?,?):::〔1,2,100〕1LogicSQL:insertintotorder(orderid,userid,price)values(?,?,?)ActualSQL:ds0:::insertintotorder0(orderid,userid,price)values(?,?,?):::〔2,2,100〕1 4。4、案例3:单库无分表规则 若表未指定分片规则,则直接路由到对应的表。 sql脚本 sjds0库中有tuser表dropdatabaseifexistssjds0;createdatabasesjds0;usesjds0;createtabletuser(idbigintnotnullprimarykeyautoincrement,namevarchar(128)notnull); 代码importcom。zaxxer。hikari。HikariDataSimportorg。apache。shardingsphere。api。config。sharding。ShardingRuleCimportorg。apache。shardingsphere。shardingjdbc。api。ShardingDataSourceFimportorg。apache。shardingsphere。underlying。common。config。properties。ConfigurationPropertyKimportjavax。sql。DataSimportjava。sql。Cimportjava。sql。PreparedSimportjava。sql。SQLEimportjava。util。HashMimportjava。util。Mimportjava。util。PpublicclassDemo3{publicstaticvoidmain(String〔〕args)throwsSQLException{1。配置真实数据源MapString,DataSourcedataSourceMapnewHashMap();dataSourceMap。put(ds0,dataSource1());2、无分片规则3、分片规则ShardingRuleConfigurationshardingRuleConfignewShardingRuleConfiguration();4、配置一些属性PropertiespropsnewProperties();输出sqlprops。put(ConfigurationPropertyKey。SQLSHOW。getKey(),true);5、创建数据源DataSourcedataSourceShardingDataSourceFactory。createDataSource(dataSourceMap,shardingRuleConfig,props);ConnectionconnectiondataSource。getConnection();connection。setAutoCommit(false);PreparedStatementpsconnection。prepareStatement(insertintotuser(name)values(?));ps。setString(1,张三);System。out。println(ps。executeUpdate());connection。commit();ps。close();connection。close();}privatestaticDataSourcedataSource1(){HikariDataSourcedataSource1newHikariDataSource();dataSource1。setDriverClassName(com。mysql。jdbc。Driver);dataSource1。setJdbcUrl(jdbc:mysql:localhost:3306sjds0?characterEncodingUTF8);dataSource1。setUsername(root);dataSource1。setPassword(root123);returndataSource1;}} 运行输出LogicSQL:insertintotuser(name)values(?)ActualSQL:ds0:::insertintotuser(name)values(?):::〔张三〕4。5、案例4:多库无分表规则 需求 2个库:sjds0、sjds1 2个库中都包含表:tuser tuser表不指定路由规则的情况下,向tuser表写入数据会落入哪个库呢?来看下效果 sqldropdatabaseifexistssjds0;createdatabasesjds0;usesjds0;createtabletuser(idbigintnotnullprimarykeyautoincrement,namevarchar(128)notnull);dropdatabaseifexistssjds1;createdatabasesjds1;usesjds1;createtabletuser(idbigintnotnullprimarykeyautoincrement,namevarchar(128)notnull); java代码 下面配置2个数据源,向tuser表插入数据,看看数据会落在哪个库?importcom。zaxxer。hikari。HikariDataSimportorg。apache。shardingsphere。api。config。sharding。ShardingRuleCimportorg。apache。shardingsphere。shardingjdbc。api。ShardingDataSourceFimportorg。apache。shardingsphere。underlying。common。config。properties。ConfigurationPropertyKimportjavax。sql。DataSimportjava。sql。Cimportjava。sql。PreparedSimportjava。sql。SQLEimportjava。util。HashMimportjava。util。LinkedHashMimportjava。util。Mimportjava。util。PpublicclassDemo4{publicstaticvoidmain(String〔〕args)throwsSQLException{1。配置2个数据源MapString,DataSourcedataSourceMapnewLinkedHashMap();dataSourceMap。put(ds0,dataSource1());dataSourceMap。put(ds1,dataSource2());2、无分片规则3、分片规则ShardingRuleConfigurationshardingRuleConfignewShardingRuleConfiguration();4、配置一些属性PropertiespropsnewProperties();输出sqlprops。put(ConfigurationPropertyKey。SQLSHOW。getKey(),true);5、创建数据源DataSourcedataSourceShardingDataSourceFactory。createDataSource(dataSourceMap,shardingRuleConfig,props);ConnectionconnectiondataSource。getConnection();connection。setAutoCommit(false);插入4条数据,测试效果for(inti0;i4;i){PreparedStatementpsconnection。prepareStatement(insertintotuser(name)values(?));ps。setString(1,张三);System。out。println(ps。executeUpdate());}connection。commit();connection。close();}privatestaticDataSourcedataSource1(){HikariDataSourcedataSource1newHikariDataSource();dataSource1。setDriverClassName(com。mysql。jdbc。Driver);dataSource1。setJdbcUrl(jdbc:mysql:localhost:3306sjds0?characterEncodingUTF8);dataSource1。setUsername(root);dataSource1。setPassword(root123);returndataSource1;}privatestaticDataSourcedataSource2(){HikariDataSourcedataSource1newHikariDataSource();dataSource1。setDriverClassName(com。mysql。jdbc。Driver);dataSource1。setJdbcUrl(jdbc:mysql:localhost:3306sjds1?characterEncodingUTF8);dataSource1。setUsername(root);dataSource1。setPassword(root123);returndataSource1;}} 运行输出 输出如下,落入的库是不确定的。LogicSQL:insertintotuser(name)values(?)ActualSQL:ds1:::insertintotuser(name)values(?):::〔张三〕1LogicSQL:insertintotuser(name)values(?)ActualSQL:ds1:::insertintotuser(name)values(?):::〔张三〕1LogicSQL:insertintotuser(name)values(?)ActualSQL:ds0:::insertintotuser(name)values(?):::〔张三〕1LogicSQL:insertintotuser(name)values(?)ActualSQL:ds1:::insertintotuser(name)values(?):::〔张三〕15、分片问题? 上面介绍的案例,db的路由、表的路由都是采用取模的方式,这种方式存在一个问题: 当查询条件是,,,、BETWEENAND的时候,就无能为力了,此时要用其他的分片策略来解决,下面来看看如何解决。6、分片6。1、分片键 用于分片的数据库字段,是将数据库(表)水平拆分的关键字段。例:将订单表中的订单主键的尾数取模分片,则订单主键为分片字段。SQL中如果无分片字段,将执行全路由,性能较差。除了对单分片字段的支持,ShardingSphere也支持根据多个字段进行分片。6。2、分片算法 通过分片算法将数据分片,支持通过、、、、、BETWEEN和IN分片。分片算法需要应用方开发者自行实现,可实现的灵活度非常高。 目前提供4种分片算法。由于分片算法和业务实现紧密相关,因此并未提供内置分片算法,而是通过分片策略将各种场景提炼出来,提供更高层级的抽象,并提供接口让应用开发者自行实现分片算法。精确分片算法 对应PreciseShardingAlgorithm,用于处理使用单一键作为分片键的与IN进行分片的场景。需要配合StandardShardingStrategy使用。范围分片算法 对应RangeShardingAlgorithm,用于处理使用单一键作为分片键的BETWEENAND、、、、进行分片的场景。需要配合StandardShardingStrategy使用。复合分片算法 对应ComplexKeysShardingAlgorithm,用于处理使用多键作为分片键进行分片的场景,包含多个分片键的逻辑较复杂,需要应用开发者自行处理其中的复杂度。需要配合ComplexShardingStrategy使用。Hint分片算法 对应HintShardingAlgorithm,用于处理使用Hint行分片的场景。需要配合HintShardingStrategy使用。6。3、5种分片策略 包含分片键和分片算法,由于分片算法的独立性,将其独立抽离。真正可用于分片操作的是分片键分片算法,也就是分片策略。目前提供5种分片策略。行表达式分片策略(InlineShardingStrategy) 对应InlineShardingStrategy。使用Groovy的表达式,提供对SQL语句中的和IN的分片操作支持,只支持单分片键。对于简单的分片算法,可以通过简单的配置使用,从而避免繁琐的Java代码开发,如:tuser{uid8}表示tuser表根据uid模8,而分成8张表,表名称为tuser0到tuser7。标准分片策略(StandardShardingStrategy) 对应StandardShardingStrategy。提供对SQL语句中的,,,,,IN和BETWEENAND的分片操作支持。StandardShardingStrategy只支持单分片键,提供PreciseShardingAlgorithm和RangeShardingAlgorithm两个分片算法。PreciseShardingAlgorithm是必选的,用于处理和IN的分片。RangeShardingAlgorithm是可选的,用于处理BETWEENAND,,,,分片,如果不配置RangeShardingAlgorithm,SQL中的BETWEENAND将按照全库路由处理。复合分片策略(ComplexShardingStrategy) 对应ComplexShardingStrategy。复合分片策略。提供对SQL语句中的,,,,,IN和BETWEENAND的分片操作支持。ComplexShardingStrategy支持多分片键,由于多分片键之间的关系复杂,因此并未进行过多的封装,而是直接将分片键值组合以及分片操作符透传至分片算法,完全由应用开发者实现,提供最大的灵活度。Hint分片策略(HintShardingStrategy) 对应HintShardingStrategy。通过Hint指定分片值而非从SQL中提取分片值的方式进行分片的策略。不分片策略 对应NoneShardingStrategy。不分片的策略。6。4、SQLHint 对于分片字段非SQL决定,而由其他外置条件决定的场景,可使用SQLHint灵活的注入分片字段。例:内部系统,按照员工登录主键分库,而数据库中并无此字段。SQLHint支持通过JavaAPI和SQL注释(待实现)两种方式使用。 原文链接:https:mp。weixin。qq。comsfE29Ayn1lAW2ld0EwN0Ypg