在互联网公司中,或者大型应用系统中,我们作为开发人员通常需要面对海量的数据,这些海量数据通常都是百万级,千万级的数据量。而且这些海量数据对企业和用户来说都是至关重要的。面对如此海量的数据,我们需要保证数据的安全可靠;当保存数据的时候数据不能丢失,当查询数据的时候,需要保证数据的及时响应。 那么,如何保证这些海量数据的安全可靠,以及实时查询呢? 当然,这里肯定会有很多解决方案。在当下大数据时代,出现了很多比较友好的解决方案和框架可以解决这个问题。例如:Elasticsearch,Hadoop等,都是现在比较流行的框架。 目前,大多数企业应用系统中的数据应该都是结构化的,也就是这些海量数据存储在关系型数据库中,例如:Mysql,Oracle等。而且Mysql关系型数据库一般都是初创企业或者中小企业的首选。那么Mysql如何承载百万级,千万级的数据呢?根据阿里巴巴的开发守则,建议Mysql的数据表容量不要超过500万条数据记录,也就说建议在500万条数据记录的时候做一次分表。针对Mysql的分库分表已经存在很多成熟,开源的解决方案和框架,例如:Mycat,ShardingSphere等。 但是,在大多数业务场景中Mysql的一个库会有很多表,数据量大的,需要及时响应查询的数据表可能就那么几张,例如:订单表数据量比较大,配置表数据量比较小;我们需要对数据量比较大的订单表进行分库分表操作,配置表可以不需要分库分表。也就是说我们需要对部分表进行分库分表,部分表不进行分库分表。那么我们可以考虑使用ShardingSphere的动态数据源,对需要分库分表的数据我们把正确的数据源路由到正确的库上,把不需要分库分表的表路由到默认的数据库。 下面我们以SpringBoot为例把详细的实践过程记录下来,方便以后查阅。 引入ShardingSphere的mavendependency,采用4。0。0版本dependencygroupIdorg。apache。shardingspheregroupIdshardingjdbccoreartifactIdversion{shardingsphere。version}versiondependencydependencygroupIdorg。apache。shardingspheregroupIdshardingcorecommonartifactIdversion{shardingsphere。version}versiondependency 配置多数据源ConfigurationpublicclassMultDSConfig{publicstaticfinalStringDEFAULTDATASOURCENAMEValue({spring。datasource。url})privateStringdbUValue({spring。datasource。username})privateStringdbUValue({spring。datasource。password})privateStringdbPAutowiredprivateMultDSConfigurationPropertiesmultDSConfigurationPAutowiredprivateDruidDataConfigurationPropertiesdruidDataConfigurationPpublicDruidDataSourcegetDataSource(Stringurl,Stringuser,Stringpassword){DruidDataSourcedruidDataSourcenewDruidDataSource();druidDataSource。setUrl(url);druidDataSource。setKeepAlive(true);druidDataSource。setPassword(password);druidDataSource。setUsername(user);druidDataSource。setMaxWait(druidDataConfigurationProperties。getMaxWait());druidDataSource。setInitialSize(druidDataConfigurationProperties。getInitialSize());druidDataSource。setMaxActive(druidDataConfigurationProperties。getMaxActive());druidDataSource。setMinIdle(druidDataConfigurationProperties。getMinIdle());druidDataSource。setTestOnBorrow(druidDataConfigurationProperties。isTestOnBorrow());druidDataSource。setTestOnReturn(druidDataConfigurationProperties。isTestOnReturn());druidDataSource。setTestWhileIdle(druidDataConfigurationProperties。isTestWhileIdle());druidDataSource。setPoolPreparedStatements(druidDataConfigurationProperties。isPoolPreparedStatements());druidDataSource。setConnectProperties(druidDataConfigurationProperties。getConnectionProperties());druidDataSource。setMaxPoolPreparedStatementPerConnectionSize(druidDataConfigurationProperties。getMaxPoolPreparedStatementPerConnectionSize());druidDataSource。setValidationQuery(druidDataConfigurationProperties。getValidationQuery());druidDataSource。setMinEvictableIdleTimeMillis(druidDataConfigurationProperties。getMinEvictableIdleTimeMillis());druidDataSource。setTimeBetweenEvictionRunsMillis(druidDataConfigurationProperties。getTimeBetweenEvictionRunsMillis());druidDataSource。setMaxPoolPreparedStatementPerConnectionSize(druidDataConfigurationProperties。getMaxPoolPreparedStatementPerConnectionSize());returndruidDataS}Bean(namedataSourceMap)publicMapString,DataSourcegetDataSourceMap(){分库设置MapString,DataSourcedataSourceMapnewHashMap();默认不分库的datasourceDruidDataSourcedefaultDSgetDataSource(dbUrl,dbUser,dbPassword);添加数据源和默认数据源,分库数据源,以’ds‘作为前缀,regioncode作为后缀dataSourceMap。put(DEFAULTDATASOURCENAME,defaultDS);按照区域编码定义datasourcemultDSConfigurationProperties。getDatasource()。forEach(x{DruidDataSourcedruidDataSourcegetDataSource(x。getUrl(),x。getUser(),x。getPassword());StringdataSourceNamedsx。getRegion();dataSourceMap。put(dataSourceName,druidDataSource);});returndataSourceM}} 说明如下:采用dataSourceMapHashMap保存多个数据源,其中有默认不需要分库分表的默认数据库,以及需要分库分表的分库后的新数据库;dataSourceMap的key包含需要分库分表的分片键;multDSConfigurationProperties是多个数据源的配置属性;druidDataConfigurationProperties是连接池的配置数据,这里我们采用阿里巴巴的数据连接池,当然也可以采用其他的连接池; 多数据源配置类DataComponentConfigurationProperties(prefixsharding)publicclassMultDSConfigurationProperties{NotEmptyprivateListDataSourcePDatapublicstaticclassDataSourceProperties{NotBlankprivateSNotBlankprivateSNotBlankprivateSNotBlankprivateS}}sharding。datasource〔0〕。regionregionAsharding。datasource〔0〕。urljdbc:mysql:xxx。xxx。xxxdatabase0sharding。datasource〔0〕。userxxxxsharding。datasource〔0〕。passwordxxxsharding。datasource〔1〕。regionregionBsharding。datasource〔1〕。urljdbc:mysql:xxx。xxx。xxxdatabase1sharding。datasource〔1〕。userxxxxsharding。datasource〔1〕。passwordxxx 说明如下:我们的多数据源采用properties的数组方式进行配置。 下面配置SpringBoot的数据源分片表列表,以逗号,分割Value({spring。sharding。tables})privateStringshardingTResourceprivateMapString,DataSourcedataSourceMPrimarypublicDataSourcedataSource()throwsSQLException{ListStringtablesLists。newArrayList(StringUtils。split(shardingTables,,));Sharding全局配置ShardingRuleConfigurationshardingRuleConfigurationnewShardingRuleConfiguration();数据不分片的默认数据源shardingRuleConfiguration。setDefaultDataSourceName(MultDSConfig。DEFAULTDATASOURCENAME);绑定表shardingRuleConfiguration。getBindingTableGroups()。addAll(tables);CollectionTableRuleConfigurationtableRuleConfigurationsshardingRuleConfiguration。getTableRuleConfigs();tables。forEach(table{配置表数据分片规则TableRuleConfigurationtableRuleConfigurationnewTableRuleConfiguration(table);自定义分片算法,可能不会用到tableRuleConfiguration。setDatabaseShardingStrategyConfig(newStandardShardingStrategyConfiguration(regioncode,newCustomShardingAlgorithm()));行表达式分片算法,’ds‘前缀regioncode后缀tableRuleConfiguration。setDatabaseShardingStrategyConfig(newInlineShardingStrategyConfiguration(regioncode,ds{regioncode}));tableRuleConfigurations。add(tableRuleConfiguration);});创建数据源PropertiespropertiesnewProperties();设置打印ActualSQL属性,true为开启,false为关闭properties。setProperty(sql。show,false);returnShardingDataSourceFactory。createDataSource(dataSourceMap,shardingRuleConfiguration,properties);} 说明如下:shardingTables是需要分库分表的表集合,我们放入properties文件中进行配置,为了简单我们采用逗号进行分割配置多个表;通过ShardingSphere绑定需要分库分表的多个表;dataSourceMap就是我们上面通过properties文件配置的多个数据源;在ShardingSphere配置我们的分片键和自定义分片算法CustomShardingAlgorithmproperties。setProperty(sql。show,false)配置ShardingSphere是否打印在哪个数据源上执行sql语言; 下面是我们自定义的分片算法CustomShardingAlgorithm类Slf4jpublicclassCustomShardingAlgorithmimplementsPreciseShardingAlgorithmString{OverridepublicStringdoSharding(CollectionStringavailableTargetNames,PreciseShardingValueStringshardingValue){log。info(availableTargetNames:{},shardingValue:{},JSON。toJSONString(availableTargetNames),JSON。toJSONString(shardingValue));StringregionCodeshardingValue。getValue();StringdataSourceNamedsregionC判断是否匹配到配置数据源if(availableTargetNames。contains(dataSourceName)){returndataSourceN}没匹配到配置数据源,使用默认数据源returnMultDSConfig。DEFAULTDATASOURCENAME;}} 说明如下:ShardingSphere配置了绑定表的增删改查sql语句会进入到该自定义算法类中;该算法逻辑比较简单,就是判断出我们sql中的分片键是否在我们配置的数据源中存在,如果存在数据源,则ShardingSphere将sql语句路由到正确的数据库上执行;如果不存在数据源,则ShardingSphere将sql语句路由到默认的数据库上执行;这样防止我们没有配置数据源而报错。 通过以上配置和自定义分片算法,借助ShardingSphere框架我们已经实现了通过携带分片键的增删改查sql语句动态路由数据源。如果操作我们需要分库分表的sql语句没用携带分片键,ShardingSphere会报错并且在日志信息中会看到对应的错误信息。 当然除过ShardingSphere能够实现动态数据源,其他框架或者方式也可以实现,例如:SpringAOP切面,拦截器也可以实现。那么就是仁者见仁,智者见智了。 不积跬步,无以至千里; 不积小流,无以成江海; 参考资料: https:shardingsphere。apache。orgdocumentlegacy4。xdocumentcnoverview