数据分片(5 分布式主键)

实现动机
内置的主键生成器
UUID
SNOWFLAKE
实现动机
传统数据库软件开发中,主键自动生成技术是基本需求。而各个数据库对于该需求也提供了相应的支持,比如 MySQL 的自增键,Oracle 的自增序列等。 数据分片后,不同数据节点生成全局唯一主键是非常棘手的问题。同一个逻辑表内的不同实际表之间的自增键由于无法互相感知而产生重复主键。 虽然可通过约束自增主键初始值和步长的方式避免碰撞,但需引入额外的运维规则,使解决方案缺乏完整性和可扩展性。

目前有许多第三方解决方案可以完美解决这个问题,如 UUID 等依靠特定算法自生成不重复键,或者通过引入主键生成服务等。为了方便用户使用、满足不同用户不同使用场景的需求, Apache ShardingSphere 不仅提供了内置的分布式主键生成器,例如 UUID、SNOWFLAKE,还抽离出分布式主键生成器的接口,方便用户自行实现自定义的自增主键生成器。

内置的主键生成器
UUID
采用 UUID.randomUUID() 的方式产生分布式主键。

SNOWFLAKE
在分片规则配置模块可配置每个表的主键生成策略,默认使用雪花算法(snowflake)生成 64bit 的长整型数据。

雪花算法是由 Twitter 公布的分布式主键生成算法,它能够保证不同进程主键的不重复性,以及相同进程主键的有序性。

实现原理
在同一个进程中,它首先是通过时间位保证不重复,如果时间相同则是通过序列位保证。 同时由于时间位是单调递增的,且各个服务器如果大体做了时间同步,那么生成的主键在分布式环境可以认为是总体有序的,这就保证了对索引字段的插入的高效性。例如 MySQL 的 Innodb 存储引擎的主键。

使用雪花算法生成的主键,二进制表示形式包含 4 部分,从高位到低位分表为:1bit 符号位、41bit 时间戳位、10bit 工作进程位以及 12bit 序列号位。

符号位(1bit)
预留的符号位,恒为零。

时间戳位(41bit)
41 位的时间戳可以容纳的毫秒数是 2 的 41 次幂,一年所使用的毫秒数是:365 * 24 * 60 * 60 * 1000。通过计算可知:

Math.pow(2, 41) / (365 * 24 * 60 * 60 * 1000L);

结果约等于 69.73 年。Apache ShardingSphere 的雪花算法的时间纪元从 2016年11月1日 零点开始,可以使用到 2086 年,相信能满足绝大部分系统的要求。

工作进程位(10bit)
该标志在 Java 进程内是唯一的,如果是分布式应用部署应保证每个工作进程的 id 是不同的。该值默认为 0,可通过属性设置。

序列号位(12bit)
该序列是用来在同一个毫秒内生成不同的 ID。如果在这个毫秒内生成的数量超过 4096 (2的12次幂),那么生成器会等待到下个毫秒继续生成。

