經常使用分庫分表的框架或中間件有MyCat和Sharding-JDBC。MyCat是基於中間件的形式,shrrding-jdbc是基於本地jar包的類庫。sharding-jdbc屬於ShardingSphere體系的一個組件,ShardingSphere體系也有Sharding-Proxy,和MyCat同樣,是獨立部署的中間件。但sharding-jdbc與其不衝突,今天主要是學習了Sharding-JDBC。現將學習心得記錄在這裏,方便本身學習和他人蔘考(估計也不會有人看TAT)。java
這裏用兩個庫,一張邏輯表,一個庫兩張真實表來演示。mysql
sql腳本(基於mysql):算法
CREATE TABLE `t_order_1` ( `id` bigint(20) NOT NULL, `user_id` bigint(20) NOT NULL, `order_id` bigint(20) NOT NULL, `order_no` varchar(30) NOT NULL, `isactive` tinyint(4) NOT NULL DEFAULT '1', `inserttime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, `updatetime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
每一個庫t_order_1和t_order_2各建一張。sql
maven引入當前最新的jar包:apache
<dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>sharding-jdbc-core</artifactId> <version>4.0.0-RC1</version> </dependency>
採起java配置方式,官方支持javaConfig、yml等配置方式,我這裏用的是JavaConfig。session
先將數據源信息寫入yml:mybatis
sharding: datasources: test1: driver-class-name: com.mysql.jdbc.Driver password: 123456 url: jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false username: root test2: driver-class-name: com.mysql.jdbc.Driver password: 123456 url: jdbc:mysql://localhost:3306/test2?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false username: root
而後建個配置信息類,保存全部的數據源信息:app
@Component @ConfigurationProperties(prefix = "sharding") @Data public class DataSourceInfo { private Map<String, Map<String,String>> datasources; }
對應的,建立真實數據源的方法,等會sharding-jdbc會用這些數據源封裝爲一個統一數據源入口。框架
public class DataSourceUtil { public static DataSource createDataSource(String data,DataSourceInfo dataSourceInfo){ DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create(); dataSourceBuilder.url(dataSourceInfo.getDatasources().get(data).get("url")); dataSourceBuilder.driverClassName(dataSourceInfo.getDatasources().get(data).get("driver-class-name")); dataSourceBuilder.username(dataSourceInfo.getDatasources().get(data).get("username")); dataSourceBuilder.password(dataSourceInfo.getDatasources().get(data).get("password")); DataSource dataSource = dataSourceBuilder.build(); return dataSource; } }
/** * @author chenzhicong * @time 2019/8/21 16:40 * @description */ @Configuration public class ShardingJdbcConfig { @Autowired private DataSourceInfo dataSourceInfo; @Bean(name = "shardingDataSource") @Primary DataSource getShardingDataSource() throws SQLException { ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration(); //添加t_order表邏輯表的分片規則配置對象 shardingRuleConfig.getTableRuleConfigs().add(getOrderTableRuleConfiguration()); //shardingRuleConfig.getTableRuleConfigs().add(getOrderItemTableRuleConfiguration()); //添加綁定表 //shardingRuleConfig.getBindingTableGroups().add("t_order, t_order_item"); //添加廣播表 //shardingRuleConfig.getBroadcastTables().add("t_config"); //設置分庫策略 //參數:第一個爲分片列名稱,第二個分片算法行表達式,需符合groovy語法 shardingRuleConfig.setDefaultDatabaseShardingStrategyConfig(new InlineShardingStrategyConfiguration("user_id", "test${user_id % 2 + 1}")); //設置分表策略 //這裏通常不用行表達式分片策略,由於涉及的邏輯表名有多個,通常本身實現一個標準分片策略 shardingRuleConfig.setDefaultTableShardingStrategyConfig(new InlineShardingStrategyConfiguration("order_id", "t_order_${order_id % 2}")); return ShardingDataSourceFactory.createDataSource(createDataSourceMap(), shardingRuleConfig, new Properties()); } private static KeyGeneratorConfiguration getKeyGeneratorConfiguration() { KeyGeneratorConfiguration result = new KeyGeneratorConfiguration("SNOWFLAKE", "id"); return result; } /** * 獲取訂單表分片規則配置對象 */ TableRuleConfiguration getOrderTableRuleConfiguration() { //第一個參數是邏輯表名稱(logicTable),第二個參數是actualDataNodes(真實數據節點),由數據源名 + 表名組成,以小數點分隔(inline表達式)缺省表示使用已知數據源與邏輯表名稱生成數據節點 TableRuleConfiguration result = new TableRuleConfiguration("t_order", "test${1..2}.t_order${0..1}"); result.setKeyGeneratorConfig(getKeyGeneratorConfiguration()); return result; } /** * 獲取訂單商品表分片規則配置對象 */ TableRuleConfiguration getOrderItemTableRuleConfiguration() { TableRuleConfiguration result = new TableRuleConfiguration("t_order_item", "test${1..2}.t_order_item${0..1}"); return result; } Map<String, DataSource> createDataSourceMap() { Map<String, DataSource> result = new HashMap<>(); result.put("test1", DataSourceUtil.createDataSource("test1",dataSourceInfo)); result.put("test2", DataSourceUtil.createDataSource("test2",dataSourceInfo)); return result; } }
@Configuration @MapperScan(basePackages = "com.czc.study.mybatis.dao", sqlSessionFactoryRef = "sessionFactory") public class SessionFactoryConfig { @Bean public SqlSessionFactory sessionFactory(DataSource shardingDataSource) throws Exception { final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(shardingDataSource); return sessionFactory.getObject(); } }
注意了,這裏的@MapperScan是tkMybatis的MapperScan註解(不要踩坑了)。maven
分片有庫分片和表分片,指的是咱們數據在庫和表間分佈的策略。
sharding-jdbc提供多種分片策略,在配置時用了策略模式,須要傳入特定算法來初始化,算法由咱們本身實現,提供最大自由度。,包括:
對應的Java類對象是StandardShardingStrategy,可傳入的算法對象是PreciseShardingAlgorithm和RangeShardingAlgorithm兩個分片算法,前者是精確分片算法,用於處理分片鍵in或=的查詢關係,後增是範圍分片算法,用於處理between and 的查詢關係。在配置時,咱們須要本身實現。好比精確分片算法的方法是:
String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<T> shardingValue);
其中availableTargetNames表明的是可用的庫或表,shardingValue封裝的是分片鍵(攜帶邏輯表名,字段名、值),該方法實現須要返回庫名或者表名,表明數據分佈到哪一個庫或哪一個表。後面介紹的算法對象都是相似這樣的接口,就很少介紹了。
對應策略類對象ComplexShardingStrategy,提供對SQL語句中的=, IN和BETWEEN AND的分片操做支持。對應算法對象ComplexKeysShardingAlgorithm,用於處理使用多鍵做爲分片鍵進行分片的場景,包含多個分片鍵的邏輯較複雜。這個也沒有實際操做過,就很少介紹了。
對應InlineShardingStrategy策略類對象,該策略類初始化有傳入分片鍵和Groovy的表達式,提供對SQL語句中的=和IN的分片操做支持,只支持單分片鍵。 咱們demo中用的就是這個。
對應HintShardingStrategy。經過Hint而非SQL解析的方式分片的策略。對應算法對象HintShardingAlgorithm,主要用於分片字段非SQL決定,而由其餘外置條件決定的場景。
路由策略指的是根據分片策略shrad-jdbc將把原始sql路由到哪些庫執行。具體方式有:
好比原始SQL是:
SELECT * FROM t_order o JOIN t_order_item i ON o.order_id=i.order_id WHERE order_id IN (1, 2);
若是有綁定鍵
SELECT * FROM t_order_0 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE order_id IN (1, 2); SELECT * FROM t_order_1 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE order_id IN (1, 2);
但沒有綁定鍵的話,不能肯定order_id相等表的序號也相等,因而:
SELECT * FROM t_order_0 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE order_id IN (1, 2); SELECT * FROM t_order_0 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE order_id IN (1, 2); SELECT * FROM t_order_1 o JOIN t_order_item_0 i ON o.order_id=i.order_id WHERE order_id IN (1, 2); SELECT * FROM t_order_1 o JOIN t_order_item_1 i ON o.order_id=i.order_id WHERE order_id IN (1, 2);
廣播路由,不攜帶分片鍵的SQL,將進行廣播路由,廣播路由分爲全庫表路由、全實例路由、單播路由、阻斷路由,咱們只須要了解全庫表路由便可,其餘的SQL都不屬於crud,全庫表路由值得是針對一個SQL,sharding-jdbc會在全部庫都執行一次。
圖示:
針對,邏輯SQL(表示業務代碼中寫的sql),某些狀況sharding-jdbc將會對其進行改寫,主要狀況有:
須要注意的是,對於單節點路由,則不會進行改寫。
對於排序、分組排序的歸併不必定將查詢結果搞到內存,而後在內存中進行歸併,sharding-jdbc有流式分組歸併的概念,即每一次從結果集中獲取到的數據,都可以經過逐條獲取的方式返回正確的單條數據。但對於分組排序的要求必須是SQL的排序項與分組項的字段以及排序類型(ASC或DESC)。
關於sharding-jdbc的核心概念和配置demo就總結到這裏,大概對其有了必定了解,再總結一下配置的方式首先是先獲取到各個真實數據源的datasource對象,而後建立分片規則配置對象ShardingRuleConfiguration,經過分片規則配置對象添加各個邏輯表的分片規則配置對象TableRuleConfiguration(這裏能夠配置各個TableRuleConfiguration的主鍵生成策略,能夠選擇使用用雪花算法),而後添加各個綁定表組,經過傳入本身實現的算法配置分片策略(這裏須要注意,最好每一個表都有相同的字段做爲分片鍵,這樣比較好處理-我的見解,可能有可能不對,由於也沒有經歷過生產實踐),而後再經過ShardingDataSourceFactory結合真實數據源建立統一的ShardingDataSource,最後注入到咱們本身的持久層框架使用的數據源中就好了。