分库分表以及Sharding-Jdbc总结(一)

 

目录

前言

分库分表

现成分库分表框架

Sharding-JDBC

简介

架构图

个人看法

概念

数据源

分片键

注意要点

分片策略

pom.xml

项目

运行输出

个人思考

数据拆分的方式

个人总结

1.取模

2.范围方案

文中总结

分布式id

应用

资料

Springboot整合Sharding-Jdbc

pom.xml

配置

Mapper.xml

自定义分片策略

github

分库配置


前言

当数据表的数据量达到百万,千万级别的时候,性能是比较差的。这个时候我们需要对它进行分库分表。

 

分库分表

分库:数据量很多,分成多个数据库储存

分表:储存到多个表中

 

现成分库分表框架

参考这一篇

功能 Cobar Mycat Heisenberg TDDL Sharding-JDBC
是否开源 开源 开源 开源 部分开源 开源
架构模型 Proxy架构 Proxy架构 Proxy架构 应用集成架构 应用集成架构
数据库支持 MySQL 任意 任意 任意 MySQL(计划Oracle)
外围依赖 Diamond
使用复杂度 一般 一般 一般 复杂 一般
技术文档支持 较少 付费 较少 一般
开源组织 阿里 社区(Cobar衍生) 社区(Cobar衍生) 阿里 当当

 

个人接触到的就Mycat还有Sharding-JDBC

MyCat应该是应用级别上的分库分表,它还可以实现mysql集群的管理。

 

下面摘自其他文章

  1. 基于Proxy的架构的缺点:网络消耗会产生性能问题,并且多一个外围系统依赖就意味着需要多增加和承担一份风险

 

这一篇主要总结Sharding-JDBC

 

Sharding-JDBC

简介

Sharding-JDBC是当当开源的数据库分库分表中间件。Sharding-JDBC直接封装JDBC协议,可以理解为增强版的JDBC驱动,旧代码迁移成本几乎为零。Sharding-JDBC定位为轻量级java框架,使用客户端直连数据库,以jar包形式提供服务,无proxy代理层,无需额外部署,无其他依赖,DBA也无需改变原有的运维方式

 

架构图

分库分表以及Sharding-Jdbc总结(一)_第1张图片

 

个人看法

从简介还有架构图中,我们可以看出sharding-jdbc是通过分片策略改写sql语句,最后进行分库分表的。不像Mycat通过应用级别去操作。

 

概念

数据源

应用配置数据库的数据源,特别是分库

jdbc_driver0=com.mysql.jdbc.Driver
jdbc_url0=jdbc:mysql://localhost:3306/sharding_0?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&autoReconnect=true
jdbc_username0=root
jdbc_password0=123456

jdbc_driver1=com.mysql.jdbc.Driver
jdbc_url1=jdbc:mysql://localhost:3306/sharding_1?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&autoReconnect=true
jdbc_username1=root
jdbc_password1=123456

 

分片键

通过分片键来匹配分库规则,从而进行改写数据库

分库分表以及Sharding-Jdbc总结(一)_第2张图片

 

注意:分片键的数据库类型为int(11)

 

yml配置

分库分表以及Sharding-Jdbc总结(一)_第3张图片

注意要点

分片键要跟查询字段一致,包括大小,如果大小不匹配,会插到到所有表中

 

分片策略

public class UserSingleKeyDatabaseShardingAlgorithm implements SingleKeyDatabaseShardingAlgorithm{  
  
    /** 
     * sql 中关键字 匹配符为 =的时候,表的路由函数 
     */  
    public String doEqualSharding(Collection availableTargetNames, ShardingValue shardingValue) {  
        for (String each : availableTargetNames) {  
            if (each.endsWith(shardingValue.getValue() % 2 + "")) {  
                return each;  
            }  
        }  
        throw new IllegalArgumentException();  
    }  
  
    /** 
     * sql 中关键字 匹配符为 in 的时候,表的路由函数 
     */  
    public Collection doInSharding(Collection availableTargetNames, ShardingValue shardingValue) {  
        Collection result = new LinkedHashSet(availableTargetNames.size());  
        for (Integer value : shardingValue.getValues()) {  
            for (String tableName : availableTargetNames) {  
                if (tableName.endsWith(value % 2 + "")) {  
                    result.add(tableName);  
                }  
            }  
        }  
        return result;  
    }  
  
    /** 
     * sql 中关键字 匹配符为 between的时候,表的路由函数 
     */  
    public Collection doBetweenSharding(Collection availableTargetNames,  
            ShardingValue shardingValue) {  
        Collection result = new LinkedHashSet(availableTargetNames.size());  
        Range range = (Range) shardingValue.getValueRange();  
        for (Integer i = range.lowerEndpoint(); i <= range.upperEndpoint(); i++) {  
            for (String each : availableTargetNames) {  
                if (each.endsWith(i % 2 + "")) {  
                    result.add(each);  
                }  
            }  
        }  
        return result;  
    }  
  
}  

 

