sharding-jdbc:实现分库分表(一)

一、分片列(Sharding Column)
分片列。例如,对user ID使用modulo操作来分割表,分片列是user ID,它还支持多个分片列。注意:如果查询SQL中没有分片列,那么所有表将被路由到性能较差的所有数据节点

二、分片算法(Sharding Algorithm)
(1.1)精确切分算法(Precise sharding algorithm)
用于在SQL中处理 =和IN 单个分片列,并与StandardShardingStrategy 一起使用。

(1.2)范围切分算法(Range sharding algorithm)
用于在SQL中处理 BETWEEN 单个分片列,并与StandardShardingStrategy 一起使用。

(1.3)复杂的钥匙切分算法(Complex keys sharding algorithm)
用于处理=、IN和BETWEEN的多个分片列,并与ComplexShardingStrategy 一起使用。

(1.4)提示切分算法(Hint sharding algorithm)
用来提示分片,并与HintShardingStrategy一起使用。

三、分片策略(Sharding Strategy)
3.1 精确切分策略(Standard sharding strategy)
支持 =、IN、BETWEEN在 SQL中使用单个分片列。它提供了PreciseShardingAlgorithm和RangeShardingAlgorithm,PreciseShardingAlgorithm是必需的,用于处理=和IN。RangeShardingAlgorithm是可选的,用于处理BETTWEN,如果没有,则不会触发分片,只需要路由到所有的数据节点。

3.2 复杂的分片策略(Complex sharding strategy)
支持=,IN、BETWEEN之间在SQL中有多个分片列,由于具有多个分片列的复杂性,最终用户需要自己处理。

3.3 (内联分片策略) Inline sharding strategy
仅对单个分片列使用groovy表达式,支持 =和IN sharding在SQL中。对于简单的分片算法,请使用内联表达式来避免java代码。例如:t_user_${u_id % 8}表示表t_user sharding by u_id,表附录的结果是u_id mod 8,实际表的名称从t_user_0到t_user_7。

3.4 (提示分片策略)Hint sharding strategy
使用提示而不是SQL来指示分片结果。

四、采用精确切分算法实现分库分表

/**
 * 数据源配置
 */
@Configuration
@MapperScan(basePackages = "com.example.demo.mapper", sqlSessionTemplateRef = "testSqlSessionTemplate")
public class DataSourceConfig {

    @Bean(name = "shardingDataSource")
    DataSource getShardingDataSource() throws SQLException {
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
        shardingRuleConfig.getTableRuleConfigs().add(getUserTableRuleConfiguration());
        //如果有多个表,可以用逗号“,”分隔 ,比如user_info,t_order
        shardingRuleConfig.getBindingTableGroups().add("user_info");

        //设置分片策略,自定义算法来实现分片规则
        shardingRuleConfig.setDefaultDatabaseShardingStrategyConfig(new     StandardShardingStrategyConfiguration("user_id", new DemoDatabaseShardingAlgorithm()));
        shardingRuleConfig.setDefaultTableShardingStrategyConfig(new StandardShardingStrategyConfiguration("user_id", new DemoTableShardingAlgorithm()));

        return ShardingDataSourceFactory.createDataSource(createDataSourceMap(), shardingRuleConfig,new ConcurrentHashMap(), new Properties());
    }

/**
 * 配置表规则
 * @return
 */
@Bean
TableRuleConfiguration getUserTableRuleConfiguration() {
    TableRuleConfiguration orderTableRuleConfig = new TableRuleConfiguration();
    //配置表名
    orderTableRuleConfig.setLogicTable("user_info");
    //配置真实的数据节点,即数据库中真实存在的节点,由数据源名 + 表名组成
    orderTableRuleConfig.setActualDataNodes("user_${0..1}.user_info_${0..1}");//user_${0..1}分库,user_info_${0..1}分表
    //主键生成列,默认的主键生成算法是snowflake
    orderTableRuleConfig.setKeyGeneratorColumnName("user_id");
    return orderTableRuleConfig;
}

/**
 * 创建数据源
 * @return
 */
private Map<String, DataSource> createDataSourceMap() {
    Map<String, DataSource> result = new HashMap<>();
    result.put("user_0", createDataSource("user_0"));
    result.put("user_1", createDataSource("user_1"));
    return result;
}

/**
 * 创建数据源
 * @param dataSourceName
 * @return
 */
private DataSource createDataSource(final String dataSourceName) {
    DruidDataSource datasource = new DruidDataSource();
    datasource.setDriverClassName(com.mysql.jdbc.Driver.class.getName());
    datasource.setUrl(String.format("jdbc:mysql://localhost:3306/%s", dataSourceName));
    datasource.setUsername("root");
    datasource.setPassword("123456");
    return datasource;
}

/**
 * 需要手动配置事务管理器
 *
 * @param shardingDataSource
 * @return
 */
@Bean
public DataSourceTransactionManager transactitonManager(DataSource shardingDataSource) {
    return new DataSourceTransactionManager(shardingDataSource);
}

@Bean
@Primary
public SqlSessionFactory sqlSessionFactory(DataSource shardingDataSource) throws Exception {
    SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
    bean.setDataSource(shardingDataSource);
    bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
    return bean.getObject();
}

@Bean
@Primary
public SqlSessionTemplate testSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) throws Exception {
    return new SqlSessionTemplate(sqlSessionFactory);
}

}

自定义分库算法实现类:DemoDatabaseShardingAlgorithm

public class DemoDatabaseShardingAlgorithm implements PreciseShardingAlgorithm<Long> {
@Override
public String doSharding(Collection<String> collection, PreciseShardingValue<Long> preciseShardingValue) {
    for (String each : collection) {
        if (each.endsWith(Long.parseLong(preciseShardingValue.getValue().toString()) % 2+"")) {
            return each;
        }
    }
    throw new IllegalArgumentException();
}

}

自定义分表算法实现类:DemoTableShardingAlgorithm

public class DemoTableShardingAlgorithm implements PreciseShardingAlgorithm<Long> {
@Override
public String doSharding(Collection<String> collection, PreciseShardingValue<Long> preciseShardingValue) {
    for (String each : collection) {
        if (each.endsWith(Long.parseLong(preciseShardingValue.getValue().toString()) % 2+"")) {
            return each;
        }
    }
    throw new IllegalArgumentException();
}

}

需要创建数据库user_0,user_1。
user_0数据库创建表:user_info_0,user_info_1。
user_1数据库创建表:user_info_0,user_info_1。
测试:

@RunWith(SpringRunner.class)
@SpringBootTest
public class DemoApplicationTests {
  public static Long userId = 600L;
  @Resource
  UserInfoMapper userInfoMapper;

  @Test
  public void demo() {

    for (int i = 1; i <= 30; i++) {
        UserInfo userInfo = new UserInfo();
        userInfo.setUserId(userId);
        userInfo.setAccount("Account" + i);
        userInfo.setPassword("pass" + i);
        userInfo.setUserName("name" + i);
        userId++;
        if(i==3){
            HintManagerHolder.clear();
            HintManager hintManager = HintManager.getInstance();
            hintManager.addDatabaseShardingValue("user_info", "user_id", 3L);
            hintManager.addTableShardingValue("user_info", "user_id", 3L);
            System.out.println(userId);
        }
        userInfoMapper.insert(userInfo);
      }
  }

}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354