当前位置: 首页 > news >正文

ShardingSphere 分库分表

中间件

常用中间件

MyCat

  • 是基于 Proxy,它复写了 MySQL 协议,将 Mycat Server 伪装成⼀个 MySQL 数据库
  • 客户端所有的jdbc请求都必须要先交给MyCat,再有 MyCat转发到具体的真实服务器
  • 缺点是效率偏低,中间包装了⼀层
  • 代码⽆侵⼊性

ShardingSphere下的Sharding-JDBC

  • 基于jdbc驱动,不⽤额外的proxy,在本地应⽤层重写Jdbc 原⽣的⽅法,实现数据库分⽚形式
  • 是基于 JDBC 接⼝的扩展,是以 jar 包的形式提供轻量级服务的,性能⾼
  • 代码有侵⼊性

两者的区别和相同点

相同点:
流程都是SQL解析–>SQL路由–>SQL改
写–>结果归并
区别:

ShardingSphere

简介

  • 是⼀套开源的分布式数据库解决⽅案组成的⽣态圈,定位为 Database Plus
  • 它由 JDBC、Proxy 和 Sidecar这 3款既能够独⽴部署,⼜⽀持混合部署配合使⽤的产品组成

三大构成

ShardingSphere-Sidecar
ShardingSphere-JDBC
ShardingSphere-Proxy

水平分表常见分片策略

  • ⾏表达式分⽚策略 InlineShardingStrategy
    只⽀持【单分⽚键】使⽤Groovy的表达式,提供对SQL语
    句中的 =和IN 的分⽚操作⽀持
  • 标准分⽚策略StandardShardingStrategy
    只⽀持【单分⽚键】
    • PreciseShardingAlgorithm 精准分⽚ 是必选的,⽤于处理
      =和IN的分⽚
    • RangeShardingAlgorithm 范围分配 是可选的,⽤于处理
      BETWEEN AND分⽚
  • 复合分⽚策略ComplexShardingStrategy
    ⽀持【多分⽚键】,提供对SQL语句中的=, IN和BETWEEN AND的分⽚操作⽀
  • Hint分⽚策略HintShardingStrategy

springboot整合sharding-jdbc

添加依赖

        <dependency><groupId>org.apache.shardingsphere</groupId><artifactId>sharding-jdbc-spring-boot-starter</artifactId></dependency>

配置文件

# 数据源 ds0 第一个数据库shardingsphere:datasource:#数据源名称names: ds0ds0:connectionTimeoutMilliseconds: 30000driver-class-name: com.mysql.cj.jdbc.DriveridleTimeoutMilliseconds: 60000jdbc-url: jdbc:mysql://120.79.150.146:3306/dcloud_account?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=truemaintenanceIntervalMilliseconds: 30000maxLifetimeMilliseconds: 1800000maxPoolSize: 50minPoolSize: 50password: 123type: com.zaxxer.hikari.HikariDataSourceusername: rootprops:# 打印执行的数据库以及语句sql:show: truesharding:tables:traffic:
# 指定traffic表的数据分布情况,配置数据节点,行表达式标识符使用 ${...} 或 $->{...},但前者与 Spring 本身的文件占位符冲突,所以在 Spring 环境中建议使用 $->{...}actual-data-nodes: ds0.traffic_$->{0..1}
#水平分表策略+行表达式分片table-strategy:inline:algorithm-expression: traffic_$->{ account_no % 2 }sharding-column: account_no
#id生成策略key-generator:column: idprops:worker:id: ${workId}#id生成策略type: SNOWFLAKE

分库分表暴露的问题-ID冲突

常见解决方案

数据库自增ID

设置不同的自增步长
auto_increment_offset、auto-increment-increment
缺点
依靠数据库系统的功能实现,但是未来扩容麻烦
主从切换时的不⼀致可能会导致重复发号
性能瓶颈存在单台sql上

UUID

性能⾮常⾼,没有⽹络消耗
缺点
⽆序的字符串,不具备趋势⾃增特性
UUID太⻓,不易于存储,浪费存储空间,很多场景不适⽤

Redis 发号器

利⽤Redis的INCR和INCRBY来实现,原⼦操作,线程安全,性能⽐Mysql强劲
缺点
需要占⽤⽹络资源,增加系统复杂度