Collection availableTargetNames, ShardingValue shardingValue

availableTargetNames:所有的资源列表,比如数据表:user_0,user_1...

shardingValue:输入的分片键的值,比如设定user_id,输入为2

 

通过定义分片算法来实现分片

 

或者Springboot配置

分库分表以及Sharding-Jdbc总结(一)_第4张图片

 

pom.xml


    org.apache.shardingsphere
    sharding-jdbc-core
    ${sharding-sphere.version}

 

项目

别人的一个项目

我刚刚也修改了里面一个bug,九色分片键大小写问题导致插入所有表

 

可以看到他通过xml配置以及重写SingleKeyDatabaseShardingAlgorithm,我们也可以通过springboot进行配置

 

运行输出

DEBUG MemberMapper.insert - ==>  Preparing: insert into t_member (ID,NAME,STRATEGY, CARD) values (?,?,?,?) 
DEBUG MemberMapper.insert - ==> Parameters: 6263c1ad-86b6-4dfa-9890-5df326662d32(String), 1(String), 3(Long), 2(String)
DEBUG parser.SQLParserFactory - Logic SQL: insert into t_member (ID,NAME,STRATEGY,  CARD) values (?,?,?,?)
DEBUG parser.SQLParseEngine - Parsed SQL result: SQLParsedResult(routeContext=RouteContext(tables=[Table(name=t_member, alias=Optional.absent())], sqlBuilder=null), conditionContexts=[ConditionContext(conditions={Condition.Column(columnName=STRATEGY, tableName=t_member)=Condition(column=Condition.Column(columnName=STRATEGY, tableName=t_member), operator==, values=[3])})], mergeContext=MergeContext(orderByColumns=[], groupByColumns=[], aggregationColumns=[], limit=null))
DEBUG parser.SQLParseEngine - Parsed SQL: INSERT INTO [Token(t_member)] (ID, NAME, STRATEGY, CARD) VALUES (?, ?, ?, ?)
**************************************tableNames:[t_member_0, t_member_1, t_member_2]************************************************************
**************************************shardingValue:ShardingValue(columnName=STRATEGY, value=3, values=[], valueRange=null)************************************************************
DEBUG router.SQLExecutionUnit - route sql to db: [sharding_1] sql: [INSERT INTO t_member_0 (ID, NAME, STRATEGY, CARD) VALUES (?, ?, ?, ?)]
DEBUG MemberMapper.insert - <==    Updates: 1

 

可以看到其中的sql解析以及改写

 

个人思考

