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

 

目录

前言

分库分表

现成分库分表框架

Sharding-JDBC

简介

架构图

个人看法

概念

数据源

分片键

注意要点

分片策略

pom.xml

项目

运行输出

个人思考

数据拆分的方式

个人总结

1.取模

2.范围方案

文中总结

分布式id

应用

资料

Springboot整合Sharding-Jdbc

pom.xml

配置

Mapper.xml

自定义分片策略

github

分库配置


前言

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

 

分库分表

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

分表:储存到多个表中

 

现成分库分表框架

参考这一篇

功能CobarMycatHeisenbergTDDLSharding-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是通过分片策略改写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

 

分片键

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

 

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

 

yml配置

注意要点

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

 

分片策略

public class UserSingleKeyDatabaseShardingAlgorithm implements SingleKeyDatabaseShardingAlgorithm<Integer>{  
  
    /** 
     * sql 中关键字 匹配符为 =的时候,表的路由函数 
     */  
    public String doEqualSharding(Collection<String> availableTargetNames, ShardingValue<Integer> shardingValue) {  
        for (String each : availableTargetNames) {  
            if (each.endsWith(shardingValue.getValue() % 2 + "")) {  
                return each;  
            }  
        }  
        throw new IllegalArgumentException();  
    }  
  
    /** 
     * sql 中关键字 匹配符为 in 的时候,表的路由函数 
     */  
    public Collection<String> doInSharding(Collection<String> availableTargetNames, ShardingValue<Integer> shardingValue) {  
        Collection<String> result = new LinkedHashSet<String>(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<String> doBetweenSharding(Collection<String> availableTargetNames,  
            ShardingValue<Integer> shardingValue) {  
        Collection<String> result = new LinkedHashSet<String>(availableTargetNames.size());  
        Range<Integer> range = (Range<Integer>) 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<String> availableTargetNames, ShardingValue<Integer> shardingValue

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

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

 

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

 

或者Springboot配置

 

pom.xml

<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>sharding-jdbc-core</artifactId>
    <version>${sharding-sphere.version}</version>
</dependency>

 

项目

别人的一个项目

我刚刚也修改了里面一个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表过热,压力过大,而其他的表没有什么压力。

 

文中总结

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

这样有什么好处呢?

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

 

分布式id

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

可以参照下这一篇

它大概由3部分组成:

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

        <dependency>
            <groupId>io.shardingjdbc</groupId>
            <artifactId>sharding-jdbc-spring-boot-starter</artifactId>
            <version>2.0.0.M3</version>
        </dependency>

配置

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

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.dao.UserDao" >
  <resultMap id="resultMap" type="com.example.demo.entity.User" >
    <id column="id" property="id" jdbcType="INTEGER" />  
    <result column="user_id" property="userId" jdbcType="INTEGER" />
    <result column="name" property="name" jdbcType="VARCHAR" />  
    <result column="age" property="age" jdbcType="INTEGER" />  
  </resultMap>  
    
  <insert id="insert">  
    insert into t_user (user_id,name,age) values (#{userId},#{name},#{age})  
  </insert>  

    
</mapper>  

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

自定义分片策略

public class MyPreciseShardingAlgorithm implements PreciseShardingAlgorithm<Integer> {

    /*PreciseShardingAlgorithm是必选的,用于处理=和IN的分片。
    RangeShardingAlgorithm是可选的,用于处理BETWEEN AND分片,如果不配置RangeShardingAlgorithm,SQL中的BETWEEN AND将按照全库路由处理。*/
    @Override
    public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Integer> 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

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值