SnowFlake雪花算法

twitter 开源的分布式 ID ⽣成算法,代码实现简单、不占⽤宽带、数据迁移不受影响
⽣成的 id 中包含有时间戳,所以⽣成的 id 按照时间递增
部署了多台服务器,需要保证系统时间⼀样,机器编号不⼀样
缺点
依赖系统时钟(多台服务器时间⼀定要⼀样)

SnowFlake雪花算法

简介

twitter⽤scala语⾔编写的⾼效⽣成唯⼀ID的算法
优点
⽣成的ID不重复
算法性能⾼
基于时间戳,基本保证有序递增

雪花算法⽣成的数字

  • long类,所以就是8个byte,64bit
  • ⽣成的唯⼀值⽤于数据库主键,不能是负数,所以值为
    0~9223372036854775807(2的63次⽅-1)
    在这里插入图片描述

两个重要的点

保证workId不能重复

解决方法:
自定义workId

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;@Configuration
@Slf4j
public class SnowFlakeWordIdConfig {/*** 动态指定sharding jdbc 的雪花算法中的属性work.id属性* 通过调用System.setProperty()的方式实现,可用容器的 id 或者机器标识位* workId最大值 1L << 100,就是1024,即 0<= workId < 1024* {@link SnowflakeShardingKeyGenerator#getWorkerId()}**/static {try {InetAddress inetAddress = Inet4Address.getLocalHost();String hostAddressIp = inetAddress.getHostAddress();String workId = Math.abs(hostAddressIp.hashCode()) % 1024+"";System.setProperty("workId",workId);log.info("workId:{}",workId);} catch (UnknownHostException e) {e.printStackTrace();}}}

配置文件中

#id生成策略key-generator:column: idprops:worker:id: ${workId}#id生成策略type: SNOWFLAKE
时钟回拨

org.apache.shardingsphere.core.strategy.keygen.SnowflakeShardingKeyGenerator 已经解决了时钟回拨问题,看下源码