既然分片储存了,那么碰到跨库查询,或者跨表查询的怎么办?因为数据表数据量都很大,范围查询很慢的。(分库分表范围查询都会去查询所有表符合的数据

那么我们需要比较好的拆分数据,范围查询时可以指定特定库进行查询。分表的时候要根据业务来拆数据,比如我是一个统计功能为主的,可以根据时间去拆,比如时间字段,数据为20190618,表示某一天,作为一个分片键。那么一天的数据都在一张表里面,我们到时可以根据时间字段直接找到数据。

缺点:存在数据热点,比如我一天数据量特别大,其他的数据量很少

如果说没有这种需求,我们可以通过id取模进行储存。

特定:数据均匀

那么他们之间怎么处理呢?看下一节数据拆分的方式

数据拆分的方式

这一篇讲得很详细

 

个人总结

1.取模

包括很多id取模,时间hash之后取模

订单数据可以均匀的放到那4张表中,这样此订单进行操作时,就不会有热点问题。

2.范围方案

比如id超过1w就跳到下一张表,或者符合什么范围就跳

  • 优点

我们小伙伴们想一下,此方案是不是有利于将来的扩容,不需要做数据迁移。即时再增加4张表,之前的4张表的范围不需要改变,id=12的还是在0表,id=1300万的还是在1表,新增的4张表他们的范围肯定是 大于 4000万之后的范围划分的。

  • 缺点

有热点问题,我们想一下,因为id的值会一直递增变大,那这段时间的订单是不是会一直在某一张表中,如id=1000万 ~ id=2000万之间,这段时间产生的订单是不是都会集中到此张表中,这个就导致1表过热,压力过大,而其他的表没有什么压力。

 

文中总结

文章最后也给了总结:先按照范围在按取模。

这样有什么好处呢?

好处:查询可以指定到特定的数据表范围,又可以避免数据热点

分库分表以及Sharding-Jdbc总结(一)_第5张图片

 

分布式id

这个也是我们需要解决的,不可能说使用主键,网上很多方案,什么redis集群发号,雪花算法(推特 Snowflake算法实现)

可以参照下这一篇

它大概由3部分组成:

分库分表以及Sharding-Jdbc总结(一)_第6张图片

Bits    名字    说明
1    符号位    等于 0
41    时间戳    从 2016/11/01 零点开始的毫秒数,支持 2 ^41 /365/24/60/60/1000=69.7年
10    工作进程编号    支持 1024 个进程
12    序列号    每毫秒从 0 开始自增,支持 4096 个编号
 

应用

我们在插入的时候要设置id为分布式唯一id

 

资料

目前网上学习资料都蛮少的

https://github.com/1181888200/sharding-jdbc-study别人的一个入门项目

https://shardingsphere.apache.org/document/current/en/overview/官网

https://cloud.tencent.com/developer/article/1441250分库分表注意事项

 

Springboot整合Sharding-Jdbc

参考尹吉欢尹大大的一篇文章

采用mybatis,好像使用jpa框架不行

pom.xml

        
            io.shardingjdbc
            sharding-jdbc-spring-boot-starter
            2.0.0.M3
        

配置

sharding.jdbc.datasource.names=sharding_0
# 数据源
sharding.jdbc.datasource.sharding_0.type=com.alibaba.druid.pool.DruidDataSource
sharding.jdbc.datasource.sharding_0.driver-class-name=com.mysql.jdbc.Driver
sharding.jdbc.datasource.sharding_0.url=jdbc:mysql://localhost:3306/sharding_0?characterEncoding=utf-8
sharding.jdbc.datasource.sharding_0.username=root
sharding.jdbc.datasource.sharding_0.password=123456
# 分表配置
sharding.jdbc.config.sharding.tables.t_user.actual-data-nodes=sharding_0.t_user_${0..2}
#自定义分片
sharding.jdbc.config.sharding.tables.t_user.table-strategy.standard.sharding-column=user_id
sharding.jdbc.config.sharding.tables.t_user.table-strategy.standard.precise-algorithm-class-name=com.example.demo.MyPreciseShardingAlgorithm
#默认hash分片
#sharding.jdbc.config.sharding.tables.t_user.table-strategy.inline.sharding-column=user_id
#sharding.jdbc.config.sharding.tables.t_user.table-strategy.inline.algorithm-expression=t_user_${user_id.longValue() % 3}

mybatis.mapperLocations=classpath:mapper/*Mapper.xml

上面已经定义了两种方式:一种是取模,一种是自定义

sharding_0库名

t_user_0,t_user_1,t_user_2表名

Mapper.xml




  
      
    
      
      
    
    
    
    insert into t_user (user_id,name,age) values (#{userId},#{name},#{age})  
    

    
  

注意:数据表名写数据表头就行,比如t_user

自定义分片策略

public class MyPreciseShardingAlgorithm implements PreciseShardingAlgorithm {

    /*PreciseShardingAlgorithm是必选的,用于处理=和IN的分片。
    RangeShardingAlgorithm是可选的,用于处理BETWEEN AND分片,如果不配置RangeShardingAlgorithm,SQL中的BETWEEN AND将按照全库路由处理。*/
    @Override
    public String doSharding(Collection availableTargetNames, PreciseShardingValue shardingValue) {
        for (String tableName : availableTargetNames) {
            if (tableName.endsWith(shardingValue.getValue() % 3 + "")) {
                return tableName;
            }
        }
        throw new IllegalArgumentException();
    }

}

下面摘自文章内容:

我们这边引入的Spring Boot Starter包是2.x的版本,在这个版本中,分片算法的接口有调整,我们需要用到标准分片策略StandardShardingStrategy。提供对SQL语句中的=, IN和BETWEEN AND的分片操作支持。

StandardShardingStrategy只支持单分片键,提供PreciseShardingAlgorithm和RangeShardingAlgorithm两个分片算法。

PreciseShardingAlgorithm是必选的,用于处理=和IN的分片。RangeShardingAlgorithm是可选的,用于处理BETWEEN AND分片,如果不配置RangeShardingAlgorithm,SQL中的BETWEEN AND将按照全库路由处理。

 

github

https://github.com/dajitui/sharding-jdbc1

 

分库配置

参考文章

# 根据merchant列进行分库
sharding.jdbc.config.sharding.default-database-strategy.standard.sharding-column=merchant
# 自定义分库算法
sharding.jdbc.config.sharding.default-database-strategy.standard.precise-algorithm-class-name=com.afei.boot.util.DbShardingAlgorithm

sharding.jdbc.config.props.sql.show=true

 

你可能感兴趣的:(分库分表以及Sharding-Jdbc总结(一))