雪花算法主键的详细结构见下图。
在这里插入图片描述
时钟回拨
服务器时钟回拨会导致产生重复序列,因此默认分布式主键生成器提供了一个最大容忍的时钟回拨毫秒数。 如果时钟回拨的时间超过最大容忍的毫秒数阈值,则程序报错;如果在可容忍的范围内,默认分布式主键生成器会等待时钟同步到最后一次主键生成的时间后再继续工作。 最大容忍的时钟回拨毫秒数的默认值为 0,可通过属性设置。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
高并发分布式系统中生成全局唯一Id汇总 数据分片时,典型的是分库分表,就有一个全局ID生成的问题。 单纯的生成全局ID并不是什么难题,但是生成的ID通常要满足分片的一些要求: 1 不能有单点故障。 2 以时间为序,或者ID里包含时间。这样一是可以少一个索引,二是冷热数据容易分离。 3 可以控制ShardingId。比如某一个用户的文章要放在同一个分片内,这样查询效率高,修改也容易。 4 不要太长,最好64bit。使用long比较好操作,如果是96bit,那就要各种移位相当的不方便,还有可能有些组件不能支持这么大的ID。 一 twitter twitter在把存储系统从MySQL迁移到Cassandra的过程中由于Cassandra没有顺序ID生成机制,于是自己开发了一套全局唯一ID生成服务:Snowflake。 1 41位的时间序列(精确到毫秒,41位的长度可以使用69年) 2 10位的机器标识(10位的长度最多支持部署1024个节点) 3 12位的计数顺序号(12位的计数顺序号支持每个节点每毫秒产生4096个ID序号) 最高位是符号位,始终为0。 优点:高性能,低延迟;独立的应用;按时间有序。 缺点:需要独立的开发和部署。 原理 java 实现代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 public class IdWorker { private final long workerId; private final static long twepoch = 1288834974657L; private long sequence = 0L; private final static long workerIdBits = 4L; public final static long maxWorkerId = -1L ^ -1L << workerIdBits; private final static long sequenceBits = 10L; private final static long workerIdShift = sequenceBits; private final static long timestampLeftShift = sequenceBits + workerIdBits; public final static long sequenceMask = -1L ^ -1L < this.maxWorkerId || workerId < 0) { throw new IllegalArgumentException(String.format( "worker Id can't be greater than %d or less than 0", this.maxWorkerId)); } this.workerId = workerId; } public synchronized long nextId() { long timestamp = this.timeGen(); if (this.lastTimestamp == timestamp) { this.sequence = (this.sequence + 1) & this.sequenceMask; if (this.sequence == 0) { System.out.println("###########" + sequenceMask); timestamp = this.tilNextMillis(this.lastTimestamp); } } else { this.sequence = 0; } if (timestamp < this.lastTimestamp) { try { throw new Exception( String.format( "Clock moved backwards. Refusing to generate id for %d milliseconds", this.lastTimestamp - timestamp)); } catch (Exception e) { e.printStackTrace(); } } this.lastTimestamp = timestamp; long nextId = ((timestamp - twepoch << timestampLeftShift)) | (this.workerId << this.workerIdShift) | (this.sequence); System.out.println("timestamp:" + timestamp + ",timestampLeftShift:" + timestampLeftShift + ",nextId:" + nextId + ",workerId:" + workerId + ",sequence:" + sequence); return nextId; } private long tilNextMillis(final long lastTimestamp) { long timestamp = this.timeGen(); while (timestamp <= lastTimestamp) { timestamp = this.timeGen(); } return timestamp; } private long timeGen() { return System.currentTimeMillis(); } public static void main(String[] args){ IdWorker worker2 = new IdWorker(2); System.out.println(worker2.nextId()); } }
Apache ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈,它由JDBC、Proxy和Sidecar(规划中)这3款相互独立,却又能够混合部署配合使用的产品组成。它们均提供标准化的数据分片分布式事务和数据库治理功能,可适用于如Java同构、异构语言、云原生等各种多样化的应用场景。 Apache ShardingSphere定位为关系型数据库中间件,旨在充分合理地在分布式的场景下利用关系型数据库的计算和存储能力,而并非实现一个全新的关系型数据库。它通过关注不变,进而抓住事物本质。关系型数据库当今依然占有巨大市场,是各个公司核心业务的基石,未来也难于撼动,我们目前阶段更加关注在原有基础上的增量,而非颠覆。 Apache ShardingSphere 5.x版本开始致力于可插拔架构,项目的功能组件能够灵活的以可插拔的方式进行扩展。目前,数据分片、读写分离、多数据副本、数据加密、影子库压测等功能,以及对MySQL、PostgreSQL、SQLServer、Oracle等SQL与协议的支持,均通过插件的方式织入项目。开发者能够像使用积木一样定制属于自己的独特系统。Apache ShardingSphere目前已提供数十个SPI作为系统的扩展点,而且仍在不断增加中。 ShardingSphere特点: ShardingSphere-JDBC 定位为轻量级Java框架,在Java的JDBC层提供的额外服务。它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架。 适用于任何基于JDBC的ORM框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。 支持任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP等。 支持任意实现JDBC规范的数据库,目前支持MySQL,Oracle,SQLServer,PostgreSQL以及任何遵循SQL92标准的数据库。 ShardingSphere-Proxy 定位为透明化的数据库代理端,提供封装了数据库二进制协议的服务端版本,用于完成对异构语言的支持。 目前提供MySQL和PostgreSQL版本,它可以使用任何兼容MySQL/PostgreSQL协议的访问客户端(如:MySQL Command Client, MySQL Workbench, Navicat等)操作数据,对DBA更加友好。 向应用程序完全透明,可直接当做MySQL/PostgreSQL服务端使用。 适用于任何兼容MySQL/PostgreSQL协议的的客户端。 ShardingSphere-Sidecar(TODO) 定位为Kubernetes的云原生数据库代理,以Sidecar的形式代理所有对数据库的访问。通过无中心、零侵入的方案提供与数据库交互的的啮合层,即Database Mesh,又可称数据库网格。 Database Mesh的关注重点在于如何将分布式数据访问应用与数据库有机串联起来,它更加关注的是交互,是将杂乱无章的应用与数据库之间的交互进行有效地梳理。使用Database Mesh,访问数据库的应用和数据库终将形成一个巨大的网格体系,应用和数据库只需在网格体系中对号入座即可,它们都是被啮合层所治理的对象。 混合架构 ShardingSphere-JDBC采用无中心化架构,适用于Java开发的高性能的轻量级OLTP应用;ShardingSphere-Proxy提供静态入口以及异构语言的支持,适用于OLAP应用以及对分片数据库进行管理和运维的场景。 Apache ShardingSphere是多接入端共同组成的生态圈。 通过混合使用ShardingSphere-JDBC和ShardingSphere-Proxy,并采用同一注册中心统一配置分片策略,能够灵活的搭建适用于各种场景的应用系统,使得架构师更加自由地调整适合与当前业务的最佳系统架构。 功能列表: 1、数据分片 分库 & 分表 读写分离 分片策略定制化 无中心化分布式主键 2、分布式事务 标准化事务接口 XA 强一致事务 柔性事务 3、数据库治理 分布式治理 弹性伸缩 可观测性(分布式跟踪、指标度量) 数据加解密 影子表压测
1. 填空 分布式数据库系统按局部数据库管理系统的数据模型分类,可以分为 和 两类。 同构型DDBS 异构型DDBS 分布式数据库系统按全避控制系统类型分类,可以分为 、 和 三类。 全局控制集中型DDBS 全局控制分散型DDBS 全局控制可变型DDBS 分布式数据库是分布式数据库系统中各站点上数据库的逻辑集合,它由 和 组成。 应用数据库 描述数据数据分片的三种基本方法是: 、 和 三类。 水平分片 垂直分片 混合分片 分布式数据库中的数据分布策略有: 、 、 和 四层。 集中式 分割式 复制式 混合式 分布式数据库是多层模式结构,一般划分为 、 、 和 四层。 全局外层 全局概念层 局部概念层 局部内层 一个分布式数据库管理系统一般应包括 、 、 和 四个基本功能模块。 查询处理模块 完整性处理模块 调度处理模块 可靠性处理模块 分布透明性包括 、 和 三个层次。 分片透明性 位置透明性 局部数据模型透明性 分布式数据库系统的创建方法,大致可分为 和 两种。 组合法 重构法 集中式数据库设计一般包括:需求分析,概念设计,逻辑设计和物理设计四个阶段,分 布式数据库设计除了上述四个阶段外,还需增加一些个新的阶段 ,它位于 和 之间。 分布设计 逻辑设计 物理设计 水平分片的方法可归为 和 两种。 初级分片 导出分片 DATAID-D相对于DATAID-1增加了 和 两个阶段。 分布要求分析 分布设计 DATAID-D中的分布设计分成 、 、 和 四个阶段。 分片设计 非冗余分配 冗余分配 局部模式的重新构造 分布式查询优化的准则是 。 通信费用和响应时间最短 在分布式系统中,查询代价QC= 。 I/O代价+CPU代价+通信代价 在分布式环境下,查询可分为 、 和 三种类型。 局部查询 远程查询 全局查询 分布式查询处理可以分为 、 、 和 四层。 查询分解 数据本地化 全局优化 局部优化一个分布式事务通常是由 和 组成。 主事务 子事务 事务的四个特性是: 、 、 和 。 原子性 一致性 隔离性 耐久性 控制分布式事务所执行的控制模型有: 、 和 。 主从模型 三角模型 层次模型 分布式数据库系统中,通信故障可以分为 和 两种。 报文故障 网络分割故障 事务恢复主要是依靠 来实现的。 日志 并发控制机制可以为 和 两种类型。 悲观并发控制法 乐观并发控制法 常用的基本封锁算法有: 、 、 和 。 简单的分布式封锁方法 主站点封锁法 主副本封锁法 快照方法 预防死锁的方法有 和 两种类型。 非占先权方法 占先权方法 检测分布式死锁的三种方法是 、 和 。 集中式 层次式 分布式 2. 简答题 分布式数据库系统的特点是什么 答:物理分布性:数据不是存放在一个站点上 逻辑整体性:是与分散式数据库系统的区别 站点自治性:是与多处理机的系统的区别 数据分布透明性 集中与自治相结合 存在适当的数据冗余度 事务管理的分布性 分布式数据库中数据分片的规则是什么 答:(1)完备性原则:必须把全局关系的所有数据映射到各自片段中,绝不允许有属于 全局关系的数据却不发球它的任何一个片段。 (2)可重构原则:必须保证能够由同一个全局关系的各个片段来重建该全局关系。对于 水平分片可用并操作重构全局关系,对于垂直分片可用连接操作重构全局关系。 (3)不相交原则:关系分片后的各个片断不能重叠或只包含主键重叠。 DATAID-D相对于DATAID-1增加哪两个阶段,这两个阶段的具体工作是什么 答:(1)分布要求分析阶段:收集关于分布的信息,如水平分片的划分谓词,每一应用 在各站点激活的频率等。 (2)分布设计阶段:始于全局数据库模式的规格说明和所收集的分布要求,然后产生全 局数据分片模式和片段的位置分配模式,分配模式描述了分配在各站点上的数据情况 。 分布式事务的一般结构是什么 答:分布式事务的一般结构为: Begin Transaction原语:开始一个事务(2分) T1[] T2[] : 子事务或操作序列 : Tn[] Commit原语:事务成功完成的结束(2分) Rollback或Abort原语:事务失败的结束(1分) 5. 论述题 分布式数据库中,"数据分配"有哪些策略"数据分片"有哪些策略 数据分片的准则是什么 数据分配是指数据在计算机网络各场地上的分配策略。包括: (1)集中式:所有数据均安排在同一个场地上。 (2)分割式:所有数据只有一份,分别被安置在若干个场地。 (3)全复制式:数据在每个场地重复存储。 (4)混合式:数据库分成若干可相交的子集,每一子集安置在一个或多个场地上,但是 每一场地未必保存全部数据数据分片的方式有以下三种: (1)水平分片:按一定的条件把全局关系的所有元组划分成若干不相交的子集,每个子 集为关系的一个片段。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值