核心流程:
最后一次生成主键的时间 lastTime,和当前系统时间比较 currTime ,如果 lastTime < currTime ,则正常,如果lastTime > currTIme ,如果时间差在容忍范围内,则线程休眠时间差值,如果时间差大于容忍范围,则直接报异常。

    public synchronized Comparable<?> generateKey() {long currentMilliseconds = timeService.getCurrentMillis();// 判断是否需要等待容忍时间差,如果需要,则等待时间差过去,再获取当前系统时间if (this.waitTolerateTimeDifferenceIfNeed(currentMilliseconds)) {currentMilliseconds = timeService.getCurrentMillis();}// 如果最后一次毫秒与当前系统时间毫秒相同,即还在同一毫秒内if (this.lastMilliseconds == currentMilliseconds) {/**&位与运算符:两个数都转为二进制,如果相对应位都是1,则结果为1,否则为0当序列为4095时,4095+1后的新序列与掩码进行位与运算结果是当序列为其他值时,位与运算结果都不会是0即本毫秒的序列已经用到最大值4096,此时要取下一个毫秒时间值*/if (0L == (this.sequence = this.sequence + 1L & 4095L)) {currentMilliseconds = this.waitUntilNextTime(currentMilliseconds);}} else {// 上一毫秒已经过去,把序列值重置为1this.vibrateSequenceOffset();this.sequence = (long)this.sequenceOffset;}// 记录最新的时间戳this.lastMilliseconds = currentMilliseconds;/**XX......xxx000000000000000000000000时间差XXXXXXXXXXXX0000000000000 机器ID XXXXXXXXXXXX序列号×序列号 xx三部分进行|位或运算:如果相对应位都是0,则结果为0,否则为1*/return currentMilliseconds - EPOCH << 22 | this.getWorkerId() << 12 | this.sequence;}
private boolean waitTolerateTimeDifferenceIfNeed(long currentMilliseconds) {try {// 如果获取ID时的最后一次时间毫秒数小于等于当前系统时间毫秒数,属于正常情况,则不需要等待if (this.lastMilliseconds <= currentMilliseconds) {return false;} else {// 时钟回拨的情况(生成序列的时间大于当前系统的时间),需要等等待时间差long timeDifferenceMilliseconds = this.lastMilliseconds - currentMilliseconds;// 获取ID时的最后一次毫秒数减去当前系统时间毫秒数的时间差// 时间差小于最大容忍时间差,即当前还在时钟回拨的时间差之内Preconditions.checkState(timeDifferenceMilliseconds < (long)this.getMaxTolerateTimeDifferenceMilliseconds(), "Clock is moving backwards, last time is %d milliseconds, current time is %d milliseconds", new Object[]{this.lastMilliseconds, currentMilliseconds});// 线程休眠时间差Thread.sleep(timeDifferenceMilliseconds);return true;}} catch (Throwable var5) {throw var5;}}

具体业务中,使用该方法生成唯一账号

import org.apache.shardingsphere.core.strategy.keygen.SnowflakeShardingKeyGenerator;
public class IDUtil {private static SnowflakeShardingKeyGenerator shardingKeyGenerator = new SnowflakeShardingKeyGenerator();/*** 雪花算法生成器* @return*/public static   Comparable<?> geneSnowFlakeID(){return shardingKeyGenerator.generateKey();}
}
//生成唯一的账号
accountDO.setAccountNo(Long.valueOf(IDUtil.geneSnowFlakeID().toString()));

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【测试项目】——个人博客系统自动化测试
  • 《微信小程序实战(3) · 推广海报制作》
  • Oracle表空间管理(二)
  • Servlet入门:服务端小程序的初试(自己学习整理的资料)
  • Spring Boot实战:使用策略模式优化商品推荐系统
  • Linux的基础知识
  • Python | Leetcode Python题解之第433题最小基因变化
  • nlohmann json:读写json文件
  • seL4 Threads(四)
  • 华为HarmonyOS灵活高效的消息推送服务(Push Kit) -- 10 推送实况窗消息
  • esp32 wifi 联网后,用http 发送hello 用pc 浏览器查看网页
  • 微软宣布弃用WSUS,企业用户尽早准备替换方案
  • 内网基础知识
  • 【Python报错已解决】AttributeError: ‘WindowsPath‘ object has no attribute ‘rstrip‘
  • 如何使用Apache Kafka处理实时数据
  • python3.6+scrapy+mysql 爬虫实战
  • 2017 年终总结 —— 在路上
  • Android路由框架AnnoRouter:使用Java接口来定义路由跳转
  • CAP 一致性协议及应用解析
  • css的样式优先级
  • CSS魔法堂:Absolute Positioning就这个样
  • css系列之关于字体的事
  • java 多线程基础, 我觉得还是有必要看看的
  • JavaScript设计模式系列一:工厂模式
  • leetcode-27. Remove Element
  • SpiderData 2019年2月23日 DApp数据排行榜
  • thinkphp5.1 easywechat4 微信第三方开放平台
  • 个人博客开发系列:评论功能之GitHub账号OAuth授权
  • 后端_MYSQL
  • 聊聊spring cloud的LoadBalancerAutoConfiguration
  • 每天一个设计模式之命令模式
  • 什么软件可以剪辑音乐?
  • 微信小程序实战练习(仿五洲到家微信版)
  • 移动端唤起键盘时取消position:fixed定位
  • [地铁译]使用SSD缓存应用数据——Moneta项目: 低成本优化的下一代EVCache ...
  • 基于django的视频点播网站开发-step3-注册登录功能 ...
  • 交换综合实验一
  • 蚂蚁金服CTO程立:真正的技术革命才刚刚开始
  • 移动端高清、多屏适配方案
  • ​​​​​​​​​​​​​​Γ函数
  • ​queue --- 一个同步的队列类​
  • #{} 和 ${}区别
  • #if #elif #endif
  • #pragma once
  • #QT(智能家居界面-界面切换)
  • (~_~)
  • (9)STL算法之逆转旋转
  • (AngularJS)Angular 控制器之间通信初探
  • (bean配置类的注解开发)学习Spring的第十三天
  • (floyd+补集) poj 3275
  • (Mac上)使用Python进行matplotlib 画图时,中文显示不出来
  • (Matlab)使用竞争神经网络实现数据聚类
  • (Redis使用系列) Springboot 整合Redisson 实现分布式锁 七
  • (STM32笔记)九、RCC时钟树与时钟 第二部分
  • (二)丶RabbitMQ的六大核心